Erstellen einer wiederverwendbaren UIView mit xib (und Laden vom Storyboard)
OK, es gibt Dutzende von posts auf StackOverflow über diese, aber keine ist besonders klar auf die Lösung. Ich möchte erstellen Sie eine benutzerdefinierte UIView
mit einer begleitenden xib-Datei. Die Anforderungen sind:
- Keine separate
UIViewController
– eine völlig eigenständige Klasse - Filialen in der Klasse, mir zu erlauben, set - /get-Eigenschaften der Ansicht
Meinem aktuellen Ansatz, um dies zu tun, ist:
- Überschreiben
-(id)initWithFrame:
-(id)initWithFrame:(CGRect)frame { self = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] objectAtIndex:0]; self.frame = frame; return self; }
- Instanziieren programmgesteuert mithilfe
-(id)initWithFrame:
in meinem view controllerMyCustomView *myCustomView = [[MyCustomView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)]; [self.view insertSubview:myCustomView atIndex:0];
Dies funktioniert gut (obwohl nie Anrufe [super init]
und einfache Einstellung auf das Objekt mit dem Inhalt der geladenen Spitze scheint ein wenig den Verdacht – es ist ein Ratschlag hier, um fügen Sie eine Untersicht in diesem Falldie auch gut funktioniert). Aber ich möchte in der Lage sein zu instanziieren der view aus dem storyboard auch. Also ich kann:
- Ort ein
UIView
auf einer übergeordneten Ansicht in der storyboard - - Festlegen der benutzerdefinierten Klasse
MyCustomView
- Überschreiben
-(id)initWithCoder:
– den code habe ich gesehen, die meisten oft passt ein Muster wie das folgende:-(id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { [self initializeSubviews]; } return self; } -(id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { [self initializeSubviews]; } return self; } -(void)initializeSubviews { typeof(view) view = [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil] objectAtIndex:0]; [self addSubview:view]; }
Natürlich, dies funktioniert nicht, als ob ich den Ansatz oben, oder ob ich instanziieren programmatisch, beide am Ende den rekursiven Aufruf -(id)initWithCoder:
beim betreten -(void)initializeSubviews
und laden der nib-Datei.
Einigen anderen Fragen befassen sich mit diese wie hierhierhier und hier. Jedoch, keine der Antworten ist zufriedenstellend behebt das problem:
- Einen gemeinsamen Vorschlag zu sein scheint, das einbetten der gesamten Klasse in einem UIViewController, und tun das nib laden gibt, aber dies scheint nicht optimal zu mir, denn es erfordert das hinzufügen einer weiteren Datei nur als wrapper
Könnte jemand Ratschläge geben, wie dieses problem zu beheben, und erhalten Sie die Arbeit Verkaufsstellen in einem benutzerdefinierten UIView
mit minimalem Aufwand/keine dünne controller wrapper? Oder gibt es eine alternative, die sauberer Art, die Dinge mit mindestens boilerplate-code?
InformationsquelleAutor der Frage Ken Chatfield | 2014-02-20
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dein problem ist der Aufruf
loadNibNamed:
aus (ein Nachkomme)initWithCoder:
.loadNibNamed:
ruft interninitWithCoder:
. Wenn Sie möchten, überschreiben Sie die storyboard-coder, und immer laden Sie Ihre xib-Umsetzung, schlage ich die folgende Technik. Fügen Sie eine Eigenschaft, um Ihre view-Klasse, und in der xib-Datei, legen Sie es auf einen vorbestimmten Wert (in User-Definierten Laufzeit-Attribute). Nun, nach dem Aufruf[super initWithCoder:aDecoder];
überprüfen Sie den Wert der Eigenschaft. Wenn er den vorbestimmten Wert nicht nennen[self initializeSubviews];
.So, in etwa so:
InformationsquelleAutor der Antwort Leo Natan
Beachten Sie, dass dieses QA - (wie viele), ist eigentlich nur von historischem Interesse.
HeuteFür Jahre und Jahre jetzt in iOS alles ist nur eine container-Ansicht. Vollständige tutorial hier(Ja auch Apple endlich Hinzugefügt Storyboard-Referenzenvor einiger Zeit jetzt, so dass es viel einfacher ist.)
Hier ist ein typisches storyboard mit container-Ansichten überall. Alles ist ein container-Ansicht. Es ist nur, wie Sie apps.
(Als Kuriosum, KenC Antwort zeigt genau, wie es getan werden, um das laden einer xib-eine Art wrapper-Ansicht, da kann man nicht wirklich "zuordnen, um sich selbst".)
InformationsquelleAutor der Antwort Fattie
Ich bin das hinzufügen dieser als separater post zu aktualisieren, die situation mit der Veröffentlichung von Swift. Der beschriebene Ansatz von LeoNatan funktioniert perfekt in Objective-C. Jedoch, die strengere compile-Zeit überprüft, verhindern
self
zugewiesen wird, beim laden aus der xib-Datei im Swift.Als Ergebnis, es ist keine option, sondern um die Ansicht geladen, aus der xib-Datei als eine Untersicht des benutzerdefinierten UIView-Unterklasse, statt ersetzen, selbst ganz. Dies ist Analog zu dem zweiten Ansatz skizziert, in der ursprünglichen Frage. Einen groben überblick über eine Klasse in Swift mit diesem Ansatz ist wie folgt:
Der Nachteil dieses Ansatzes ist die Einführung einer zusätzlichen, redundanten layer in der view-Hierarchie, die nicht vorhanden ist, wenn mit dem skizzierten Ansatz von LeoNatan in Objective-C. dies könnte sich Jedoch als ein notwendiges übel und ein Produkt von der grundsätzlichen Art, wie die Dinge sind so gestaltet, Xcode (es scheint immer noch verrückt für mich, dass es so schwierig ist die Verknüpfung eines custom UIView-Klasse mit einem UI-layout in einer Weise, die konsequent arbeitet sowohl über storyboards und vom code) – ersetzen
self
Großhandel in der Initialisierung vor der schien nie wie ein besonders interpretierbaren Art und Weise, Dinge zu tun, auch wenn es im wesentlichen zwei Klassen anzeigen-per-view scheint nicht so toll.Dennoch, eine glückliche Ergebnis dieses Ansatzes ist, dass wir Sie nicht mehr benötigen, um die Ansicht der benutzerdefinierten Klasse, unsere Klasse Datei im interface builder, um sicherzustellen, dass Sie korrekte Verhalten bei der Zuordnung zu
self
und so den rekursiven Aufrufinit(coder aDecoder: NSCoder)
bei der AusstellungloadNibNamed()
gebrochen ist (durch die nicht-Einstellung der benutzerdefinierten Klasse in der xib-Datei, dieinit(coder aDecoder: NSCoder)
von plain-vanilla-UIView, anstatt unsere custom version namens statt).Obwohl wir nicht Klasse Anpassungen an der view gespeichert in der xib-direkt, wir sind immer noch in der Lage, den Blick auf unsere "Eltern" UIView-Unterklasse, mit Steckdosen/Aktionen etc. nach der Einstellung der Datei-Eigentümer der Blick auf unsere custom class:
Einem video demonstriert die Implementierung eines solchen view-Klasse Schritt für Schritt mit diesem Ansatz finden Sie in dem folgenden video.
InformationsquelleAutor der Antwort Ken Chatfield
STEP1. Ersetzen
self
vom StoryboardErsetzen
self
iminitWithCoder:
Methode schlägt fehl mit dem folgenden Fehler.Stattdessen können Sie ersetzen Sie dekodiert Objekt mit
awakeAfterUsingCoder:
(nichtawakeFromNib
). wie:STEP2. Verhindert rekursiven Aufruf
Natürlich auch dadurch rekursiven Aufruf problem. (storyboard-Decodierung - >
awakeAfterUsingCoder:
->loadNibNamed:
->awakeAfterUsingCoder:
->loadNibNamed:
-> ...)So haben Sie zum überprüfen des aktuellen
awakeAfterUsingCoder:
genannt wird in der Storyboard-Dekodierung oder XIB-Dekodierung.Sie haben mehrere Möglichkeiten, das zu tun:
a) die Benutzung von privaten
@property
die im NIB.und setzen Sie "benutzerdefinierte Laufzeit-Attribute" nur noch in " MyCustomView.xib'.
Vorteile:
Nachteile:
setXib:
aufgerufen werden NACHawakeAfterUsingCoder:
b) Überprüfen Sie, ob
self
hat alle UntersichtenIn der Regel haben Sie Untersichten in der xib -, aber nicht in das storyboard.
Vorteile:
Nachteile:
c) Legen Sie eine statische Flagge während
loadNibNamed:
AnrufVorteile:
Nachteile:
d) private Unterklasse in XIB
Deklarieren Sie beispielsweise
_NIB_MyCustomView
als Unterklasse vonMyCustomView
.Und verwenden Sie
_NIB_MyCustomView
stattMyCustomView
in Ihrem XIB nur.MyCustomView.h:
MyCustomView.m:
Vorteile:
if
imMyCustomView
Nachteile:
_NIB_
trick in xib Interface Buildere) Unterklasse als Platzhalter in der Storyboard -
Ähnlich
d)
aber verwenden Unterklasse im Storyboard, original-Klasse in XIB.Hier erklären wir
MyCustomViewProto
als Unterklasse vonMyCustomView
.Vorteile:
MyCustomView
.if
überprüfen dasselbe wied)
Nachteile:
Ich denke
e)
ist die sicherste und sauberste Strategie. So beschließen wir, hier.SCHRITT 3. Kopieren von Eigenschaften
Nach
loadNibNamed:
im 'awakeAfterUsingCoder:' Sie müssen kopieren mehrere Eigenschaften ausself
die dekodierten Instanz f Storyboard.frame
und autolayout/autoresize Eigenschaften besonders wichtig sind.ENDGÜLTIGE LÖSUNG
Wie Sie sehen können, ist dies ein bit von boilerplate-code. Wir implementieren können, die Sie als 'Kategorie'.
Hier spreche ich allgemein verwendet
UIView+loadFromNib
code.Anhand dieser können Sie erklären
MyCustomViewProto
wie:XIB:
Storyboard:
Ergebnis:
InformationsquelleAutor der Antwort rintaro
Nicht vergessen
Zwei wichtige Punkte:
Kam ich zu diesem Q&Eine Seite mehrere Male, während das lernen, um wiederverwendbare Ansichten. Vergessen Sie die oben genannten Punkte gemacht, mich verschwenden viel Zeit versucht herauszufinden, was die Folge hatte, dass eine unendliche Rekursion zu passieren. Diese Punkte sind erwähnt in anderen Antworten hier und anderswoaber ich möchte nur nochmals betonen diese hier.
Meine volle Rasche Antwort mit den Schritten ist hier.
InformationsquelleAutor der Antwort Suragch
Gibt es eine Lösung, die ist viel sauberer als die oben genannten Lösungen:
https://www.youtube.com/watch?v=xP7YvdlnHfA
Keine Runtime-Eigenschaften, kein rekursiver Aufruf problem.
Ich versuchte es und es funktionierte wie ein Charme mithilfe von storyboard und von XIB mit IBOutlet Eigenschaften (iOS8.1, XCode6).
Glück für die Codierung!
InformationsquelleAutor der Antwort ingaham