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.
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:
Getestet mit Swift 2.2 & Xcode 7.3.1
// 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
}
}
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".
Erstellen Sie eine passende xib-Datei mit dem Namen 'ReuseableView'.
Legen Sie den Dateibesitzer der xib fest
Setzen Sie die benutzerdefinierte Klasse im Identitätsinspektor auf 'ReusableView'.
Stellen Sie einen Ausgang von der Ansicht in der Datei ReuseableView.xib zu Ihrer Oberfläche ReuseableView.h her
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;
}
Testen Sie Ihre wiederverwendbare Ansicht in einem Storyboard
Lauf und beobachte!
UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)
die schneller ist als die NSBundle-Version.
@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/…
Swift 3 & 4 Update auf die akzeptierte Antwort
1. Erstellen Sie eine neue UIView mit dem Namen 'DesignableXibView'.
2. Erstellen Sie eine passende xib-Datei mit dem Namen 'DesignableXibView'.
3. Legen Sie den Dateieigentümer des xib fest
Wählen Sie "DesignableXibView.xib"> "Dateibesitzer"> setzen Sie "Benutzerdefinierte Klasse" im Identitätsinspektor auf "DesignableXibView".
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
DesignableXibView
. Erstellen Sie das Projekt, um die Änderungen anzuzeigen.
String(describing: type(of: self))
sollte dies sicher tun
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]))
}
Nur das Umwandeln Swift
zu Objective-C
ist 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 Guides
in 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 :)
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);
}
}
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 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:
UIView
nach NibView
(oder von UITableViewCell
nach 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 👣