ฉันมักจะพบวิธีแก้ปัญหา "เพิ่มเป็นมุมมองย่อย" ที่น่าพอใจเพราะมันสกรูที่มี (1) การชำระอัตโนมัติ (2) @IBInspectable
และ (3) แทนที่จะให้ฉันแนะนำคุณกับความมหัศจรรย์ของawakeAfter:
การNSObject
วิธีการ
awakeAfter
ให้คุณสลับวัตถุที่ตื่นขึ้นมาจริง ๆ จาก NIB / Storyboard ด้วยวัตถุอื่นโดยสิ้นเชิง จากนั้นวัตถุนั้นจะถูกนำไปผ่านกระบวนการไฮเดรชั่นซึ่งถูกawakeFromNib
เรียกบนมันจะถูกเพิ่มเป็นมุมมอง ฯลฯ
เราสามารถใช้สิ่งนี้ในคลาสย่อย "กระดาษแข็งที่ถูกตัดออก" ในมุมมองของเราจุดประสงค์เพียงอย่างเดียวคือการโหลดมุมมองจาก NIB และส่งคืนเพื่อใช้ใน Storyboard คลาสย่อยที่ฝังได้นั้นจะถูกระบุในตัวตรวจสอบข้อมูลประจำตัวของมุมมองสตอรีบอร์ดแทนที่จะเป็นคลาสดั้งเดิม ไม่จำเป็นต้องเป็นคลาสย่อยเพื่อให้สามารถใช้งานได้ แต่การทำให้คลาสย่อยเป็นสิ่งที่ทำให้ IB สามารถเห็นคุณสมบัติ IBInspectable / IBOutlet ใด ๆ
หม้อไอน้ำพิเศษนี้อาจดูเหมือนไม่ดี - และในแง่ที่มันเป็นเพราะUIStoryboard
จะจัดการกับสิ่งนี้ได้อย่างราบรื่น - แต่มันมีข้อได้เปรียบในการปล่อยให้ NIB ดั้งเดิมและUIView
คลาสย่อยไม่มีการแก้ไขอย่างสมบูรณ์ บทบาทที่เล่นนั้นโดยพื้นฐานแล้วคือคลาสอะแดปเตอร์หรือบริดจ์และใช้ได้อย่างสมบูรณ์แบบออกแบบอย่างชาญฉลาดในฐานะคลาสเพิ่มเติมแม้ว่ามันจะน่าเศร้าก็ตาม ในทางกลับกันหากคุณต้องการที่จะให้ความสำคัญกับชั้นเรียนของคุณโซลูชันของ @ BenPatch นั้นทำงานโดยใช้โปรโตคอลที่มีการเปลี่ยนแปลงเล็กน้อยอื่น ๆ คำถามที่ว่าวิธีแก้ปัญหาแบบไหนดีกว่ากันในเรื่องของโปรแกรมเมอร์สไตล์: ไม่ว่าใครจะชอบองค์ประกอบของวัตถุหรือมรดกหลายอย่าง
หมายเหตุ: ชุดคลาสบนมุมมองในไฟล์ NIB ยังคงเหมือนเดิม ประเภทรองฝังเป็นเพียงใช้ในสตอรี่บอร์ด ไม่สามารถใช้คลาสย่อยเพื่อสร้างอินสแตนซ์ของมุมมองในโค้ดดังนั้นจึงไม่ควรมีตรรกะเพิ่มเติมใด ๆ มันควรเพียง แต่มีawakeAfter
ตะขอ
class MyCustomEmbeddableView: MyCustomView {
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
return (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)! as Any
}
}
draw ข้อเสียเปรียบที่สำคัญประการหนึ่งคือถ้าคุณกำหนดข้อ จำกัด ความกว้างความสูงหรืออัตราส่วนในกระดานเรื่องราวที่ไม่เกี่ยวข้องกับมุมมองอื่นต้องทำการคัดลอกด้วยตนเอง ข้อ จำกัด ที่เกี่ยวข้องกับสองมุมมองถูกติดตั้งบนบรรพบุรุษร่วมที่ใกล้ที่สุดและมุมมองจะถูกปลุกจากกระดานเรื่องราวจากด้านใน - ด้านนอกดังนั้นเมื่อถึงเวลาที่มีข้อ จำกัด เหล่านี้จะถูกทำให้ชุ่มอยู่บนหน้าผู้ดูแลการแลกเปลี่ยนเกิดขึ้นแล้ว ข้อ จำกัด ที่เกี่ยวข้องกับมุมมองที่เป็นปัญหาจะถูกติดตั้งโดยตรงบนมุมมองนั้นและทำให้ถูกโยนเมื่อมีการสลับเกิดขึ้นยกเว้นว่ามีการคัดลอก
โปรดทราบว่าสิ่งที่เกิดขึ้นที่นี่คือข้อ จำกัด ที่ติดตั้งบนมุมมองในกระดานเรื่องราวจะถูกคัดลอกไปยังมุมมองที่สร้างอินสแตนซ์ใหม่ซึ่งอาจมีข้อ จำกัด ของตัวเองอยู่แล้วซึ่งกำหนดไว้ในไฟล์ปลายปากกา สิ่งเหล่านั้นไม่ได้รับผลกระทบ
class MyCustomEmbeddableView: MyCustomView {
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
let newView = (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)!
for constraint in constraints {
if constraint.secondItem != nil {
newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: newView, attribute: constraint.secondAttribute, multiplier: constraint.multiplier, constant: constraint.constant))
} else {
newView.addConstraint(NSLayoutConstraint(item: newView, attribute: constraint.firstAttribute, relatedBy: constraint.relation, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: constraint.constant))
}
}
return newView as Any
}
}
instantiateViewFromNib
UIView
เป็นส่วนขยายของชนิดที่ปลอดภัยในการ สิ่งที่มันทำคือวนลูปผ่านวัตถุของ NIB จนกว่าจะพบวัตถุที่ตรงกับประเภท โปรดทราบว่าประเภททั่วไปเป็นค่าตอบแทนดังนั้นจะต้องมีการระบุประเภทที่ไซต์การโทร
extension UIView {
public class func instantiateViewFromNib<T>(_ nibName: String, inBundle bundle: Bundle = Bundle.main) -> T? {
if let objects = bundle.loadNibNamed(nibName, owner: nil) {
for object in objects {
if let object = object as? T {
return object
}
}
}
return nil
}
}