พิมพ์การหล่อในลูป 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

แต่ก็ไม่ได้ผล


How aboutfor button in view.subviews as [AClass]
vacawama

2
ฮ่าฮ่าฉันไม่ได้คิดถึงเรื่องนี้แน่นอนว่ามันจะดีกว่าที่จะโยนอาร์เรย์ลงในอาร์เรย์ของ AClass ขอบคุณมาก คุณสามารถให้คำตอบเกี่ยวกับเรื่องนี้เพื่อที่ฉันจะได้ตอบกลับบ้าง :)
Arbitur

เปรียบเทียบstackoverflow.com/questions/25097958/… .
Martin R

คำตอบ:


173

สำหรับSwift 2และใหม่กว่า:

สวิฟท์ 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>มาก
Arbitur

ด้วยความอยากรู้อยากเห็นมีวิธีทำหลาย case statement ภายใน for loop หรือไม่? เช่นถ้าฉันต้องการทำสิ่งหนึ่งกับปุ่มอีกสิ่งหนึ่งในช่องข้อความ?
RonLugge

125

ตัวเลือกนี้ปลอดภัยกว่า:

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

หรือวิธีที่รวดเร็ว:

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

1
ดูเหมือนจะเป็นคำตอบที่ดีกว่าเนื่องจากไม่มีแรงโยน
Patrick

1
นี่ควรเป็นคำตอบที่ได้รับการยอมรับ เป็นสิ่งที่ดีที่สุดและถูกต้อง!
Thomás Calmon

คำตอบที่ถูกต้อง สั้นและเรียบง่าย
PashaN

4

คุณยังสามารถใช้whereอนุประโยค

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

12
โดยที่ประโยคเป็นตัวป้องกันบูลีน แต่ไม่ได้โยนประเภทของbuttonภายในตัวห่วงซึ่งเป็นเป้าหมายของโซลูชันอื่น ๆ ดังนั้นนี้จะทำงานเฉพาะร่างกายห่วงเกี่ยวกับองค์ประกอบของview.subviewsซึ่งเป็นUIButtons แต่ไม่ได้ช่วยในการเรียกร้องเช่นAClassวิธีการ -specific buttonบน
John Whitley

1
@JohnWhitley คุณถูกต้องที่whereมีเพียงยามและไม่ได้โยน
edelaney05

whereอาจดูหรูหราและอ่านง่ายกว่าสำหรับกรณี (เล่นสำนวนโดยไม่ได้ตั้งใจ) ที่คุณต้องการเพียงบูลีนการ์ด
STO

3

คำตอบที่ให้มานั้นถูกต้องฉันแค่อยากจะเพิ่มสิ่งนี้เป็นการเพิ่มเติม

เมื่อใช้ for loop ที่มี force casting โค้ดจะหยุดทำงาน (ตามที่ผู้อื่นกล่าวไว้แล้ว)

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

แต่แทนที่จะใช้ if-clause

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

อีกวิธีหนึ่งคือการใช้ 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
    // ...
}

ซึ่งดูเหมือนว่าจะได้รับความสะดวกมากขึ้นถ้าองค์ประกอบบางอย่าง ( แต่ไม่ใช่ทั้งหมด) AClassของอาร์เรย์อาจเป็นชนิด

แม้ว่าในตอนนี้ (ณ Swift 5) ฉันจะใช้ for-case loop:

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 }) {

}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.