web-dev-qa-db-fra.com

Type moulant dans la boucle for-in

J'ai cette boucle for-in:

for button in view.subviews {
}

Maintenant, je veux que le bouton soit transféré dans 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.

100
Arbitur

Pour Swift 2 et plus tard:

Swift 2 ajoute des motifs de casse à pour les boucles , ce qui simplifie encore les choses. et plus sûr de taper dans une boucle pour :

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

Pourquoi est-ce meilleur que ce que vous pourriez faire dans Swift 1.2 et plus tôt? Parce que les modèles de casse vous permettent de choisir votre type spécifique dans la collection. Elle correspond uniquement au type que vous recherchez. Ainsi, 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)
}

Sortie:

Hello
World!

Pour Swift 1.2:

Dans ce cas, vous lancez view.subviews et non button, vous devez donc le convertir en un 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 va planter. C'est ce que le ! sur as! vous dit. Si vous n'êtes pas sûr du type, vous pouvez utiliser une distribution conditionnelle as? avec la 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 antérieur:

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

Remarque: ceci provoquera également un blocage si les sous-vues ne sont pas toutes de type AClass. La méthode sécurisée indiquée ci-dessus fonctionne également avec les versions antérieures de Swift.

144
vacawama

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 { .... }
119
ober

Vous pouvez également utiliser une clause where

for button in view.subviews where button is UIButton {
    ...
}
4
edelaney05

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 semblable à:

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

Dans Swift 2.0, vous devez écrire comme:

for button in view.subviews as! [AClass]
{
}
2
Midhun MP

Les réponses fournies sont correctes, je voulais juste ajouter ceci comme un ajout.

Lors de l'utilisation d'une boucle for avec le casting forcé, le code va planter (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 consiste à 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 pourraient être de type AClass.

Bien que pour le moment (à partir de Swift 5), je choisirais la boucle for-case:

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

for case let subview as AClass in view.subviews {
    // ...
}
0
T.Meyer