Type casting dans la boucle for-in

116

J'ai cette boucle for-in:

for button in view.subviews {
}

Maintenant, je veux que le bouton soit converti en une classe personnalisée afin que je puisse utiliser ses propriétés.

J'ai essayé ceci: for button in view.subviews as AClass

Mais cela ne fonctionne pas et me donne une erreur:'AClass' does not conform to protocol 'SequenceType'

Et j'ai essayé ceci: for button:AClass in view.subviews

Mais cela ne fonctionne pas non plus.

Arbitur
la source
Que diriez-vousfor button in view.subviews as [AClass]
vacawama
2
haha je n'ai pas pensé à cela, bien sûr, il est préférable de jeter le tableau dans un tableau d'AClass, merci l'homme. Vous pouvez faire une réponse à ce sujet afin que je puisse vous donner un peu de rep :)
Arbitur

Réponses:

173

Pour Swift 2 et versions ultérieures:

Swift 2 ajoute des modèles de cas aux boucles for , ce qui rend encore plus facile et plus sûr la saisie de caractères dans une boucle for :

for case let button as AClass in view.subviews {
    // do something with button
}

Pourquoi est-ce mieux que ce que vous pourriez faire dans Swift 1.2 et versions antérieures? Parce que les modèles de cas vous permettent de choisir votre type spécifique dans la collection. Il ne correspond qu'au type que vous recherchez, donc si votre tableau contient un mélange, vous ne pouvez opérer que sur un type spécifique.

Par exemple:

let array: [Any] = [1, 1.2, "Hello", true, [1, 2, 3], "World!"]
for case let str as String in array {
    print(str)
}

Production:

Hello
World!

Pour Swift 1.2 :

Dans ce cas, vous effectuez un cast view.subviewset non button, vous devez donc le réduire dans le tableau du type souhaité:

for button in view.subviews as! [AClass] {
    // do something with button
}

Remarque: si le type de tableau sous-jacent n'est pas [AClass], cela plantera. C'est ce que l' !on as!vous dit. Si vous n'êtes pas sûr du type, vous pouvez utiliser un cast conditionnel as?avec une liaison facultative if let:

if let subviews = view.subviews as? [AClass] {
    // If we get here, then subviews is of type [AClass]
    for button in subviews {
        // do something with button
    }
}

Pour Swift 1.1 et versions antérieures:

for button in view.subviews as [AClass] {
    // do something with button
}

Remarque: cela plantera également si les sous-vues ne sont pas toutes de type AClass. La méthode sûre répertoriée ci-dessus fonctionne également avec les versions antérieures de Swift.

vacawama
la source
Juste une note pour d'autres comme moi qui ne l'ont pas compris au début - le nom de la classe doit être placé [ ]entre crochets exactement comme dans cet exemple. Peut-être que c'est juste moi, mais je pensais que c'était juste un choix de formatage dans l'exemple de code au début :) Je suppose que c'est parce que nous sommes en train de convertir un tableau.
@kmcgrady Cela a l' [Class]air bien mieux que Array<Class>.
Arbitur le
Donc, par curiosité, existe-t-il un moyen de faire plusieurs déclarations de cas dans la boucle for? EG si je veux faire une chose aux boutons, une autre aux champs de texte?
RonLugge
125

Cette option est plus sécurisée:

for case let button as AClass in view.subviews {
}

ou façon rapide:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }
ober
la source
1
Cela semble être la meilleure réponse car il n'y a pas de force lancée.
Patrick
1
Cela devrait être la réponse acceptée. Est-ce le meilleur et le bon!
Thomás Calmon
Bonne réponse. Bref et simple.
PashaN
4

Vous pouvez également utiliser une whereclause

for button in view.subviews where button is UIButton {
    ...
}
edelaney05
la source
12
La clause where est une garde booléenne, mais ne convertit pas le type de l' buttonintérieur du corps de la boucle, ce qui est le but des autres solutions. Donc, cela ne lancera le corps de la boucle que sur les éléments view.subviewsdont sont UIButtons, mais n'aide pas, par exemple, à appeler des AClassméthodes spécifiques à button.
John Whitley
1
@JohnWhitley vous avez raison de dire que whereseuls les gardes et ne lance pas.
edelaney05
wherepeut être plus élégant et lisible pour les cas (jeu de mots non intentionnel) où vous n'avez besoin que d'une garde booléenne.
STO
3

Les réponses fournies sont correctes, je voulais juste ajouter ceci en complément.

Lors de l'utilisation d'une boucle for avec force cast, le code plantera (comme déjà mentionné par d'autres).

for button in view.subviews as! [AClass] {
    // do something with button
}

Mais au lieu d'utiliser une clause if,

if let subviews = view.subviews as? [AClass] {
    // If we get here, then subviews is of type [AClass]
    ...
}

une autre façon est d'utiliser une boucle while:

/* If you need the index: */
var iterator = view.subviews.enumerated().makeIterator()
while let (index, subview) = iterator.next() as? (Int, AClass) {
    // Use the subview
    // ...
}

/* If you don't need the index: */
var iterator = view.subviews.enumerated().makeIterator()
while let subview = iterator.next().element as? AClass {
    // Use the subview
    // ...
}

Ce qui semble être plus pratique si certains éléments (mais pas tous) du tableau peuvent être de type AClass.

Bien que pour l'instant (à partir de Swift 5), j'opterais pour la boucle for-case:

for case let (index, subview as AClass) in view.subviews.enumerated() {
    // ...
}

for case let subview as AClass in view.subviews {
    // ...
}
user0800
la source
Juste un doute ici ... est-ce que la fonction rapide énumérée effectue des itérations / boucle ici ... il suffit de penser que le refactoring pour perdre des performances ne sera pas génial ... merci
Amber K
2

La réponse fournie par vacawama était correcte dans Swift 1.0. Et ne fonctionne plus avec Swift 2.0.

Si vous essayez, vous obtiendrez une erreur similaire à:

«[AnyObject]» n'est pas convertible en «[AClass]»;

Dans Swift 2.0, vous devez écrire comme:

for button in view.subviews as! [AClass]
{
}
Député de Midhun
la source
0

Vous pouvez effectuer le casting et être en sécurité en même temps avec ceci:

for button in view.subviews.compactMap({ $0 as? AClass }) {

}
nandodelauni
la source