Приклад спеціального підкласу UIView
Зазвичай я створюю додатки для iOS, не використовуючи дошки розкадрувань чи гвинти. Я поділюсь деякими прийомами, які я навчився відповідати на ваші запитання.
Приховування небажаних initметодів
Моя перша пропозиція - оголосити базу UIViewдля приховування небажаних ініціалізаторів. Я детально обговорив цей підхід у своїй відповіді на тему "Як приховати розкадрування та конкретні ініціалізатори Nib в підкласах інтерфейсу користувача" . Примітка. Цей підхід передбачає, що ви не будете використовувати BaseViewйого або його нащадків у розкадровці чи перо, оскільки це навмисно спричинить збій програми.
class BaseView: UIView {
// This initializer hides init(frame:) from subclasses
init() {
super.init(frame: CGRect.zero)
}
// This attribute hides `init(coder:)` from subclasses
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
Ваш власний підклас UIView повинен успадковуватися від BaseView. Він повинен викликати super.init () в своєму ініціалізаторі. Це не потрібно реалізовувати init(coder:). Це продемонстровано на прикладі нижче.
Додавання UITextField
Я створюю збережені властивості для підпоглядів, на які посилаються поза initметодом. Я зазвичай це роблю для UITextField. Я вважаю за краще , щоб створити екземпляр підвиди в декларації майна підвиду , як це: let textField = UITextField().
Поле UITextFi не буде видимим, якщо ви не додасте його до списку підвидного перегляду спеціального вигляду, зателефонувавши addSubview(_:). Це продемонстровано на прикладі нижче.
Програматичне макетування без автоматичного макетування
UITextField не буде видимим, якщо ви не встановите його розмір та положення. Я часто роблю макет у коді (не використовуючи автоматичний макет) в межах методу layoutSubviews . layoutSubviews()викликається спочатку і щоразу, коли трапляється подія зміни розміру. Це дозволяє регулювати макет залежно від розміру CustomView. Наприклад, якщо CustomView відображається на повну ширину на різних розмірах iPhone та iPad і коригується для обертання, він повинен вміщувати безліч початкових розмірів і динамічно змінювати розмір.
Ви можете звернутися до frame.heightі frame.widthв межах, layoutSubviews()щоб отримати розміри CustomView для довідки. Це продемонстровано на прикладі нижче.
Приклад підкласу UIView
Спеціальний підклас UIView, що містить UITextField, який не потрібно реалізовувати init?(coder:).
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
// configure and add textField as subview
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
// Set textField size and position
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Програмний макет з автоматичною компонуванням
Ви також можете реалізувати макет, використовуючи Auto Layout у коді. Оскільки я не часто це роблю, я не буду наводити приклад. Ви можете знайти приклади реалізації автоматичного макетування в коді на Stack Overflow та в інших місцях Інтернету.
Фреймворки програмного макету
Є фреймворки з відкритим кодом, які реалізують макет у коді. Мене цікавить, але я не пробував, це LayoutKit . Це написано командою розробників LinkedIn. З репозиторію Github: "LinkedIn створив LayoutKit, оскільки ми виявили, що автоматичне макетування недостатньо ефективне для складних ієрархій подань у прокручуваних поданнях."
Навіщо ставити fatalErrorвinit(coder:)
Створюючи підкласи UIView, які ніколи не будуть використовуватися в дошці розкадрування або вказівці, ви можете ввести ініціалізатори з різними параметрами та вимогами ініціалізації, які неможливо викликати init(coder:)методом. Якщо ви не вийшли з ладу init (coder :) з a fatalError, це може призвести до дуже заплутаних проблем вниз за лінією, якщо випадково його використати в дошці / скрипці. FatalError стверджує ці наміри.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Якщо ви хочете запустити якийсь код, коли підклас створений незалежно від того, створений він у коді чи дошці / нибі, тоді ви можете зробити щось на кшталт наступного (на основі відповіді Джеффа Гу Канга )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
// Your custom initialization code
}
}