for-inループでの型キャスト


116

私はこのfor-inループを持っています:

for button in view.subviews {
}

次に、ボタンをカスタムクラスにキャストして、そのプロパティを使用できるようにします。

私はこれを試しました: for button in view.subviews as AClass

しかし、それは機能せず、エラーが発生します:'AClass' does not conform to protocol 'SequenceType'

そして私はこれを試しました: for button:AClass in view.subviews

しかし、どちらも機能しません。


どうですかfor button in view.subviews as [AClass]
バカワマ2014

2
はは、私はそのことを考えていませんでした。もちろん、配列をAClassの配列にキャストする方が良いです。ありがとうございます。あなたはそれについて答えを出すことができますので、私はあなたにいくつかの担当者を与えることができます:)
Arbitur

回答:


173

スウィフト2以降:

Swift 2では、forループに大文字小文字のパターンが追加され、forループでの型キャストがさらに簡単かつ安全になります。

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

これがSwift 1.2以前でできることよりも優れているのはなぜですか?のでケースパターンは、コレクションのうちの特定のタイプを選択することができます。探している型とのみ一致するため、配列に混合が含まれている場合、特定の型のみを操作できます。

例えば:

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

出力:

Hello
World!

スウィフト1.2

この場合、キャストview.subviewsではなくなbuttonので、必要な型の配列にダウンキャストする必要があります。

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

注:基になる配列タイプがでない場合[AClass]、これはクラッシュします。それが!オンas!があなたに言っていることです。タイプがわからない場合はas?、オプションのバインディングとともに条件付きキャストを使用できます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
    }
}

スウィフト1.1およびそれ以前:

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

注:サブビューのタイプがすべてではない場合も、これはクラッシュしAClassます。上記の安全な方法は、以前のバージョンのSwiftでも機能します。


最初は理解できなかった私のような他の人へのメモです-クラス名は[ ]この例のように括弧内に入れなければなりません。たぶんそれは私だけかもしれませんが、最初はコードサンプルでフォーマットの選択にすぎないと思っていました:)これは、配列にキャストしているためだと思います。

@kmcgradyそれはただ[Class]よりはるかに良く見えArray<Class>ます。
アービトゥール2015

では、好奇心から、forループ内で複数のcaseステートメントを実行する方法はありますか?たとえば、ボタンに対して1つのことをしたい場合、テキストフィールドに対して別のことをしたいですか
RonLugge 2017年

125

このオプションはより安全です:

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

または迅速な方法:

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

1
フォースキャストがないので、これはより良い答えのようです。
Patrick

1
これは受け入れられる答えになるはずです。最高のものと正しいものです!
トーマスCalmon

正解。短くてシンプル。
PashaN 2017年

4

where句を使用することもできます

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

12
where句はブールガードですがbutton、他のソリューションの目標であるループボディ内のタイプをキャストしません。したがって、view.subviewsこれはUIButtons である要素でのみループ本体を実行しますが、たとえばでAClass固有のメソッドを呼び出す場合には役立ちませんbutton
John Whitley、

1
@JohnWhitleyあなたはwhereガードしているだけでキャストしないということは正しいです。
edelaney05

whereブールガードのみが必要な場合(意図的ではない)は、よりエレガントで読みやすいかもしれません。
STO 2018

3

提供された答えは正しいです、私はこれを追加として追加したかっただけです。

フォースキャスティングでforループを使用すると、コードがクラッシュします(既に他の人が述べたように)。

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

しかし、if節を使用する代わりに、

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

別の方法は、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
    // ...
}

これは、配列の一部の要素(すべてではない)がtypeの場合に便利ですAClass

現時点では(Swift 5以降)、for-caseループを使用します。

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

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

ここで1つだけ疑問があります...ここで高速列挙関数が反復/ループを実行しますか?ここでパフォーマンスを失うためのリファクタリングは素晴らしいことではないと思います...ありがとう
Amber K

2

vacawamaによって提供された答えはSwift 1.0で正しかった。また、Swift 2.0では動作しなくなりました。

実行しようとすると、次のようなエラーが表示されます。

'[AnyObject]'は '[AClass]'に変換できません。

Swift 2.0では、次のように記述する必要があります。

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

0

これにより、キャストを実行すると同時に安全を確保できます。

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.