Geben Sie Casting in For-In-Schleife ein

116

Ich habe diese For-In-Schleife:

for button in view.subviews {
}

Jetzt möchte ich, dass die Schaltfläche in eine benutzerdefinierte Klasse umgewandelt wird, damit ich ihre Eigenschaften verwenden kann.

Ich habe es versucht: for button in view.subviews as AClass

Aber es funktioniert nicht und gibt mir einen Fehler:'AClass' does not conform to protocol 'SequenceType'

Und ich habe es versucht: for button:AClass in view.subviews

Aber das funktioniert auch nicht.

Arbitur
quelle
Wie wäre esfor button in view.subviews as [AClass]
Vacawama
2
haha, daran habe ich nicht gedacht, natürlich ist es besser, das Array in ein Array von AClass umzuwandeln, danke Mann. Sie können eine Antwort darauf geben, damit ich Ihnen einige
Wiederholungen

Antworten:

173

Für Swift 2 und höher:

Swift 2 fügt Fall Muster zu für Schleifen, die es noch einfacher und sicherer macht Guss in einem eintippen für Schleife:

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

Warum ist das besser als das, was Sie in Swift 1.2 und früher tun könnten ? Mit Fallmustern können Sie Ihren spezifischen Typ aus der Sammlung auswählen. Es entspricht nur dem Typ, den Sie suchen. Wenn Ihr Array also eine Mischung enthält, können Sie nur einen bestimmten Typ bearbeiten.

Beispielsweise:

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

Ausgabe:

Hello
World!

Für Swift 1.2 :

In diesem Fall wird ein Casting durchgeführt view.subviewsund nicht button. Sie müssen es daher auf das Array des gewünschten Typs herabstufen:

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

Hinweis: Wenn der zugrunde liegende Array-Typ nicht lautet [AClass], stürzt dies ab. Das sagt dir der !On as!. Wenn Sie sich über den Typ nicht sicher sind, können Sie eine bedingte Umwandlung as?zusammen mit einer optionalen Bindung verwenden 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
    }
}

Für Swift 1.1 und früher:

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

Hinweis: Dies stürzt auch ab, wenn die Unteransichten nicht alle vom Typ sind AClass. Die oben aufgeführte sichere Methode funktioniert auch mit früheren Versionen von Swift.

vacawama
quelle
Nur eine Notiz für andere wie mich, die es zuerst nicht verstanden haben - der Klassenname muss [ ]genau wie in diesem Beispiel in Klammern stehen. Vielleicht bin ich es nur, aber ich dachte, es war zunächst nur eine Formatierungsoption im Codebeispiel :) Ich nehme an, das liegt daran, dass wir in ein Array umwandeln.
@kmcgrady Es [Class]sieht einfach viel besser aus als Array<Class>.
Arbitur
Gibt es aus Neugier eine Möglichkeit, mehrere case-Anweisungen innerhalb der for-Schleife auszuführen? ZB wenn ich eine Sache mit Schaltflächen machen möchte, eine andere mit Textfeldern?
RonLugge
125

Diese Option ist sicherer:

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

oder schnell:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }
ober
quelle
1
Dies scheint die schönere Antwort zu sein, da keine Gewalt angewendet wird.
Patrick
1
Dies sollte die akzeptierte Antwort sein. Ist der beste und der richtige!
Thomás Calmon
Korrekte Antwort. Kurz und einfach.
PashaN
4

Sie können auch eine whereKlausel verwenden

for button in view.subviews where button is UIButton {
    ...
}
edelaney05
quelle
12
Die where-Klausel ist ein boolescher Schutz, wirft jedoch nicht den Typ buttoninnerhalb des Schleifenkörpers, der das Ziel der anderen Lösungen ist. Dies führt also nur den Schleifenkörper auf Elementen aus, von view.subviewsdenen UIButtons sind, hilft aber nicht, z. B. beim Aufrufen AClass-spezifischer Methoden auf button.
John Whitley
1
@ JohnWhitley Sie haben Recht, dass wherenur Wachen und nicht werfen.
edelaney05
wherekann eleganter und lesbarer sein für Fälle (Wortspiel unbeabsichtigt), in denen Sie nur einen Booleschen Schutz benötigen.
STO
3

Die Antworten sind korrekt, ich wollte dies nur als Ergänzung hinzufügen.

Bei Verwendung einer for-Schleife mit Force Casting stürzt der Code ab (wie bereits von anderen erwähnt).

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

Aber anstatt eine if-Klausel zu verwenden,

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

Eine andere Möglichkeit ist die Verwendung einer while-Schleife:

/* 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
    // ...
}

Dies scheint bequemer zu sein, wenn einige Elemente (aber nicht alle) des Arrays vom Typ sind AClass.

Obwohl ich mich vorerst (ab Swift 5) für die for-case-Schleife entscheiden würde:

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

for case let subview as AClass in view.subviews {
    // ...
}
user0800
quelle
Nur ein Zweifel hier ... führt die schnell aufgezählte Funktion hier Iterationen / Schleifen durch ... nur daran zu denken, dass Refactoring für Leistungseinbußen nicht großartig ist ... danke
Amber K
2

Die Antwort von vacawama war in Swift 1.0 korrekt. Und funktioniert nicht mehr mit Swift 2.0.

Wenn Sie es versuchen, wird eine ähnliche Fehlermeldung angezeigt:

'[AnyObject]' kann nicht in '[AClass]' konvertiert werden.

In Swift 2.0 müssen Sie wie folgt schreiben:

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

Sie können das Casting durchführen und gleichzeitig sicher sein:

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

}
Nandodelauni
quelle