Приведение типов в цикле for-in

У меня есть этот цикл 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

Но и это не работает.


person Arbitur    schedule 27.09.2014    source источник
comment
Как насчет for button in view.subviews as [AClass]   -  person vacawama    schedule 27.09.2014
comment
ха-ха, я не думал об этом, конечно, лучше преобразовать массив в массив AClass, спасибо, чувак. Вы можете ответить об этом, чтобы я мог дать вам некоторую репутацию :)   -  person Arbitur    schedule 27.09.2014
comment
Сравните stackoverflow.com/questions/25097958/.   -  person Martin R    schedule 07.08.2016


Ответы (7)


Для Swift 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!

Для Swift 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
    }
}

Для Swift 1.1 и более ранних версий:

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

Примечание. Это также приведет к сбою, если не все подпредставления имеют тип AClass. Перечисленный выше безопасный метод также работает с более ранними версиями Swift.

person vacawama    schedule 27.09.2014
comment
Просто примечание для таких, как я, которые сначала не поняли - имя класса должно заключаться в скобки [ ] точно так же, как в этом примере. Может быть, это только я, но я сначала подумал, что это просто выбор форматирования в примере кода :) Я предполагаю, что это потому, что мы приводим к массиву. - person ; 05.06.2015
comment
@kmcgrady Просто [Class] выглядит намного лучше, чем Array<Class>. - person Arbitur; 14.07.2015
comment
Итак, из любопытства, есть ли способ сделать несколько операторов case внутри цикла for? Например, если я хочу сделать одно с кнопками, другое с текстовыми полями? - person RonLugge; 18.11.2017

Этот вариант более безопасен:

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

или быстрый способ:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }
person ober    schedule 14.03.2016
comment
Это кажется более приятным ответом, поскольку нет принудительного применения. - person Patrick; 07.11.2016
comment
Это должен быть принятый ответ. Самый лучший и правильный! - person Thomás Pereira; 21.03.2017
comment
Правильный ответ. Коротко и просто. - person PashaN; 23.03.2017

Предоставленные ответы верны, я просто хотел добавить это как дополнение.

При использовании цикла 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
    // ...
}

Что кажется более удобным, если некоторые элементы (но не все) массива могут иметь тип 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 {
    // ...
}
person j3141592653589793238    schedule 05.05.2019
comment
Только одно сомнение здесь... выполняет ли перечисляемая функция swift итерации/цикл здесь... просто думаю, что рефакторинг для потери производительности не будет большим... спасибо - person Amber K; 15.02.2020

Вы также можете использовать предложение where

for button in view.subviews where button is UIButton {
    ...
}
person edelaney05    schedule 31.08.2016
comment
Предложение where является логической защитой, но не приводит тип button внутри тела цикла, что является целью других решений. Таким образом, это будет запускать тело цикла только для элементов view.subviews, которые являются UIButtons, но не помогает, например. при вызове AClass специфичных для button методов. - person John Whitley; 28.10.2016
comment
@JohnWhitley, вы правы, что where только охраняет, а не бросает. - person edelaney05; 30.10.2016
comment
where может быть более элегантным и читаемым для случаев (непреднамеренно игра слов), где вам нужна только логическая защита. - person STO; 07.09.2018

Ответ, предоставленный vacawama, был правильным в Swift 1.0. И больше не работает со Swift 2.0.

Если вы попытаетесь, вы получите ошибку, похожую на:

«[AnyObject]» не может быть преобразован в «[AClass]»;

В Swift 2.0 вам нужно написать так:

for button in view.subviews as! [AClass]
{
}
person Midhun MP    schedule 14.07.2015

Вы можете выполнить кастинг и быть в безопасности одновременно с этим:

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

}
person nandodelauni    schedule 09.09.2019

Если вы хотите иметь индекс в своем цикле:

for case let (index, button as AClass) in view.subviews.enumerated() {
}
person melvinrudolph    schedule 16.02.2021