Beispiel für eine benutzerdefinierte UIView-Unterklasse
Normalerweise erstelle ich iOS-Apps ohne Storyboards oder Schreibfedern. Ich werde einige Techniken teilen, die ich gelernt habe, um Ihre Fragen zu beantworten.
Unerwünschte init
Methoden ausblenden
Mein erster Vorschlag ist, eine Basis UIView
zu deklarieren , um unerwünschte Initialisierer auszublenden. Ich habe diesen Ansatz in meiner Antwort auf "Ausblenden von Storyboard- und Nib-spezifischen Initialisierern in UI-Unterklassen" ausführlich erörtert . Hinweis: Bei diesem Ansatz wird davon ausgegangen, dass Sie BaseView
oder seine Nachkommen nicht in Storyboards oder Schreibfedern verwenden, da die App absichtlich abstürzt.
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")
}
}
Ihre benutzerdefinierte UIView-Unterklasse sollte von erben BaseView
. Es muss super.init () in seinem Initialisierer aufrufen. Es muss nicht implementiert werden init(coder:)
. Dies wird im folgenden Beispiel gezeigt.
Hinzufügen eines UITextField
Ich erstelle gespeicherte Eigenschaften für Unteransichten, auf die außerhalb der init
Methode verwiesen wird. Ich würde dies normalerweise für ein UITextField tun. Ich bevorzuge es, Unteransichten innerhalb der Deklaration der Unteransichtseigenschaft wie folgt zu instanziieren : let textField = UITextField()
.
Das UITextField ist nur sichtbar, wenn Sie es durch Aufrufen zur Unteransichtsliste der benutzerdefinierten Ansicht hinzufügen addSubview(_:)
. Dies wird im folgenden Beispiel gezeigt.
Programmatisches Layout ohne automatisches Layout
Das UITextField ist nur sichtbar, wenn Sie seine Größe und Position festlegen. Ich mache oft Layout in Code (ohne Auto Layout) innerhalb der layoutSubviews Methode .layoutSubviews()
wird anfänglich aufgerufen und immer dann, wenn ein Größenänderungsereignis auftritt. Dies ermöglicht das Anpassen des Layouts abhängig von der Größe von CustomView. Wenn CustomView beispielsweise auf verschiedenen Größen von iPhones und iPads in voller Breite angezeigt wird und sich an die Drehung anpasst, muss es viele Anfangsgrößen berücksichtigen und die Größe dynamisch ändern.
Sie können auf frame.height
und frame.width
innerhalb verweisen, layoutSubviews()
um die Abmessungen von CustomView als Referenz zu erhalten. Dies wird im folgenden Beispiel gezeigt.
Beispiel UIView-Unterklasse
Eine benutzerdefinierte UIView-Unterklasse, die ein UITextField enthält, das nicht implementiert werden muss 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)
}
}
Programmatisches Layout mit automatischem Layout
Sie können das Layout auch mithilfe des automatischen Layouts im Code implementieren. Da ich das nicht oft mache, werde ich kein Beispiel zeigen. Beispiele für die Implementierung des automatischen Layouts im Code finden Sie im Stapelüberlauf und an anderer Stelle im Internet.
Programmatische Layout-Frameworks
Es gibt Open Source-Frameworks, die das Layout in Code implementieren. Eine, die mich interessiert, aber noch nicht ausprobiert hat, ist LayoutKit . Es wurde vom Entwicklungsteam ein LinkedIn geschrieben. Aus dem Github-Repository: "LinkedIn hat LayoutKit erstellt, weil wir festgestellt haben, dass das automatische Layout für komplizierte Ansichtshierarchien in scrollbaren Ansichten nicht leistungsfähig genug ist."
Warum setzen fatalError
ininit(coder:)
Wenn Sie UIView-Unterklassen erstellen, die niemals in einem Storyboard oder einer Schreibfeder verwendet werden, können Sie Initialisierer mit unterschiedlichen Parametern und Initialisierungsanforderungen einführen, die von der init(coder:)
Methode nicht aufgerufen werden konnten . Wenn Sie init (coder :) nicht mit a versagt haben fatalError
, kann dies zu sehr verwirrenden Problemen führen, wenn Sie versehentlich in einem Storyboard / einer Feder verwendet werden. Der fatalError bestätigt diese Absichten.
required init?(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
Wenn Sie beim Erstellen der Unterklasse Code ausführen möchten, unabhängig davon, ob dieser im Code oder in einem Storyboard / einer Feder erstellt wurde, können Sie Folgendes tun (basierend auf der Antwort von Jeff Gu Kang ).
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
}
}