Tôi luôn thấy giải pháp "thêm nó dưới dạng một cuộc phỏng vấn" không đạt yêu cầu, khi thấy nó bắt vít với (1) tự động thanh toán, (2) @IBInspectable
và (3) các cửa hàng. Thay vào đó, hãy để tôi giới thiệu cho bạn về phép thuật của awakeAfter:
một NSObject
phương pháp.
awakeAfter
cho phép bạn trao đổi đối tượng thực sự thức dậy từ NIB / Storyboard với một đối tượng hoàn toàn khác. Đó là sau đó đối tượng được đưa qua quá trình hydrat hóa, đã awakeFromNib
được gọi vào nó, được thêm vào như một cái nhìn, vv
Chúng tôi có thể sử dụng điều này trong một lớp con "cắt bìa cứng" theo quan điểm của chúng tôi, mục đích duy nhất sẽ là tải chế độ xem từ NIB và trả lại để sử dụng trong Bảng phân cảnh. Lớp con có thể nhúng sau đó được chỉ định trong trình kiểm tra danh tính của khung nhìn Storyboard, thay vì lớp gốc. Nó không thực sự phải là một lớp con để nó hoạt động, nhưng làm cho nó trở thành một lớp con là điều cho phép IB thấy bất kỳ thuộc tính IBInutect / IBOutlet nào.
Bản tóm tắt bổ sung này có vẻ như tối ưu, và theo một nghĩa nào đó, bởi vì lý tưởng là UIStoryboard
sẽ xử lý một cách liền mạch này nhưng nó có lợi thế là để NIB ban đầu và UIView
lớp con hoàn toàn không bị thay đổi. Vai trò của nó về cơ bản là của một bộ điều hợp hoặc lớp cầu nối, và hoàn toàn hợp lệ, thiết kế khôn ngoan, như một lớp bổ sung, ngay cả khi nó đáng tiếc. Mặt khác, nếu bạn thích phân tích kỹ hơn với các lớp của mình, giải pháp của @ BenPatch hoạt động bằng cách triển khai một giao thức với một số thay đổi nhỏ khác. Câu hỏi về giải pháp nào tốt hơn là giải quyết vấn đề về phong cách lập trình viên: liệu người ta thích thành phần đối tượng hay nhiều kế thừa.
Lưu ý: lớp được đặt trên dạng xem trong tệp NIB vẫn giữ nguyên. Lớp con có thể nhúng chỉ được sử dụng trong bảng phân cảnh. Lớp con không thể được sử dụng để khởi tạo khung nhìn trong mã, vì vậy nó không nên có bất kỳ logic bổ sung nào. Nó chỉ nên chứa awakeAfter
móc.
class MyCustomEmbeddableView: MyCustomView {
override func awakeAfter(using aDecoder: NSCoder) -> Any? {
return (UIView.instantiateViewFromNib("MyCustomView") as MyCustomView?)! as Any
}
}
Một nhược điểm đáng kể ở đây là nếu bạn xác định các ràng buộc về chiều rộng, chiều cao hoặc tỷ lệ khung hình trong bảng phân cảnh không liên quan đến chế độ xem khác thì chúng phải được sao chép thủ công. Các ràng buộc liên quan đến hai chế độ xem được cài đặt trên tổ tiên chung gần nhất và các chế độ xem được đánh thức từ bảng phân cảnh từ trong ra ngoài, do đó, tại thời điểm các ràng buộc đó được ngậm nước trên giám sát, việc hoán đổi đã xảy ra. Các ràng buộc chỉ liên quan đến chế độ xem trong câu hỏi được cài đặt trực tiếp trên chế độ xem đó và do đó được đưa ra khi hoán đổi xảy ra trừ khi chúng được sao chép.
Lưu ý rằng những gì đang xảy ra ở đây là các ràng buộc được cài đặt trên chế độ xem trong bảng phân cảnh được sao chép sang chế độ xem mới được khởi tạo , có thể có các ràng buộc của chính nó, được xác định trong tệp nib của nó. Những người không bị ảnh hưởng.
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
là một phần mở rộng an toàn loại UIView
. Tất cả những gì nó làm là lặp qua các đối tượng của NIB cho đến khi tìm thấy một đối tượng phù hợp với loại. Lưu ý rằng loại chung là giá trị trả về , vì vậy loại phải được chỉ định tại trang web cuộc gọi.
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
}
}