Приклад спеціального підкласу 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
}
}