Ketik casting di for-in loop

116

Saya memiliki loop for-in ini:

for button in view.subviews {
}

Sekarang saya ingin tombol dilemparkan ke kelas kustom sehingga saya dapat menggunakan propertinya.

Saya mencoba ini: for button in view.subviews as AClass

Tapi itu tidak berhasil dan memberi saya kesalahan:'AClass' does not conform to protocol 'SequenceType'

Dan saya mencoba ini: for button:AClass in view.subviews

Tapi itu juga tidak berhasil.

Arbitur
sumber
Bagaimana denganfor button in view.subviews as [AClass]
vacawama
2
haha Saya tidak memikirkan itu, tentu saja lebih baik untuk mengubah array menjadi array AClass, terima kasih man. Anda dapat membuat jawaban tentang hal itu sehingga saya dapat memberikan Anda beberapa perwakilan :)
Arbitur

Jawaban:

173

Untuk Swift 2 dan lebih baru:

Swift 2 menambahkan pola kasus ke for loop, yang membuatnya lebih mudah dan lebih aman untuk mengetik cast in a for loop:

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

Mengapa ini lebih baik daripada yang dapat Anda lakukan di Swift 1.2 dan sebelumnya? Karena pola case memungkinkan Anda untuk memilih tipe spesifik Anda dari koleksi. Ini hanya cocok dengan tipe yang Anda cari, jadi jika array Anda berisi campuran, Anda hanya dapat beroperasi pada tipe tertentu.

Sebagai contoh:

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

Keluaran:

Hello
World!

Untuk Swift 1.2 :

Dalam kasus ini, Anda sedang mentransmisi view.subviewsdan tidak button, jadi Anda perlu menurunkannya ke array jenis yang Anda inginkan:

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

Catatan: Jika tipe array yang mendasarinya tidak [AClass], ini akan macet. Itulah yang dikatakan !pada as!Anda. Jika Anda tidak yakin tentang jenisnya, Anda dapat menggunakan cast bersyarat as?bersama dengan binding opsional 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
    }
}

Untuk Swift 1.1 dan sebelumnya:

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

Catatan: Ini juga akan macet jika subview tidak semuanya berjenis AClass. Metode aman yang tercantum di atas juga berfungsi dengan versi Swift sebelumnya.

vacawama
sumber
Hanya sebuah catatan untuk orang lain seperti saya yang pada awalnya tidak mengerti - nama kelas harus dimasukkan ke dalam [ ]tanda kurung persis seperti dalam contoh ini. Mungkin hanya saya tapi saya pikir itu hanya pilihan pemformatan dalam sampel kode pada awalnya :) Saya kira ini karena kita melakukan casting ke array.
@kmhh Ini hanya [Class]terlihat jauh lebih baik daripada Array<Class>.
Arbitur
Jadi karena penasaran, apakah ada cara untuk melakukan beberapa pernyataan kasus di dalam for? Misalnya, jika saya ingin melakukan satu hal ke tombol, hal lain ke bidang teks?
RonLugge
125

Opsi ini lebih aman:

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

atau cara cepat:

view.subviews
  .compactMap { $0 as AClass }
  .forEach { .... }
ober
sumber
1
Ini sepertinya jawaban yang lebih bagus karena tidak ada kekuatan yang dilemparkan.
Patrick
1
Ini harus menjadi jawaban yang diterima. Apakah yang terbaik dan yang benar!
Thomás Calmon
Jawaban yang benar. Pendek dan sederhana.
PashaN
4

Anda juga dapat menggunakan whereklausa

for button in view.subviews where button is UIButton {
    ...
}
edelaney05
sumber
12
Klausa where adalah pelindung boolean, tetapi tidak memasukkan tipe buttoninside body loop, yang merupakan tujuan dari solusi lainnya. Jadi ini hanya akan menjalankan badan perulangan pada elemen view.subviewsyang UIButtons, tetapi tidak membantu, misalnya dalam memanggil AClassmetode -specific on button.
John Whitley
1
@JohnWhitley Anda benar bahwa wherehanya penjaga dan tidak melemparkan.
edelaney05
wheremungkin lebih elegan dan mudah dibaca untuk kasus (permainan kata tidak disengaja) di mana Anda hanya memerlukan pelindung boolean.
STO
3

Jawaban yang diberikan benar, saya hanya ingin menambahkan ini sebagai tambahan.

Saat menggunakan for loop dengan force casting, kode akan macet (seperti yang telah disebutkan oleh orang lain).

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

Namun alih-alih menggunakan klausa if,

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

cara lain adalah dengan menggunakan while-loop:

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

Yang tampaknya lebih nyaman jika beberapa elemen (tetapi tidak semua) dari array mungkin bertipe AClass.

Meskipun untuk saat ini (pada Swift 5), saya akan menggunakan for-case loop:

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

for case let subview as AClass in view.subviews {
    // ...
}
pengguna0800
sumber
Hanya satu keraguan di sini ... apakah fungsi enumerasi cepat melakukan iterasi / loop di sini .. hanya memikirkan refactoring untuk kehilangan kinerja tidak akan bagus ... terima kasih
Amber K
2

Jawaban yang diberikan oleh vacawama benar di Swift 1.0. Dan tidak lagi berfungsi dengan Swift 2.0.

Jika Anda mencoba, Anda akan mendapatkan kesalahan yang mirip dengan:

'[AnyObject]' tidak dapat diubah menjadi '[AClass]';

Di Swift 2.0 Anda perlu menulis seperti:

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

Anda dapat melakukan transmisi dan aman pada saat yang sama dengan ini:

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

}
nandodelauni
sumber