Zazwyczaj lubię tworzyć i projektować uiviews w konstruktorze interfejsów. Czasami potrzebuję utworzyć pojedynczy widok w xib, który można ponownie wykorzystać w wielu kontrolerach widoku w scenorysie.
Zazwyczaj lubię tworzyć i projektować uiviews w konstruktorze interfejsów. Czasami potrzebuję utworzyć pojedynczy widok w xib, który można ponownie wykorzystać w wielu kontrolerach widoku w scenorysie.
Odpowiedzi:
Przetestowano w Swift 2.2 i 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
}
}
NOWY! zaktualizowana odpowiedź z możliwością renderowania bezpośrednio w scenorysie (i szybko!)
Działa w Xcode 6.3.1
Utwórz nowy UIView o nazwie „ReuseableView”
Utwórz pasujący plik XIB o nazwie „ReuseableView”
Ustaw właściciela pliku xib
ustaw klasę niestandardową na „ReusableView” w Inspektorze tożsamości.
Utwórz gniazdko z widoku w ReuseableView.xib do interfejsu ReuseableView.h
Dodaj implementację initWithCoder, aby załadować widok i dodać jako widok podrzędny.
- (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;
}
Przetestuj widok wielokrotnego użytku w scenorysie
Biegnij i obserwuj!
UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil)
szybszego niż NSBundle-Version.
@IBDesignable on the class.
również zaimplementować init(frame:)
- ale poza tym działa całkiem dobrze! supereasyapps.com/blog/2014/12/15/…
Swift 3 i 4 Zaktualizuj zaakceptowaną odpowiedź
1. Utwórz nowy UIView o nazwie „DesignableXibView”
2. Utwórz pasujący plik xib o nazwie „DesignableXibView”
3. Ustaw właściciela pliku xib
Wybierz „DesignableXibView.xib”> „Właściciel pliku”> ustaw „Niestandardową klasę” na „DesignableXibView” w Inspektorze tożsamości.
4. Implementacja 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 Przetestuj widok wielokrotnego użytku w serii ujęć
Otwórz scenorys
Dodaj widok
Ustaw klasę niestandardową tego widoku
DesignableXibView
. Zbuduj projekt, aby wyświetlić zmiany.
String(describing: type(of: self))
powinienem to zrobić bezpiecznie
Funkcja initWithCoder w swift 2, jeśli ktoś ma problem z jej przetłumaczeniem:
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]))
}
Samo przekonwertowanie Swift
na Objective-C
nie wystarczy, aby to działało. Trudno mi było zezwolić na renderowanie na żywo w Storyboard.
Po przetłumaczeniu całego kodu widok jest dobrze ładowany, gdy działa na urządzeniu (lub symulatorze), ale renderowanie na żywo w Storyboard nie działa. Powodem tego jest to, że użyłem, [NSBundle mainBundle]
podczas gdy Interface Builder nie ma dostępu do mainBundle. Zamiast tego musisz użyć[NSBundle bundleForClass:self.classForCoder]
. BOOM, renderowanie na żywo działa teraz!
Uwaga: jeśli masz problemy z Auto Layout, spróbuj wyłączyć Safe Area Layout Guides
w Xib.
Dla Twojej wygody zostawiam tutaj cały mój kod, abyś musiał tylko skopiować / wkleić (dla całego procesu postępuj zgodnie z oryginalną odpowiedzią ):
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
Powiedz mi, jeśli napotkasz jakieś problemy, ale powinno prawie działać po wyjęciu z pudełka :)
Jeśli ktoś jest zainteresowany, oto Xamarin.iOS wersja kodu Krok 4 @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);
}
}
Oto odpowiedź, której szukałeś przez cały czas. Możesz po prostu utworzyć swoją CustomView
klasę, umieścić jej główną instancję w pliku xib ze wszystkimi widokami podrzędnymi i gniazdami. Następnie możesz zastosować tę klasę do dowolnych wystąpień w swoich scenorysach lub innych plikach xib.
Nie trzeba majstrować przy właścicielu pliku, podłączać gniazd do serwera proxy lub modyfikować xib w szczególny sposób ani dodawać instancji własnego widoku jako widoku podrzędnego samego siebie.
Po prostu zrób to:
UIView
na NibView
(lub z UITableViewCell
na NibTableViewCell
)Otóż to!
Działa nawet z IBDesignable, aby renderować niestandardowy widok (w tym podglądy z xib) w czasie projektowania w scenorysie.
Możesz przeczytać więcej na ten temat tutaj: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
Możesz pobrać platformę BFWControls typu open source tutaj: https://github.com/BareFeetWare/BFWControls
Tom 👣