For-in döngüsünde döküm türü


116

Şu giriş döngüsüne sahibim:

for button in view.subviews {
}

Şimdi, düğmenin özelliklerini kullanabilmek için özel bir sınıfa dönüştürülmesini istiyorum.

Bunu denedim: for button in view.subviews as AClass

Ama işe yaramıyor ve bana bir hata veriyor:'AClass' does not conform to protocol 'SequenceType'

Ve bunu denedim: for button:AClass in view.subviews

Ama bu da işe yaramıyor.


Ne dersinfor button in view.subviews as [AClass]
vacawama

2
haha bunu düşünmedim, elbette diziyi bir AClass dizisine dönüştürmek daha iyidir, teşekkürler dostum. Bu konuda bir cevap verebilirsin, böylece sana biraz replik verebilirim :)
Arbitur

Yanıtlar:


173

İçin Swift 2 üstü ve:

Swift 2 , for döngülerine durum desenleri ekler , bu da for döngüsünde cast yazmayı daha da kolay ve güvenli hale getirir :

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

Bu neden Swift 1.2 ve öncesinde yapabileceğinizden daha iyi? Çünkü vaka kalıpları , koleksiyondan kendi türünüzü seçmenize izin verir. Yalnızca aradığınız türle eşleşir, bu nedenle diziniz bir karışım içeriyorsa, yalnızca belirli bir tür üzerinde işlem yapabilirsiniz.

Örneğin:

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

Çıktı:

Hello
World!

İçin Swift 1.2 :

Bu durumda, yayın yapıyorsunuz view.subviewsve yapmıyorsunuz button, bu nedenle istediğiniz diziye indirgemelisiniz:

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

Not: Temel dizi türü değilse [AClass], bu çökecektir. Yani ne !üzerinde as!anlatıyor. Türden emin değilseniz, as?isteğe bağlı bağlamayla birlikte koşullu atama kullanabilirsiniz 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
    }
}

İçin Swift 1.1 ve öncesi:

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

Not: Bu, alt görünümlerin tümü tür değilse de çökecektir AClass. Yukarıda listelenen güvenli yöntem, Swift'in önceki sürümleriyle de çalışır.


İlk başta anlamayan benim gibi başkaları için bir not - sınıf adı [ ]tam olarak bu örnekteki gibi parantez içinde yazılmalıdır. Belki sadece benim ama ilk başta kod örneğinde sadece bir biçimlendirme seçimi olduğunu düşündüm :) Bunun bir diziye döküm yaptığımız için olduğunu varsayıyorum.

@kmcgrady Bundan [Class]çok daha iyi görünüyor Array<Class>.
Arbitur

Öyleyse meraktan, for döngüsü içinde birden çok durum ifadesi yapmanın bir yolu var mı? Örneğin, bir şeyi düğmelere, diğerini metin alanlarına yapmak istersem?
RonLugge

125

Bu seçenek daha güvenlidir:

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

veya hızlı yol:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }

1
Zorlama olmadığı için bu daha güzel bir cevap gibi görünüyor.
Patrick

1
Kabul edilen cevap bu olmalıdır. En iyisi ve doğru olanı!
Thomás Calmon

Doğru cevap. Kısa ve basit.
PashaN

4

Ayrıca bir wherecümle de kullanabilirsiniz

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

12
Where cümlesi bir boole korumadır, ancak buttondiğer çözümlerin amacı olan döngü gövdesi içi türünü oluşturmaz . Bu nedenle bu, döngü gövdesini yalnızca s view.subviewsolan öğeleri üzerinde çalıştırır UIButton, ancak örneğin AClass-özel yöntemleri açık olarak çağırmaya yardımcı olmaz button.
John Whitley

1
@JohnWhitley, haklısınız, wheresadece gardiyanlar ve oyuncuları atmıyor.
edelaney05

whereyalnızca bir boole korumasına ihtiyaç duyduğunuz durumlar için (istemeden ceza) daha zarif ve okunaklı olabilir.
STO

3

Verilen cevaplar doğru, bunu bir ekleme olarak eklemek istedim.

For döngüsü ile zorlama çevrimini kullanırken, kod çökecektir (başkaları tarafından daha önce belirtildiği gibi).

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

Ancak bir if cümlesi kullanmak yerine,

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

başka bir yol ise while döngüsü kullanmaktır:

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

Bu, dizinin bazı öğelerinin (ancak tümü değil) türünde olması durumunda daha kullanışlı görünüyor AClass.

Şimdilik (Swift 5 itibariyle), durum için döngüye giriyorum:

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

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

Burada sadece bir şüphe var ... hızlı numaralandırılmış işlev burada yineleme / döngü gerçekleştiriyor mu .. sadece performansı kaybetmek için yeniden düzenlemenin harika olmayacağını düşünmek ... teşekkürler
Amber K

2

Vacawama'nın verdiği cevap Swift 1.0'da doğruydu. Ve artık Swift 2.0 ile çalışmıyor.

Eğer denerseniz, şuna benzer bir hata alırsınız:

'[AnyObject]', '[AClass]' olarak dönüştürülemez;

Swift 2.0'da şöyle yazmanız gerekir:

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

0

Bununla aynı anda hem döküm yapabilir hem de güvende olabilirsiniz:

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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.