Normalerweise möchte ich meine Ansichten im Interface Builder erstellen und gestalten. Manchmal muss ich eine einzelne Ansicht in einem Xib erstellen, die in mehreren Ansichtscontrollern in einem Storyboard wiederverwendet werden kann.
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 Möglichkeit, direkt im Storyboard zu rendern (und Swift!)
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'}
Legt den Dateibesitzer der Xib fest
setzen Sie die benutzerdefinierte Klasse im Identity Inspector auf 'ReusableView'.
Machen Sie einen Auslass aus der Ansicht in der ReuseableView.xib zu Ihrer ReuseableView.h-Schnittstelle
Fügen Sie die initWithCoder-Implementierung hinzu, um die Ansicht zu laden und als Unteransicht hinzuzufügen.
- (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!
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. Setze den Dateibesitzer des xib
Wählen Sie "DesignableXibView.xib"> "Eigentümer der Datei"> 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 (over any custom drawing > see note below)
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 Teste deine wiederverwendbare Ansicht in einem Storyboard
Öffne dein Storyboard
Ansicht hinzufügen
Legen Sie die benutzerdefinierte Klasse dieser Ansicht fest
Die initWithCoder-Funktion in Swift 2, falls 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]))
}
Es reicht nicht aus, Swift
in Objective-C
zu konvertieren, damit es funktioniert. Es fiel mir schwer, Live-Rendering in Storyboard zuzulassen.
Nachdem der gesamte Code übersetzt wurde, ist die Ansicht auf dem Gerät (oder im Simulator) gut geladen, aber das Live-Rendering in Storyboard funktioniert nicht. Der Grund dafür ist, dass ich [NSBundle mainBundle]
verwendet habe, 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 Safe Area Layout Guides
in der 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 ursprüngliche 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
Sag mir, wenn du auf ein paar Probleme stößt, aber es sollte fast funktionieren :)
Wenn jemand interessiert 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 schon immer haben wollten. Sie können einfach Ihre Klasse CustomView
erstellen und die Master-Instanz davon in einer xib mit allen Unteransichten und Ausgängen haben. Dann können Sie diese Klasse auf alle Instanzen in Ihren Storyboards oder anderen xibs anwenden.
Sie müssen nicht mit dem Besitzer von File herumspielen, keine 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 einfach folgendes:
UIView
zu NibView
(oder von UITableViewCell
zu NibTableViewCell
)Das ist es!
Es funktioniert sogar mit IBDesignable, um Ihre benutzerdefinierte Ansicht (einschließlich der Unteransichten aus der xib) zur Entwurfszeit im Storyboard zu rendern.
Sie können hier mehr darüber lesen: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Das Open-Source-Framework für BFWControls erhalten Sie hier: https://github.com/BareFeetWare/BFWControls
Tom ????