Ich versuche, etwas etwas Aufwändiges zu tun, aber etwas, das möglich sein sollte. Hier ist also eine Herausforderung für alle Experten da draußen (dieses Forum ist ein Rudel von vielen von euch :)).
Ich erstelle eine Fragebogen- "Komponente", die ich auf ein NavigationContoller
(mein QuestionManagerViewController
) laden möchte. Die "Komponente" ist eine "leere" UIViewController
, die je nach zu beantwortender Frage unterschiedliche Ansichten laden kann.
So mache ich es:
UIView
, in der einige IBOutlets
definiert sind.Question1View.xib
(HIER IS WHERE MY PROBLEM PROBABLY IS). Ich habe sowohl die UIViewController
als auch die UIView
als Klasse festgelegt Question1View.Ich überschreibe das initWithNib
meines QuestionManagerViewController
so:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
// Custom initialization
}
return self;
}
Wenn ich den Code ausführe, wird folgende Fehlermeldung angezeigt:
2009-05-14 15: 05: 37.152 iMobiDines [17148: 20b] *** Beenden der App aufgrund einer nicht erfassten Ausnahme '
NSInternalInconsistencyException
', Grund: '-[UIViewController _loadViewFromNibNamed:bundle:]
hat die Schreibfeder "Question1View" geladen, aber der View-Ausgang wurde nicht festgelegt. '
Ich bin mir sicher, dass es eine Möglichkeit gibt, die Ansicht mithilfe der NIB-Datei zu laden, ohne eine viewController-Klasse erstellen zu müssen.
Es gibt auch eine einfachere Möglichkeit, auf die Ansicht zuzugreifen, anstatt die Spitze als Array zu behandeln.
1) Erstellen Sie eine benutzerdefinierte View-Unterklasse mit allen Outlets, auf die Sie später zugreifen möchten. --Meine Sicht
2) Erstellen Sie in dem UIViewController, den Sie laden und verarbeiten möchten, eine IBOutlet-Eigenschaft, die beispielsweise die Ansicht der geladenen Nib enthält
in MyViewController (eine UIViewController-Unterklasse)
@property (nonatomic, retain) IBOutlet UIView *myViewFromNib;
(Vergessen Sie nicht, es zu synthetisieren und in Ihrer .m-Datei freizugeben.)
) öffne deine Nib (wir nennen sie 'myViewNib.xib') in IB, setze den Besitzer deiner Datei auf MyViewController
4) Verbinden Sie nun den Besitzer-Ausgang myViewFromNib Ihrer Datei mit der Hauptansicht in der Nib.
5) Schreiben Sie nun in MyViewController die folgende Zeile:
[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];
Sobald Sie dies tun, erhalten Sie durch Aufrufen Ihrer Eigenschaft "self.myViewFromNib" Zugriff auf die Ansicht von Ihrer Schreibfeder aus!
Danke euch allen. Ich habe einen Weg gefunden, um das zu tun, was ich wollte.
UIView
mit den IBOutlet
s, die Sie benötigen.UIViewController
(keine benutzerdefinierte Unterklasse, sondern die "echte"). Die Ansicht des Dateieigentümers ist mit der Hauptansicht verbunden und seine Klasse wird als die aus Schritt 1) deklariert.IBOutlet
s.Das DynamicViewController
kann seine Logik ausführen, um zu entscheiden, welche Ansicht/xib geladen werden soll. Sobald es die Entscheidung getroffen hat, schreiben Sie in die loadView
Methode so etwas:
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
owner:self
options:nil];
QPickOneView* myView = [ nibViews objectAtIndex: 1];
myView.question = question;
Das ist es!
Die loadNibNamed
-Methode des Hauptpakets sorgt dafür, dass die Ansicht initialisiert und die Verbindungen hergestellt werden.
Jetzt kann der ViewController eine Ansicht oder eine andere anzeigen, abhängig von den Daten im Speicher, und der "übergeordnete" Bildschirm muss sich nicht mit dieser Logik befassen.
Ich bin nicht sicher, worüber einige der Antworten sprechen, aber ich muss diese Antwort hier einfügen, wenn ich das nächste Mal in Google suche. Suchbegriffe: "Wie lade ich eine UIView von einer Nib?" Oder "Wie lade ich eine UIView von einem NSBundle?"
Hier ist der Code fast 100% direkt aus dem Buch Apress Beginning iPhone (Seite 247, "Verwenden der neuen Tabellenzelle"):
- (void)viewDidLoad {
[super viewDidLoad];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
owner:self options:nil];
Blah *blah;
for (id object in bundle) {
if ([object isKindOfClass:[Blah class]]) {
blah = (Blah *)object;
break;
}
}
assert(blah != nil && "blah can't be nil");
[self.view addSubview: blah];
}
Dies setzt voraus, dass Sie eine UIView
-Unterklasse mit dem Namen Blah
haben, eine Schreibfeder mit dem Namen Blah
, die eine UIView
-Klasse enthält, deren Klasse auf Blah
festgelegt ist. .
#import "NSObject+LoadFromNib.h"
@implementation NSObject (LoadFromNib)
+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:classToLoad]) {
return object;
}
}
return nil;
}
@end
extension UIView {
class func loadFromNib<T>(withName nibName: String) -> T? {
let nib = UINib.init(nibName: nibName, bundle: nil)
let nibObjects = nib.instantiate(withOwner: nil, options: nil)
for object in nibObjects {
if let result = object as? T {
return result
}
}
return nil
}
}
Und ein Beispiel im Einsatz:
class SomeView: UIView {
class func loadFromNib() -> SomeView? {
return self.loadFromNib(withName: "SomeView")
}
}
Für alle, die mehr als eine Instanz der benutzerdefinierten Ansicht verwalten müssen, also eine Outlet-Sammlung, habe ich die Antworten @Gonso, @AVeryDev und @Olie folgendermaßen zusammengeführt und angepasst:
Erstelle ein benutzerdefiniertes MyView : UIView
und setze es als "Custom Class" des Stamms UIView
im gewünschten XIB ;
Erstellen Sie alle Ausgänge, die Sie in MyView
benötigen (tun Sie dies jetzt, da der IB nach Punkt 3 vorschlägt, die Ausgänge mit UIViewController
und nicht mit der benutzerdefinierten Ansicht als zu verbinden wir wollen);
Setzen Sie Ihr UIViewController
als "Eigentümer der Datei" der benutzerdefinierten Ansicht XIB ;
Fügen Sie im UIViewController
für jede Instanz von UIViews
, die Sie möchten, ein neues MyView
hinzu und verbinden Sie sie mit UIViewController
, um ein Outlet Collection zu erstellen =: Diese Ansichten dienen als "Wrapper" -Ansichten für die benutzerdefinierten Ansichtsinstanzen.
Fügen Sie schließlich im viewDidLoad
Ihres UIViewController
die folgenden Zeilen hinzu:
NSArray *bundleObjects; MyView *currView; NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count]; for (UIView *currWrapperView in myWrapperViews) { bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil]; for (id object in bundleObjects) { if ([object isKindOfClass:[MyView class]]){ currView = (MyView *)object; break; } } [currView.myLabel setText:@"myText"]; [currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal]; //... [currWrapperView addSubview:currView]; [myViews addObject:currView]; } //self.myViews = myViews; if need to access them later..
Ich würde UINib verwenden, um eine benutzerdefinierte UIView zu instanziieren, die wiederverwendet werden soll
UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];
In diesem Fall sind folgende Dateien erforderlich: MyCustomView.xib, MyCustomViewClass.h und MyCustomViewClass.m Beachten Sie, dass [UINib instantiateWithOwner]
gibt ein Array zurück, daher sollten Sie das Element verwenden, das die UIView widerspiegelt, die Sie wiederverwenden möchten. In diesem Fall ist es das erste Element.
Sie sollten die Klasse Ihres Ansichtscontrollers in Interface Builder nicht auf eine Unterklasse von UIView
festlegen. Das ist auf jeden Fall zumindest ein Teil Ihres Problems. Lassen Sie dies entweder als UIViewController
, eine Unterklasse davon oder eine andere benutzerdefinierte Klasse, die Sie haben.
Bezüglich des Ladens nur einer Ansicht aus einer xib ging ich davon aus, dass Sie hatten eine Art Ansichtscontroller haben müssen (auch wenn er UIViewController
nicht erweitert, was möglicherweise der Fall ist) zu schwergewichtig für Ihre Anforderungen), legen Sie als Eigentümer der Datei im Interface Builder fest, wenn Sie sie zum Definieren Ihrer Schnittstelle verwenden möchten. Ich habe ein wenig nachgeforscht, um dies zu bestätigen. Dies liegt daran, dass es sonst weder eine Möglichkeit gibt, auf eines der Schnittstellenelemente in UIView
zuzugreifen, noch eine Möglichkeit, eigene Methoden im Code durch Ereignisse auszulösen.
Wenn Sie UIViewController
als Eigentümer Ihrer Datei für Ihre Ansichten verwenden, können Sie einfach initWithNibName:bundle:
, um es zu laden und das View-Controller-Objekt zurückzugewinnen. Stellen Sie in IB sicher, dass Sie den Ausgang view
auf die Ansicht mit Ihrer Schnittstelle in der xib setzen. Wenn Sie einen anderen Objekttyp als Eigentümer Ihrer Datei verwenden, müssen Sie NSBundle
's loadNibNamed:owner:options:
Methode zum Laden der NIB, wobei eine Instanz des Dateieigentümers an die Methode übergeben wird. Alle seine Eigenschaften werden entsprechend den von Ihnen in IB definierten Ausgängen richtig eingestellt.
Sie können auch den initWithNibName von UIViewController anstelle von loadNibNamed verwenden. Es ist einfacher, finde ich.
UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release]; // release the VC
Jetzt müssen Sie nur noch MySubView.xib und MySubView.h/m erstellen. Setzen Sie in MySubView.xib die Owner-Klasse der Datei auf UIViewController und die View-Klasse auf MySubView.
Sie können Position und Größe des subview
mithilfe der übergeordneten xib-Datei festlegen.
Dies ist etwas, das einfacher sein sollte. Am Ende habe ich UIViewController
erweitert und einen loadNib:inPlaceholder:
- Selektor hinzugefügt. Jetzt kann ich sagen
self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];
Hier ist der Code für die Kategorie (es macht das gleiche Rigamarole wie von Gonso beschrieben):
@interface UIViewController (nibSubviews)
- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;
@end
@implementation UIViewController (nibSubviews)
- (UIView *)viewFromNib:(NSString *)nibName
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
for (id view in xib) { // have to iterate; index varies
if ([view isKindOfClass:[UIView class]]) return view;
}
return nil;
}
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
UIView *nibView = [self viewFromNib:nibName];
[nibView setFrame:placeholder.frame];
[self.view insertSubview:nibView aboveSubview:placeholder];
[placeholder removeFromSuperview];
return nibView;
}
@end
Dies ist eine großartige Frage (+1) und die Antworten waren fast hilfreich;) Tut mir leid, aber ich hatte eine verdammte Zeit damit zu kämpfen, obwohl sowohl Gonso als auch AVeryDev gute Hinweise gaben. Hoffentlich hilft diese Antwort anderen.
MyVC
ist der View-Controller, der all diese Informationen enthält.
MySubview
ist die Ansicht, die wir aus einer xib laden möchten
MySubView
, die die richtige Größe und Form hat und an der gewünschten Stelle positioniert ist.In MyVC.h haben
IBOutlet MySubview *mySubView
// ...
@property (nonatomic, retain) MySubview *mySubview;
In MyVC.m wird @synthesize mySubView;
und vergessen Sie nicht, es in dealloc
freizugeben.
UIView *view
(ist möglicherweise unnötig, hat aber für mich funktioniert.) Synthetisieren und veröffentlichen Sie es in .mMySubview
und verknüpfen Sie die Ansichtseigenschaft mit Ihrer Ansicht.IBOutlet
Zurück in MyVC.m, habe
NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
MySubview *msView = [xibviews objectAtIndex: 0];
msView.frame = mySubview.frame;
UIView *oldView = mySubview;
// Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
[[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
self.mySubview = msView;
[oldCBView removeFromSuperview];
Das Knifflige für mich war: Die Hinweise in den anderen Antworten luden meine Ansicht aus dem xib, ersetzten aber NICHT die Ansicht in MyVC (duh!) - ich musste das selbst austauschen.
Um auf die Methoden von mySubview
zugreifen zu können, muss die Eigenschaft view
in der .xib-Datei auf MySubview
gesetzt sein. Andernfalls wird es als einfaches UIView
zurückgegeben.
Wenn es eine Möglichkeit gibt, mySubview
direkt von seinem eigenen xib zu laden, würde das rocken, aber das hat mich dahin gebracht, wo ich sein musste.
Ich wollte auch etwas Ähnliches machen, das habe ich gefunden: (SDK 3.1.3)
Ich habe einen View-Controller A (der sich im Besitz eines Nav-Controllers befindet), der VC B auf Knopfdruck lädt:
In AViewController.m
BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];
Jetzt hat VC B sein Interface von Bnib, aber wenn eine Taste gedrückt wird, möchte ich in einen 'Bearbeitungsmodus' gehen, der eine von einer anderen Feder getrennte Benutzeroberfläche hat, aber ich nicht Ich möchte eine neue VC für den Bearbeitungsmodus. Ich möchte, dass die neue Feder mit meiner vorhandenen B-VC verknüpft wird.
Also, in BViewController.m (in Knopfdruckmethode)
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];
Drücken Sie dann auf eine andere Taste (um den Bearbeitungsmodus zu verlassen):
[editView removeFromSuperview];
und ich bin zurück zu meinem ursprünglichen Bnib.
Dies funktioniert gut, aber beachte, dass meine EditMode.nib nur 1 oberstes Objekt enthält, ein UIView-Objekt. Es spielt keine Rolle, ob der Eigentümer der Datei in dieser Nib als BViewController oder als Standard-NSObject festgelegt ist, ABER stellen Sie sicher, dass für den View-Ausgang im Eigentümer der Datei KEIN Wert festgelegt ist. Wenn dies der Fall ist, kommt es zu einem Absturz von exc_bad_access und xcode lädt 6677 Stack-Frames mit einer internen UIView-Methode, die wiederholt aufgerufen wird. Sieht also wie eine Endlosschleife aus. (The View Outlet IS in meinem ursprünglichen Bnib jedoch eingestellt)
Hoffe das hilft.
In Swift
Eigentlich bestand meine Lösung für dieses Problem darin, die Ansicht in einem viewDidLoad in meinem CustonViewController zu laden, in dem ich die Ansicht wie folgt verwenden wollte:
myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView
Laden Sie die Ansicht nicht in eine loadView()
Methode! Die loadView-Methode dient zum Laden der Ansicht für Ihren benutzerdefinierten ViewController.
Ich habe eine Kategorie erstellt, die mir gefällt:
UIView+NibInitializer.h
#import <UIKit/UIKit.h>
@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end
UIView+NibInitializer.m
#import "UIView+NibInitializer.h"
@implementation UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
if (!nibNameOrNil) {
nibNameOrNil = NSStringFromClass([self class]);
}
NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
owner:self
options:nil];
for (id view in viewsInNib) {
if ([view isKindOfClass:[self class]]) {
self = view;
break;
}
}
return self;
}
@end
Rufen Sie dann wie folgt auf:
MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];
Verwenden Sie einen Namen für die Schreibfeder, wenn Ihre Schreibfeder einen anderen Namen als den Namen Ihrer Klasse hat.
Um es in Ihren Unterklassen für zusätzliches Verhalten zu überschreiben, könnte es folgendermaßen aussehen:
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
self = [super initWithNibNamed:nibNameOrNil];
if (self) {
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
}
return self;
}
Für Swift Benutzer mit definierbarer Option:
UIView
-Unterklasse und eine xib-Datei , die wir nach unserem eigenen Klassennamen benennen: in unserem Fall MemeView. Denken Sie daran, die Meme View-Klasse vor der Klassendeklaration als mit dem Attribut @IBDesignable
Konfigurierbar zu definierenDenken Sie daran, den Dateieigentümer in der xib mit unserer benutzerdefinierten Unterklasse UIView
im Bereich "Indetity Inspector" festzulegen
In der xib-Datei können wir nun unsere Schnittstelle erstellen, Einschränkungen vornehmen, Outlets, Aktionen usw. erstellen.
Wir müssen einige Methoden für unsere benutzerdefinierte Klasse implementieren, um die xib nach der Initialisierung zu öffnen
class XibbedView: UIView {
weak var nibView: UIView!
override convenience init(frame: CGRect) {
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
self.init(nibName: nibName)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
init(nibName: String) {
super.init(frame: CGRectZero)
let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
let nib = loadNib(nibName)
nib.frame = bounds
nib.translatesAutoresizingMaskIntoConstraints = false
addSubview(nib)
nibView = nib
setUpConstraints()
}
func setUpConstraints() {
["V","H"].forEach { (quote) -> () in
let format = String(format:"\(quote):|[nibView]|")
addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
}
}
func loadNib(name: String) -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: name, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
}
In unserer benutzerdefinierten Klasse können wir auch einige überprüfbare Eigenschaften definieren, um die vollständige Kontrolle über sie vom Interface Builder aus zu haben
@IBDesignable class MemeView: XibbedView {
@IBInspectable var memeImage: UIImage = UIImage() {
didSet {
imageView.image = memeImage
}
}
@IBInspectable var textColor: UIColor = UIColor.whiteColor() {
didSet {
label.textColor = textColor
}
}
@IBInspectable var text: String = "" {
didSet {
label.text = text
}
}
@IBInspectable var roundedCorners: Bool = false {
didSet {
if roundedCorners {
layer.cornerRadius = 20.0
clipsToBounds = true
}
else {
layer.cornerRadius = 0.0
clipsToBounds = false
}
}
}
@IBOutlet weak var label: UILabel!
@IBOutlet weak var imageView: UIImageView!
}
Wenn wir weitere Informationen hinzufügen müssen, während die Ansicht in einem Storyboard oder einer anderen xib angezeigt wird, um prepareForInterfaceBuilder()
zu implementieren, wird diese Methode nur beim Öffnen der Datei im Interface Builder ausgeführt. Wenn Sie alles getan haben, was ich geschrieben habe, aber nichts funktioniert, können Sie eine einzelne Ansicht durch Hinzufügen von Haltepunkten in ihrer Implementierung debuggen.
Hier ist die Ansichtshierarchie.
Hoffe, dies hilft, eine vollständige Probe kann heruntergeladen werden hier
Ich fand diesen Blogeintrag von Aaron Hillegass (Autor, Dozent, Cocoa Ninja) sehr aufschlussreich. Selbst wenn Sie seine modifizierte Vorgehensweise zum Laden von NIB-Dateien über einen festgelegten Initialisierer nicht übernehmen, erhalten Sie wahrscheinlich zumindest ein besseres Verständnis des laufenden Prozesses. Ich habe diese Methode in letzter Zeit mit großem Erfolg angewendet!
Ich hatte Grund, dasselbe zu tun (programmgesteuertes Laden einer Ansicht aus einer XIB-Datei), aber ich musste dies vollständig aus dem Kontext einer Unterklasse einer Unterklasse eines UIView
heraus tun (dh ohne den Ansichtscontroller einzubeziehen in irgendeiner Weise). Dazu habe ich diese Utility-Methode erstellt:
+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
owner:myself options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[myself class]]) {
return object;
}
}
return nil;
}
Dann rufe ich es aus der initWithFrame
-Methode meiner Unterklasse folgendermaßen auf:
- (id)initWithFrame:(CGRect)frame {
self = [Utilities initWithNibName:@"XIB1" withSelf:self];
if (self) {
// Initialization code.
}
return self;
}
Geschrieben für allgemeines Interesse; Wenn jemand Probleme sieht, ohne es auf diese Weise zu tun, lass es mich wissen.
Hier ist eine Möglichkeit, dies in Swift (schreibt gerade Swift 2.0 in XCode 7 Beta 5)) zu tun.
Erstellen Sie aus Ihrer UIView
-Unterklasse, die Sie im Interface Builder als "Benutzerdefinierte Klasse" festgelegt haben, eine Methode wie die folgende (meine Unterklasse heißt RecordingFooterView):
class func loadFromNib() -> RecordingFooterView? {
let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
let nibObjects = nib.instantiateWithOwner(nil, options: nil)
if nibObjects.count > 0 {
let topObject = nibObjects[0]
return topObject as? RecordingFooterView
}
return nil
}
Dann können Sie es einfach so nennen:
let recordingFooterView = RecordingFooterView.loadFromNib()
Die vorherige Antwort berücksichtigt keine Änderung der NIB (XIB) -Struktur zwischen 2.0 und 2.1 des iPhone SDK. Benutzerinhalte beginnen jetzt bei Index 0 anstelle von 1.
Sie können das 2.1-Makro verwenden, das für alle Versionen ab 2.1 gültig ist (das sind zwei Unterstriche vor IPHONE):
// Cited from previous example
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
int startIndex;
#ifdef __IPHONE_2_1
startIndex = 0;
#else
startIndex = 1;
#endif
QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
myView.question = question;
Für die meisten unserer Anwendungen verwenden wir eine ähnliche Technik.
Barney
Keine der Antworten erklärt, wie das eigenständige XIB erstellt wird, das die Wurzel dieser Frage ist. Es gibt kein Xcode 4
Option zu "Neue XIB-Datei erstellen".
Um dies zu tun
1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format
Dies mag einfach erscheinen, aber es kann Ihnen einige Minuten ersparen, danach zu stöbern, da das Wort "XIB" nirgendwo erscheint.
@AVeryDev
6) So hängen Sie die geladene Ansicht an die Ansicht Ihres View Controllers an:
[self.view addSubview:myViewFromNib];
Vermutlich muss es aus der Ansicht entfernt werden, um Speicherverluste zu vermeiden.
Zur Verdeutlichung: Der View-Controller verfügt über mehrere IBOutlets, von denen einige (wie üblich) mit Elementen in der ursprünglichen NIB-Datei und einige mit Elementen in der geladenen NIB verbunden sind. Beide Schreibfedern haben dieselbe Besitzerklasse. Die geladene Ansicht überlagert die ursprüngliche Ansicht.
Tipp: Setzen Sie die Deckkraft der Hauptansicht in der geladenen Schreibfeder auf Null, damit die Elemente der ursprünglichen Schreibfeder nicht verdeckt werden.
Kürzeste Version:
RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
Nachdem ich viele Stunden verbracht hatte, schmiedete ich folgende Lösung aus. Führen Sie die folgenden Schritte aus, um ein benutzerdefiniertes UIView
zu erstellen.
1) Erstellen Sie eine Klasse myCustomView , die von UIView geerbt wurde.
2) Erstellen Sie . Xib mit dem Namen myCustomView .
3) Ändern Sie die Klasse von UIView in Ihrer .xib-Datei, und weisen Sie myCustomView zu. Klasse da.
5) Laden Sie .xib in myCustomView * customView . Verwenden Sie folgenden Beispielcode.
myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];
Hinweis: Für diejenigen, die noch Probleme haben, werde ich den Link des Beispielprojekts mit myCustomView bereitstellen.
Ich habe die Konvention, xibs mit Ansichten zu benennen, die mit der Ansicht identisch sind. Das Gleiche wie bei einem View Controller. Dann muss ich keine Klassennamen im Code ausschreiben. Ich lade eine UIView aus einer gleichnamigen NIB-Datei.
Beispiel für eine Klasse namens MyView.
Erstellen Sie in Ihrem Code ein neues MyView wie folgt:
MyView * myView = [MyView nib_viewFromNibWithOwner: Eigentümer];
Hier ist die Kategorie dafür:
@implementation UIView (nib)
+ (id) nib_viewFromNib {
return [self nib_viewFromNibWithOwner:nil];
}
+ (id) nib_viewFromNibWithOwner:(id)owner {
NSString *className = NSStringFromClass([self class]);
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
UIView *view = nil;
for(UIView *v in nib) {
if ([v isKindOfClass:[self class]]) {
view = v;
break;
}
}
assert(view != nil && "View for class not found in nib file");
[view nib_viewDidLoad];
return view;
}
// override this to do custom setup
-(void)nib_viewDidLoad {
}
Ich verbinde dann Schaltflächen mit Aktionen des Controllers, den ich verwende, und setze Dinge mit den Ausgängen in meiner Unterklasse für benutzerdefinierte Ansichten auf Etiketten.
So laden Sie eine Ansicht programmgesteuert von einer Nib/Xib in Swift 4:
// Load a view from a Nib given a placeholder view subclass
// Placeholder is an instance of the view to load. Placeholder is discarded.
// If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {
let nib = loadNib(givenNibName, placeholder: placeholderView)
return instantiateView(fromNib: nib, placeholder: placeholderView)
}
// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
//1. Load and unarchive nib file
let nibName = givenNibName ?? String(describing: type(of: placeholderView))
let nib = UINib(nibName: nibName, bundle: Bundle.main)
return nib
}
// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
//1. Get top level objects
let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)
//2. Have at least one top level object
guard let firstObject = topLevelObjects.first else {
fatalError("\(#function): no top level objects in nib")
}
//3. Return instantiated view, placeholderView is not used
let instantiatedView = firstObject as! T
return instantiatedView
}