Ich möchte eine Ansicht in mehreren Viewcontrollern in einem Storyboard verwenden. Daher habe ich darüber nachgedacht, die Ansicht in einer externen xib zu entwerfen, damit Änderungen in jedem Ansichtscontroller berücksichtigt werden. Aber wie kann man eine Ansicht von einem externen xib in ein Storyboard laden und ist das überhaupt möglich? Wenn dies nicht der Fall ist, welche anderen Alternativen stehen zur Verfügung, um der obigen Situation zu entsprechen?
ios
uiview
storyboard
xib
Sebastian Hoffmann
quelle
quelle
Antworten:
Mein vollständiges Beispiel ist hier , aber ich werde unten eine Zusammenfassung geben.
Layout
Fügen Sie Ihrem Projekt eine .swift- und eine .xib-Datei mit jeweils demselben Namen hinzu. Die .xib-Datei enthält Ihr benutzerdefiniertes Ansichtslayout (vorzugsweise unter Verwendung von Einschränkungen für das automatische Layout).
Machen Sie die schnelle Datei zum Eigentümer der xib-Datei.
Code
Fügen Sie der .swift-Datei den folgenden Code hinzu und verbinden Sie die Outlets und Aktionen mit der .xib-Datei.
Benutze es
Verwenden Sie Ihre benutzerdefinierte Ansicht an einer beliebigen Stelle in Ihrem Storyboard. Fügen Sie einfach ein hinzu
UIView
und setzen Sie den Klassennamen auf Ihren benutzerdefinierten Klassennamen.quelle
init(frame:)
. Weitere Informationen finden Sie in diesem Tutorial .Für eine Weile war Christopher Swaseys Ansatz der beste, den ich gefunden hatte. Ich habe ein paar leitende Entwickler in meinem Team danach gefragt und einer von ihnen hatte die perfekte Lösung ! Es erfüllt alle Bedenken, die Christopher Swasey so eloquent angesprochen hat, und es erfordert keinen Code für die Unterklasse des Boilerplates (mein Hauptanliegen bei seinem Ansatz). Es gibt ein Gotcha , aber ansonsten ist es ziemlich intuitiv und einfach zu implementieren.
MyCustomClass.swift
MyCustomClass.xib
File's Owner
von der .xib Datei Ihre benutzerdefinierte Klasse zu sein (MyCustomClass
)class
Wert (unteridentity Inspector
) für Ihre benutzerdefinierte Ansicht in der .xib-Datei leer. Ihre benutzerdefinierte Ansicht hat also keine angegebene Klasse, aber den Eigentümer einer bestimmten Datei.Assistant Editor
.Connections Inspector
Sie feststellen, dass Ihre Referenzierungs-Outlets nicht auf Ihre benutzerdefinierte Klasse (dhMyCustomClass
) verweisen, sondern auf dieseFile's Owner
. Da diesFile's Owner
als Ihre benutzerdefinierte Klasse angegeben ist, werden die Steckdosen angeschlossen und funktionieren ordnungsgemäß.NibLoadable
unten angegebenen Protokoll entspricht..swift
Name Ihrer benutzerdefinierten Klassendatei von Ihrem.xib
Dateinamen unterscheidet, legen Sie dienibName
Eigenschaft als Namen Ihrer.xib
Datei fest.required init?(coder aDecoder: NSCoder)
undoverride init(frame: CGRect)
aufrufensetupFromNib()
wie im folgenden Beispiel.MyCustomClass
) fest.Hier ist das Protokoll, auf das Sie verweisen möchten:
Und hier ist ein Beispiel dafür
MyCustomClass
, wie das Protokoll implementiert wird (wobei die .xib-Datei benannt wirdMyCustomClass.xib
):HINWEIS: Wenn Sie das Gotcha verpassen und den
class
Wert in Ihrer .xib-Datei als benutzerdefinierte Klasse festlegen, wird es nicht im Storyboard gezeichnet und Sie erhalten eineEXC_BAD_ACCESS
Fehlermeldung, wenn Sie die App ausführen, da sie in einer Endlosschleife von stecken bleibt versuchen, die Klasse von der Schreibfeder aus mit derinit?(coder aDecoder: NSCoder)
Methode zu initialisieren , die dann aufruftSelf.nib.instantiate
und dieinit
erneut aufruft .quelle
setupFromNib()
, scheint bestimmte seltsame Probleme beim automatischen Layout mit Tabellenzellen mit automatischer Größenanpassung zu beheben, die XIB-erstellte Ansichten enthalten.@IBDesignable
Kompatibilität. Ich kann nicht glauben, warum Xcode oder UIKit beim Hinzufügen einer UIView-Datei nicht standardmäßig so etwas bereitstellen.Angenommen, Sie haben eine xib erstellt, die Sie verwenden möchten:
1) Erstellen Sie eine benutzerdefinierte Unterklasse von UIView (Sie können zu Datei -> Neu -> Datei ... -> Cocoa Touch-Klasse gehen. Stellen Sie sicher, dass "Unterklasse von:" "UIView" ist).
2) Fügen Sie dieser Ansicht bei der Initialisierung eine Ansicht hinzu, die auf der xib als Unteransicht basiert.
In Obj-C
In Swift 2
In Swift 3
3) Wenn Sie es in Ihrem Storyboard verwenden möchten, fügen Sie wie gewohnt eine UIView hinzu, wählen Sie die neu hinzugefügte Ansicht aus und rufen Sie den Identitätsinspektor auf (das dritte Symbol oben rechts, das wie ein Rechteck mit Linien darin aussieht). und geben Sie den Namen Ihrer Unterklasse als "Klasse" unter "Benutzerdefinierte Klasse" ein.
quelle
xibView.frame = self.frame;
sollte seinxibView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
, sonst hat xibView einen Versatz, wenn die Ansicht zum Storyboard hinzugefügt wird.Ich fand die Lösung "Als Unteransicht hinzufügen" immer unbefriedigend, da sie mit (1) Autolayout-, (2)
@IBInspectable
und (3) Steckdosen verschraubt ist. Lassen Sie mich Ihnen stattdessen die MagieawakeAfter:
einerNSObject
Methode vorstellen .awakeAfter
Mit dieser Option können Sie das tatsächlich von einer NIB / Storyboard aufgeweckte Objekt gegen ein völlig anderes Objekt austauschen. Dieses Objekt wird dann dem Hydratationsprozess unterzogen, hat esawakeFromNib
aufgerufen, wird als Ansicht hinzugefügt usw.Wir können dies in einer Unterklasse "Pappausschnitt" unserer Ansicht verwenden, deren einziger Zweck darin besteht, die Ansicht von der NIB zu laden und zur Verwendung im Storyboard zurückzugeben. Die einbettbare Unterklasse wird dann im Identitätsinspektor der Storyboard-Ansicht und nicht in der ursprünglichen Klasse angegeben. Es muss eigentlich keine Unterklasse sein, damit dies funktioniert, aber wenn Sie es zu einer Unterklasse machen, kann IB alle IBInspectable / IBOutlet-Eigenschaften anzeigen.
Diese zusätzliche Boilerplate mag suboptimal erscheinen - und in gewissem Sinne, weil sie dies im Idealfall
UIStoryboard
nahtlos handhaben würde -, hat jedoch den Vorteil, dass die ursprüngliche NIB undUIView
Unterklasse vollständig unverändert bleiben. Die Rolle, die es spielt, ist im Grunde die eines Adapters oder einer Bridge-Klasse und ist in Bezug auf das Design als zusätzliche Klasse absolut gültig, auch wenn es bedauerlich ist. Auf der anderen Seite, wenn Sie es vorziehen, mit Ihren Klassen sparsam umzugehen, implementiert die Lösung von @ BenPatch ein Protokoll mit einigen anderen geringfügigen Änderungen. Die Frage, welche Lösung besser ist, hängt vom Programmierstil ab: ob man die Objektzusammensetzung oder die Mehrfachvererbung bevorzugt.Hinweis: Die in der Ansicht in der NIB-Datei festgelegte Klasse bleibt unverändert. Die einbettbare Unterklasse wird nur im Storyboard verwendet. Die Unterklasse kann nicht zum Instanziieren der Ansicht im Code verwendet werden, daher sollte sie selbst keine zusätzliche Logik haben. Es sollte nur den
awakeAfter
Haken enthalten .⚠️ Der einzige wesentliche Nachteil hierbei ist, dass wenn Sie im Storyboard Einschränkungen für Breite, Höhe oder Seitenverhältnis definieren, die sich nicht auf eine andere Ansicht beziehen, diese manuell kopiert werden müssen. Einschränkungen, die zwei Ansichten betreffen, werden auf dem nächsten gemeinsamen Vorfahren installiert, und Ansichten werden von innen nach außen aus dem Storyboard geweckt. Wenn diese Einschränkungen in der Übersicht hydratisiert sind, ist der Austausch bereits erfolgt. Einschränkungen, die nur die betreffende Ansicht betreffen, werden direkt in dieser Ansicht installiert und werden daher beim Auslagern verworfen, sofern sie nicht kopiert werden.
Beachten Sie, dass hier Einschränkungen in der Ansicht im Storyboard installiert werden, die in die neu instanziierte Ansicht kopiert werden , für die möglicherweise bereits eigene Einschränkungen in der NIB-Datei definiert sind. Diese sind nicht betroffen.
instantiateViewFromNib
ist eine typsichere Erweiterung vonUIView
. Es werden lediglich die Objekte der NIB durchlaufen, bis eines gefunden wird, das dem Typ entspricht. Beachten Sie, dass der generische Typ der Rückgabewert ist, daher muss der Typ am Aufrufstandort angegeben werden.quelle
instantiateViewFromNib
nichts zurückgibt. IMO ist keine große Sache, die Unterklasse ist nur eine Erfindung, um sich in das Storyboard einzuhängen. Der gesamte Code sollte sich in der ursprünglichen Klasse befinden.MyCustomView
. In meinem xib fehlte standardmäßig die linke innere Seitenleiste. Zum Einschalten befindet sich neben der Merkmalssteuerung "Anzeigen als: iPhone 7" unten / links eine Schaltfläche.Die derzeit beste Lösung besteht darin, nur einen benutzerdefinierten Ansichts-Controller zu verwenden, dessen Ansicht in einer XIB definiert ist, und einfach die Eigenschaft "Ansicht" zu löschen, die Xcode im Storyboard erstellt, wenn der Ansichts-Controller hinzugefügt wird (vergessen Sie nicht, den Namen von festzulegen die benutzerdefinierte Klasse allerdings).
Dadurch sucht die Laufzeit automatisch nach der xib und lädt sie. Sie können diesen Trick für jede Art von Containeransicht oder Inhaltsansicht verwenden.
quelle
Ich denke über die
alternative
für die VerwendungXIB views
zu verwendenView Controller
in getrennter Storyboard .Dann im Hauptstoryboard anstelle von benutzerdefinierter Ansicht Verwendung
container view
mitEmbed Segue
und habenStoryboardReference
auf diese benutzerdefinierten Ansicht Controller die Ansicht sollte innerhalb anderer Ansicht in Hauptstoryboard platziert werden.Anschließend können wir die Delegierung und Kommunikation zwischen diesem eingebetteten ViewController und dem Hauptansichts-Controller einrichten, indem wir uns auf den Übergang vorbereiten . Dieser Ansatz unterscheidet sich von der Anzeige von UIView, kann jedoch viel einfacher und effizienter (aus Programmiersicht) verwendet werden, um dasselbe Ziel zu erreichen, dh eine wiederverwendbare benutzerdefinierte Ansicht, die im Haupt-Storyboard sichtbar ist
Der zusätzliche Vorteil besteht darin, dass Sie Ihre Logik in der CustomViewController-Klasse implementieren und dort die gesamte Delegierung und Ansichtsvorbereitung einrichten können, ohne separate (im Projekt schwerer zu findende) Controller-Klassen zu erstellen und ohne mithilfe von Component Boilerplate-Code im Haupt-UIViewController zu platzieren. Ich denke, das ist gut für wiederverwendbare Komponenten, z. Music Player-Komponente (Widget-ähnlich), die in andere Ansichten eingebettet werden kann.
quelle
Obwohl die beliebtesten Antworten gut funktionieren, sind sie konzeptionell falsch. Sie alle werden
File's owner
als Verbindung zwischen den Outlets der Klasse und den UI-Komponenten verwendet.File's owner
soll nur für Objekte der obersten Ebene verwendet werden, nicht fürUIView
s. Lesen Sie das Apple-Entwicklerdokument . UIView alsFile's owner
führt zu diesen unerwünschten Konsequenzen.contentView
wo Sie verwenden sollenself
. Es ist nicht nur hässlich, sondern auch strukturell falsch, da die Zwischenansicht verhindert, dass die Datenstruktur die UI-Struktur vermittelt. Es ist wie das Gegenteil von deklarativer Benutzeroberfläche.Es gibt eine elegante Möglichkeit, dies ohne Verwendung zu tun
File's owner
. Bitte überprüfen Sie diesen Blog-Beitrag . Es erklärt, wie man es richtig macht.quelle
Hier ist die Antwort, die Sie sich schon immer gewünscht haben. Sie können einfach Ihre
CustomView
Klasse erstellen und die Master-Instanz davon in einer XIB mit allen Unteransichten und Ausgängen haben. Anschließend können Sie diese Klasse auf alle Instanzen in Ihren Storyboards oder anderen xibs anwenden.Sie müssen nicht mit dem Eigentümer der Datei herumspielen oder Outlets mit einem Proxy verbinden oder die xib auf besondere Weise ändern oder eine Instanz Ihrer benutzerdefinierten Ansicht als Unteransicht von sich selbst hinzufügen.
Mach das einfach:
UIView
nachNibView
(oder vonUITableViewCell
nachNibTableViewCell
)Das ist es!
Es funktioniert sogar mit IBDesignable, um Ihre benutzerdefinierte Ansicht (einschließlich der Unteransichten von xib) zur Entwurfszeit im Storyboard zu referenzieren.
Weitere Informationen finden Sie hier: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Das Open Source BFWControls-Framework erhalten Sie hier: https://github.com/BareFeetWare/BFWControls
Und hier ist ein einfacher Auszug des
NibReplaceable
Codes, der ihn antreibt, falls Sie neugierig sind: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4Tom 👣
quelle
Diese Lösung kann auch verwendet werden, wenn Ihre Klasse nicht denselben Namen wie die XIB hat. Wenn Sie beispielsweise eine Basisansicht-Controller-Klasse controllerA mit dem XIB-Namen controllerA.xib haben und diese mit controllerB unterklassifiziert haben und eine Instanz von controllerB in einem Storyboard erstellen möchten, können Sie:
* *
quelle
Lösung für Objective-C gemäß den in der Antwort von Ben Patch beschriebenen Schritten .
Verwenden Sie die Erweiterung für UIView:
Erstellen von Dateien
MyView.h
,MyView.m
undMyView.xib
.Bereiten Sie zuerst Ihre Antwort vor ,
MyView.xib
wie in der Antwort von Ben Patch angegeben, und legen Sie die KlasseMyView
für den Eigentümer der Datei fest, anstatt die Hauptansicht in diesem XIB.MyView.h
::MyView.m
::Und später erstellen Sie einfach Ihre Ansicht programmgesteuert:
Warnung! Die Vorschau dieser Ansicht wird im Storyboard nicht angezeigt, wenn Sie die WatchKit-Erweiterung aufgrund dieses Fehlers in Xcode> = 9.2 verwenden: https://forums.developer.apple.com/thread/95616
quelle