Я завжди вважав рішення "додати його як підзапис" незадовільним, бачачи, що він вкручується з розетками (1), (2) @IBInspectable
та (3). Замість цього дозвольте мені познайомити вас з магією awakeAfter:
, в NSObject
методі.
awakeAfter
дозволяє обміняти об'єкт, фактично прокинутий з NIB / Дошки розгортки, повністю іншим об'єктом. Потім цей об'єкт ставиться через процес гідратації, awakeFromNib
закликає його, додається як перегляд тощо.
Ми можемо використати це в підкласі "вирізання з картону" нашого перегляду, єдиною метою якого буде завантаження подання від НБУ та повернення його для використання на "Радці". Потім вкладений підклас задається інспектором особистих даних перегляду «Розгортка», а не початковим класом. Насправді це не повинно бути підкласом, щоб це працювало, але зробити його підкласом - це те, що дозволяє 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
}
}
One Важливим недоліком тут є те, що якщо ви визначите обмеження у ширині, висоті чи співвідношенні сторін на дошці даних, які не стосуються іншого виду, їх потрібно копіювати вручну. Обмеження, що стосуються двох поглядів, встановлюються на найближчого загального предка, і погляди пробуджуються із дошки розгортання зсередини, тому до того моменту, коли ці обмеження будуть гідратовані на огляді, заміни вже відбулися. Обмеження, які стосуються лише розглянутого перегляду, встановлюються безпосередньо на цьому огляді, і, таким чином, отримують переміщення, коли відбувається заміна, якщо вони не скопійовані.
Зауважте, що тут відбувається обмеження, встановлене на поданні на дошці розкадрування , скопійовано в нещодавно створений вигляд , який, можливо, вже має власні обмеження, визначені у його файлі ніб. Ці не зачіпаються.
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
}
}