Przykład niestandardowej podklasy UIView
Zwykle tworzę aplikacje na iOS bez używania scenorysów lub końcówek. Podzielę się kilkoma technikami, których nauczyłem się, aby odpowiedzieć na Twoje pytania.
Ukrywanie niechcianych initmetod
Moja pierwsza sugestia to zadeklarowanie bazy UIViewdo ukrycia niechcianych inicjatorów. Szczegółowo omówiłem to podejście w mojej odpowiedzi na temat „Jak ukryć inicjatory specyficzne dla serii ujęć i końcówek w podklasach interfejsu użytkownika” . Uwaga: to podejście zakłada, że nie będziesz używać BaseViewani jego elementów podrzędnych w scenorysach lub stalówkach, ponieważ celowo spowoduje to awarię aplikacji.
class BaseView: UIView {
init() {
super.init(frame: CGRect.zero)
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
}
Twoja niestandardowa podklasa UIView powinna dziedziczyć z BaseView. Musi wywołać super.init () w swoim inicjatorze. Nie trzeba go wdrażać init(coder:). Pokazuje to poniższy przykład.
Dodanie UITextField
Tworzę przechowywane właściwości dla widoków podrzędnych, do których istnieją odniesienia poza initmetodą. Zazwyczaj robiłbym to dla UITextField. Wolę subviews instancji w deklaracji właściwości podrzędny tak: let textField = UITextField().
UITextField nie będzie widoczny, chyba że dodasz go do listy widoku podrzędnego niestandardowego widoku przez wywołanie addSubview(_:). Pokazuje to poniższy przykład.
Układ programowy bez układu automatycznego
UITextField nie będzie widoczne, chyba że ustawisz jego rozmiar i położenie. Często tworzę układ w kodzie (nie używam Auto Layout) w ramach metody layoutSubviews .layoutSubviews()jest wywoływana początkowo i zawsze, gdy ma miejsce zdarzenie zmiany rozmiaru. Umożliwia to dostosowanie układu w zależności od rozmiaru CustomView. Na przykład, jeśli CustomView pojawia się w pełnej szerokości na różnych rozmiarach iPhone'ów i iPadów i dostosowuje się do obrotu, musi uwzględniać wiele początkowych rozmiarów i dynamicznie zmieniać rozmiar.
Możesz odwołać się do frame.heighti frame.widthw obrębie, layoutSubviews()aby uzyskać wymiary CustomView w celach informacyjnych. Pokazuje to poniższy przykład.
Przykładowa podklasa UIView
Niestandardowa podklasa UIView zawierająca UITextField, której nie trzeba implementować init?(coder:).
class CustomView: BaseView {
let textField = UITextField()
override init() {
super.init()
textField.placeholder = "placeholder text"
textField.font = UIFont.systemFont(ofSize: 12)
addSubview(textField)
}
override func layoutSubviews() {
super.layoutSubviews()
textField.frame.size = CGSize(width: frame.width - 20, height: 30)
textField.frame.origin = CGPoint(x: 10, y: 10)
}
}
Układ programowy z układem automatycznym
Możesz również zaimplementować układ za pomocą automatycznego układu w kodzie. Ponieważ nie robię tego często, nie będę pokazywał przykładu. Przykłady implementacji automatycznego układu można znaleźć w kodzie na stronie Stack Overflow oraz w innych miejscach w Internecie.
Programmatic Layout Framework
Istnieją platformy open source, które implementują układ w kodzie. Interesuje mnie LayoutKit, ale nie próbowałem . Został napisany przez zespół programistów LinkedIn. Z repozytorium Github: „LinkedIn utworzył LayoutKit, ponieważ odkryliśmy, że Auto Layout nie jest wystarczająco wydajny dla skomplikowanych hierarchii widoków w przewijalnych widokach”.
Dlaczego umieścić fatalErrorwinit(coder:)
Podczas tworzenia podklas UIView, które nigdy nie będą używane w scenorysie lub końcówce, możesz wprowadzić inicjatory z różnymi parametrami i wymaganiami inicjalizacyjnymi, których nie można wywołać za pomocą init(coder:)metody. Jeśli nie pomylisz init (kodera :) z a fatalError, może to prowadzić do bardzo mylących problemów, jeśli zostanie przypadkowo użyte w scenorysie / końcówce. FatalError potwierdza te zamiary.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Jeśli chcesz uruchomić jakiś kod, gdy podklasa jest tworzona, niezależnie od tego, czy jest tworzona w kodzie, czy w scenorysie / końcówce, możesz zrobić coś takiego (na podstawie odpowiedzi Jeffa Gu Kanga )
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
initCommon()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initCommon()
}
func initCommon() {
}
}