it-swarm.com.de

So laden Sie eine UIView mit einer mit Interface Builder erstellten NIB-Datei

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:

  1. Erstellen Sie das Question1View-Objekt als Unterklasse UIView, in der einige IBOutlets definiert sind.
  2. Erstellen Sie (mit Interface Builder) das Question1View.xib(HIER IS WHERE MY PROBLEM PROBABLY IS). Ich habe sowohl die UIViewController als auch die UIView als Klasse festgelegt Question1View.
  3. Ich verbinde die Ausgänge mit der Ansichtskomponente (mit IB).
  4. 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.

238
Gonso

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!

223
averydev

Danke euch allen. Ich habe einen Weg gefunden, um das zu tun, was ich wollte.

  1. Erstellen Sie Ihr UIView mit den IBOutlets, die Sie benötigen.
  2. Erstellen Sie die xib in IB, gestalten Sie sie nach Ihren Wünschen und verknüpfen Sie sie wie folgt: Der Eigentümer der Datei hat die Klasse 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.
  3. Verbinden Sie Ihre Steuerelemente mit den IBOutlets.
  4. 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.

119
Gonso

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. .


Kategorie: NSObject + LoadFromNib

#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

Schnelle Erweiterung

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")
    }
}
69
Dan Rosenstark

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:

  1. Erstelle ein benutzerdefiniertes MyView : UIView und setze es als "Custom Class" des Stamms UIView im gewünschten XIB custom class

  2. 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); custom class outlet

  3. Setzen Sie Ihr UIViewController als "Eigentümer der Datei" der benutzerdefinierten Ansicht XIB enter image description here

  4. 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. enter image description here

  5. 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..
22

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.

18
thgc

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.

11
Marc W

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.

10
Ying

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
8
skot

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

  • Erstellen Sie in MyVC.xib eine Ansicht vom Typ 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.

  • Haben Sie in MySubview.h einen Ausgang/eine Eigenschaft für UIView *view (ist möglicherweise unnötig, hat aber für mich funktioniert.) Synthetisieren und veröffentlichen Sie es in .m
  • In MySubview.xib
    • setzen Sie den Dateieigentümertyp auf MySubview und verknüpfen Sie die Ansichtseigenschaft mit Ihrer Ansicht.
    • Lege alle Bits aus und verbinde sie wie gewünscht mit den 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.

8
Olie

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.

6
Brynjar

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.

6
kalafun

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;
}
6
Logan

Für Swift Benutzer mit definierbarer Option:

  1. Erstellen Sie eine benutzerdefinierte 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 definieren
  2. Denken Sie daran, den Dateieigentümer in der xib mit unserer benutzerdefinierten Unterklasse UIView im Bereich "Indetity Inspector" festzulegen

    enter image description here

  3. In der xib-Datei können wir nun unsere Schnittstelle erstellen, Einschränkungen vornehmen, Outlets, Aktionen usw. erstellen. enter image description here

  4. 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
    }
    

    }

  5. 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!
    

}

Einige Beispiele:
enter image description hereenter image description here

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. enter image description here
Hier ist die Ansichtshierarchie. View hiearchy

Hoffe, dies hilft, eine vollständige Probe kann heruntergeladen werden hier

5
Andrea

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!

4
Meltemi

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.

3
MusiGenesis

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()

3
Johannes

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

3
Barney Mattox

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.

2
whitneyland

@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.

1
Matt

Kürzeste Version:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];
1
wzbozon

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.
enter image description here

2) Erstellen Sie . Xib mit dem Namen myCustomView .
enter image description here

3) Ändern Sie die Klasse von UIView in Ihrer .xib-Datei, und weisen Sie myCustomView zu. Klasse da.
enter image description here

4) IBOutlets erstellen
enter image description here

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.

1
Aamir

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 Interface Builder eine NIB-Datei mit dem Namen MyView.xib
  • Fügen Sie im Interface Builder eine UIView hinzu. Setzen Sie seine Klasse auf MyView. Passen Sie nach Herzenslust an, verknüpfen Sie Instanzvariablen von MyView mit Unteransichten, auf die Sie möglicherweise später zugreifen möchten.
  • 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.

0
n13

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
}
0
Eng Yew