Verwenden Sie eine uiview xib im Storyboard erneut


81

Normalerweise erstelle und gestalte ich meine Uiviews gerne im Interface Builder. Manchmal muss ich eine einzelne Ansicht in einer xib erstellen, die in mehreren View-Controllern in einem Storyboard wiederverwendet werden kann.

Antworten:


132

Wiederverwenden und rendern Sie eine xib in einem Storyboard.

Getestet mit Swift 2.2 & Xcode 7.3.1

1 ---- Erstellen Sie eine neue UIView mit dem Namen 'DesignableXibView'

  • Datei> Neu> Datei> Quelle> Cocoa Touch-Klasse> UIView

2 ---- Erstellen Sie eine passende xib-Datei mit dem Namen 'DesignableXibView'.

  • Datei> Neu> Datei> Benutzeroberfläche> Ansicht

3 ---- Legen Sie den Dateieigentümer des xib fest

  1. Wähle die xib aus
  2. Wählen Sie den Eigentümer der Datei aus
  3. Setzen Sie die benutzerdefinierte Klasse im Identitätsinspektor auf 'DesignableXibView'.

Festlegen des Besitzers eines Xib auf eine benutzerdefinierte Klasse

  • Hinweis: Legen Sie die benutzerdefinierte Klasse der Ansicht in der xib nicht fest. Nur der Dateibesitzer!

4 ---- Implementierung von DesignableXibView

//  DesignableXibView.swift

import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView?

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView!.frame = bounds

        // Make the view stretch with containing view
        contentView!.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]

        // Adding custom subview on top of our view (over any custom drawing > see note below)
        addSubview(contentView!)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = NSBundle(forClass: self.dynamicType)
        let nib = UINib(nibName: String(self.dynamicType), bundle: bundle)
        let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView

        return view
    }

}

5 ---- Testen Sie Ihre wiederverwendbare Ansicht in einem Storyboard

  1. Öffne dein Storyboard
  2. Fügen Sie eine Ansicht hinzu
  3. Legen Sie die benutzerdefinierte Klasse dieser Ansicht fest
  4. Warte eine Sekunde ... BOOM !!

Xib-Rendering in einem Storyboard View Controller


1
Vielen Dank! Möglicherweise möchten Sie ein Beispiel mit Einschränkungen für Swift hinzufügen, wie Sie es in Ihrer Obj-C-Antwort getan haben (mit und ohne VFL).
Evan R

3
Ich habe meine eigene Frage beantwortet. Es sieht so aus, als würde ich die Steckdosen einrichten, bevor ich diese SO-Artikel lese, in denen es darum geht, den Dateibesitzer und nicht die Ansicht festzulegen. Es funktionierte, nachdem ich alle Steckdosen getrennt, sichergestellt hatte, dass der Dateibesitzer korrekt war, und dann alle Steckdosen erneut hinzugefügt hatte.
SuperDuperTango

4
Was ist der Unterschied zwischen dem Festlegen des Dateibesitzers und der benutzerdefinierten Klasse?
Paul Brewczynski

9
Ich sehe die Vorschau auch nicht im Interface Builder. Xcode 9
Jaap Weijland

3
Der Code funktioniert beim Ausführen, aber in meinem Xcode 9 wird unter der Modulzeile "Designable Build fehlgeschlagen"
angezeigt

76

NEU! aktualisierte Antwort mit der Fähigkeit, direkt im Storyboard zu rendern (und schnell!)

Funktioniert in Xcode 6.3.1

Erstellen Sie eine neue UIView mit dem Namen "ReuseableView".

  • Datei> Neu> Datei> Quelle> Cocoa Touch-Klasse> UIView

Erstellen Sie eine passende xib-Datei mit dem Namen 'ReuseableView'.

  • Datei> Neu> Datei> Benutzeroberfläche> Ansicht

Legen Sie den Dateibesitzer der xib fest

  1. Wähle die xib aus
  2. Wählen Sie den Eigentümer der Datei aus
  3. Setzen Sie die benutzerdefinierte Klasse im Identitätsinspektor auf 'ReusableView'. Geben Sie hier die Bildbeschreibung ein

    • Hinweis: Legen Sie die benutzerdefinierte Klasse der Ansicht in der xib nicht fest. Nur der Dateibesitzer!

Stellen Sie einen Ausgang von der Ansicht in der Datei ReuseableView.xib zu Ihrer Oberfläche ReuseableView.h her

  1. Öffnen Sie den Assistenten-Editor
  2. Strg + Ziehen aus der Ansicht auf Ihre Benutzeroberfläche

Geben Sie hier die Bildbeschreibung ein

Fügen Sie die initWithCoder-Implementierung zum Laden der Ansicht hinzu und fügen Sie sie als Unteransicht hinzu.

- (id)initWithCoder:(NSCoder *)aDecoder{
    self = [super initWithCoder:aDecoder];
    if (self) {

        // 1. load the interface
        [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
        // 2. add as subview
        [self addSubview:self.view];
        // 3. allow for autolayout
        self.view.translatesAutoresizingMaskIntoConstraints = NO;
        // 4. add constraints to span entire view
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:@{@"view":self.view}]];

    }
    return self;
}

Geben Sie hier die Bildbeschreibung ein

Testen Sie Ihre wiederverwendbare Ansicht in einem Storyboard

  1. Öffne dein Storyboard
  2. Fügen Sie eine Ansicht hinzu
  3. Legen Sie die benutzerdefinierte Klasse dieser Ansicht fest

Geben Sie hier die Bildbeschreibung ein

Lauf und beobachte! Geben Sie hier die Bildbeschreibung ein


@Garfbargle Hinzu kommt ... ist es möglich, die Unterelemente des xib im Storyboard anzuzeigen? In Ihrem letzten Schritt wird die hinzugefügte Ansicht wie ein graues Quadrat angezeigt. Gibt es eine Möglichkeit, dies zu beheben? Danke!
Andres C

2
@ andres.cianio Ja, aber Sie müssen einen ganz anderen Ansatz wählen. Sie müssen die Ansicht programmgesteuert erstellen und die IBDesignable-Direktive verwenden. Dies war speziell für Leute gedacht, die Ansichten visuell erstellen wollten. Mir ist keine Möglichkeit bekannt, eine Ansicht in einem xib visuell zu erstellen und in einem Storyboard rendern zu lassen. Es würde mich nicht wundern, wenn dies sehr bald möglich sein wird, wenn es nicht bereits geschehen ist.
Garfbargle

@ Garfbargle danke ... Das hat mein Leben gerettet, aber es beim Einfügen der Ansicht im SB rendern zu lassen, wäre schön gewesen;)
Andres C

2
Tolle Erklärung! Aber ich habe einen Hinweis für Sie: Verwenden Sie diese, UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)die schneller ist als die NSBundle-Version.
Blackjacx

@Garfbargle Dies scheint die Anforderung zu erfüllen, dass Sie in IB entwerfen und im Storyboard rendern können. Grundsätzlich machst du genau das, was du für das XIB gemacht @IBDesignable on the class.hast und stellst du musst es auch implementieren init(frame:)- aber ansonsten funktioniert es ziemlich gut! supereasyapps.com/blog/2014/12/15/…
wyu

53

Swift 3 & 4 Update auf die akzeptierte Antwort

1. Erstellen Sie eine neue UIView mit dem Namen 'DesignableXibView'.

  • Datei> Neu> Datei> Quelle> Cocoa Touch-Klasse> UIView

2. Erstellen Sie eine passende xib-Datei mit dem Namen 'DesignableXibView'.

  • Datei> Neu> Datei> Benutzeroberfläche> Ansicht

3. Legen Sie den Dateieigentümer des xib fest

Wählen Sie "DesignableXibView.xib"> "Dateibesitzer"> setzen Sie "Benutzerdefinierte Klasse" im Identitätsinspektor auf "DesignableXibView".

  • Hinweis: Legen Sie die benutzerdefinierte Klasse der Ansicht in der xib nicht fest. Nur der Dateibesitzer!

4. Implementierung von DesignableXibView

    import UIKit

@IBDesignable

class DesignableXibView: UIView {

    var contentView : UIView!

    override init(frame: CGRect) {
        super.init(frame: frame)
        xibSetup()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        xibSetup()
    }

    func xibSetup() {
        contentView = loadViewFromNib()

        // use bounds not frame or it'll be offset
        contentView.frame = bounds

        // Make the view stretch with containing view
        contentView.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]

        // Adding custom subview on top of our view 
        addSubview(contentView)
    }

    func loadViewFromNib() -> UIView! {

        let bundle = Bundle(for: type(of: self))
        let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
        let view = nib.instantiate(withOwner: self, options: nil).first as! UIView

        return view
    }
} 

5 Testen Sie Ihre wiederverwendbare Ansicht in einem Storyboard

  • Öffne dein Storyboard

  • Fügen Sie eine Ansicht hinzu

  • Legen Sie die benutzerdefinierte Klasse dieser Ansicht fest


2
Ich kann es nicht in Xcode8 / Swift3 zum Laufen bringen. Funktioniert gut, wenn die App ausgeführt wird, wird jedoch nicht im Storyboard angezeigt.
flach gedacht

Funktioniert gut für mich. Wenn Sie versuchen, Änderungen auf der xib anzuzeigen, werden diese nicht angezeigt. Fügen Sie eine Ansicht in einem anderen Controller hinzu und setzen Sie die Klasse dieser Ansicht auf DesignableXibView. Erstellen Sie das Projekt, um die Änderungen anzuzeigen.
harsch_v

1
Hat auch für mich gearbeitet. Ich musste nur das DesignableXibView zu meinem aktuellen Storyboard hinzufügen, wie in Schritt 5 des Garfbargle-Beitrags beschrieben
Tinkerbell,

Nur ich bekomme zur Laufzeit "NIB konnte nicht im Bundle geladen werden"?
Jonny

1
@ Jonny Ich denke, String(describing: type(of: self))sollte dies sicher tun
harsch_v

11

Die initWithCoder-Funktion in Swift 2, wenn jemand Probleme beim Übersetzen hat:

required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        UINib(nibName: String(self.dynamicType), bundle: NSBundle.mainBundle()).instantiateWithOwner(self, options: nil)
        self.addSubview(view)
        self.view.translatesAutoresizingMaskIntoConstraints = false
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterY , metrics: nil, views: ["view": self.view]))
        self.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: NSLayoutFormatOptions.AlignAllCenterX , metrics: nil, views: ["view": self.view]))
    }

Vielen Dank! Möglicherweise möchten Sie ein Beispiel hinzufügen, das VFL nicht verwendet, für diejenigen, die es nicht verwenden möchten.
Evan R

3

Für alle, die versuchen, die akzeptierte Antwort (von @Garfbargle) an Objective-C anzupassen

Nur das Umwandeln Swiftzu Objective-Cist nicht genug , um es funktioniert. Es fiel mir schwer, Live-Rendering in Storyboard zuzulassen.

Nach der Übersetzung des gesamten Codes ist die Ansicht gut geladen, wenn sie auf einem Gerät (oder Simulator) ausgeführt wird, aber das Live-Rendering in Storyboard funktioniert nicht. Der Grund dafür ist, dass ich verwendet habe, [NSBundle mainBundle]während Interface Builder keinen Zugriff auf mainBundle hat. Was Sie stattdessen verwenden müssen, ist [NSBundle bundleForClass:self.classForCoder]. BOOM, Live-Rendering funktioniert jetzt!

Hinweis: Wenn Sie Probleme mit dem automatischen Layout haben, deaktivieren Sie es Safe Area Layout Guidesin Xib.

Für Ihre Bequemlichkeit lasse ich meinen gesamten Code hier, so dass Sie nur kopieren / einfügen müssen (für alle Prozesse folgen Sie der ursprünglichen Antwort ):

BottomBarView.h

#import <UIKit/UIKit.h>

IB_DESIGNABLE
@interface BottomBarView : UIView

@end

BottomBarView.m

#import "BottomBarView.h"

@interface BottomBarView() {
    UIView *contentView;
}

@end


@implementation BottomBarView

-(id) initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(id) initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self xibSetup];
    }
    return self;
}

-(void) xibSetup {
    contentView = [self loadViewFromNib];

    contentView.frame = self.bounds;

    contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    [self addSubview:contentView];
}

-(UIView*) loadViewFromNib {
    NSBundle *bundle = [NSBundle bundleForClass:self.classForCoder]; //this is the important line for view to render in IB
    UINib *nib = [UINib nibWithNibName:NSStringFromClass([self class]) bundle:bundle];
    UIView *view = [nib instantiateWithOwner:self options:nil][0];

    return view;
}

@end

Sagen Sie mir, wenn Sie auf einige Probleme stoßen, aber es sollte fast sofort funktionieren :)


0

Wenn jemand interessiert ist, ist hier die Xamarin.iOS- Version des Codes Schritt 4 von @Garfbargle

public partial class CustomView : UIView
    {
        public ErrorView(IntPtr handle) : base(handle)
        {

        }

        [Export("awakeFromNib")]
        public override void AwakeFromNib()
        {
            var nibObjects = NSBundle.MainBundle.LoadNib("CustomView", this, null);
            var view = (UIView)Runtime.GetNSObject(nibObjects.ValueAt(0));
            view.Frame = Bounds;
            view.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;

            AddSubview(rootView);
        }
    }

0

Hier ist die Antwort, die Sie sich schon immer gewünscht haben. Sie können einfach Ihre CustomViewKlasse 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 Steckdosen 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:

  1. Importieren Sie das BFWControls-Framework
  2. Ändern Sie Ihre Oberklasse von UIViewnach NibView(oder von UITableViewCellnach NibTableViewCell)

Das ist es!

Es funktioniert sogar mit IBDesignable, um Ihre benutzerdefinierte Ansicht (einschließlich der Unteransichten von xib) zur Entwurfszeit im Storyboard zu rendern.

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

Tom 👣

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.