Введіть кастинг у циклі для введення


116

У мене є цей цикл для входу:

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]
vacawama

2
ха-ха, я не думав про це, звичайно, краще віддати масив до масиву AClass, дякую людині. Ви можете відповісти на це, щоб я могла дати вам відповідь :)
Арбітур,

Відповіді:


173

Для Swift 2 та новіших версій:

Swift 2 додає моделі регістра в протягом циклів, що робить його ще простіше і безпечніше набирати акторів у протягом циклу:

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.


Лише зауваження для інших, як я, які спочатку цього не отримали - ім'я класу має міститись у [ ]дужках точно так, як у цьому прикладі. Можливо, це лише я, але спочатку я подумав, що це просто вибір форматування у зразку коду :) Я припускаю, що це тому, що ми перекладаємо на масив.

@kmcgrady Це просто [Class]виглядає набагато краще, ніж Array<Class>.
Арбітур

Отже, з цікавості, чи існує спосіб зробити кілька заяв із справи у циклі for? EG, якщо я хочу зробити одне, що стосується кнопок, інше - текстові поля?
RonLugge

125

Цей варіант є більш безпечним:

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

або швидкий спосіб:

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

1
Це здається приємнішою відповіддю, оскільки немає жодної сили.
Патрік

1
Це має бути прийнятою відповіддю. Найкращий і правильний!
Томас Кальмон

Правильна відповідь. Короткий і простий.
ПашаN

4

Ви також можете скористатись whereпунктом

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

12
Пункт де є булевим захистом, але не містить тип buttonвнутрішнього корпусу циклу, що є метою інших рішень. Таким чином, це запустить тіло циклу лише на елементах, view.subviewsякі є UIButtons, але не допоможе, наприклад, у виклику AClass-specific методів на button.
Джон Вітлі

1
@JohnWhitley ви праві, що whereтільки охороняє і не кидає.
edelaney05

whereможе бути більш елегантним і читабельним у випадках, коли вам потрібна лише булева охорона.
СТО

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

Що здається більш зручним, якщо деякі елементи масиву (але не всі) можуть мати тип 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 {
    // ...
}

Тут є лише одне сумніви ... чи швидка перелічена функція виконує тут ітерації / цикл .. просто думати про рефакторинг для втрати продуктивності не буде великим ... дякую
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 }) {

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.