【发布时间】:2015-07-31 20:09:22
【问题描述】:
我通常喜欢在界面生成器中创建和设计我的 uiview。有时我需要在 xib 中创建一个视图,该视图可以在情节提要的多个视图控制器中重用。
【问题讨论】:
标签: uiview autolayout interface-builder uistoryboard xib
我通常喜欢在界面生成器中创建和设计我的 uiview。有时我需要在 xib 中创建一个视图,该视图可以在情节提要的多个视图控制器中重用。
【问题讨论】:
标签: uiview autolayout interface-builder uistoryboard xib
使用 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
}
}
【讨论】:
NEW! updated answer with ability to render directly in the storyboard (and swift!)
适用于 Xcode 6.3.1
创建一个名为“ReuseableView”的新 UIView
创建一个名为“ReuseableView”的匹配 xib 文件
设置xib的文件所有者
在 Identity Inspector 中将自定义类设置为“ReusableView”。
从 ReuseableView.xib 中的视图到您的 ReuseableView.h 界面建立一个出口
添加 initWithCoder 实现以加载视图并添加为子视图。
- (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;
}
在故事板中测试可重复使用的视图
跑步观察!
【讨论】:
UINib(nibName: nibName, bundle: nil).instantiateWithOwner(nil, options: nil),它比 NSBundle-Version 更快。
@IBDesignable on the class. 您还必须实现 init(frame:) - 但除此之外它工作得很好! supereasyapps.com/blog/2014/12/15/…
Swift 3&4 更新到已接受的答案
1.创建一个名为“DesignableXibView”的新 UIView
2。创建一个名为“DesignableXibView”的匹配 xib 文件
3.设置xib的文件所有者
在身份检查器中选择“DesignableXibView.xib”>“文件的所有者”>将“自定义类”设置为“DesignableXibView”。
4. 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 在故事板中测试您的可重用视图
打开故事板
添加视图
设置该视图的自定义类
【讨论】:
DesignableXibView。构建项目以查看更改。
String(describing: type(of: self)) 应该安全地做到这一点
swift 2 中的 initWithCoder 函数,如果有人在翻译时遇到问题:
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]))
}
【讨论】:
仅仅将Swift 转换为Objective-C 是不够的。我很难在 Storyboard 中进行实时渲染。
翻译整个代码后,视图在设备(或模拟器)上运行时加载良好,但 Storyboard 中的实时渲染不起作用。原因是我使用了[NSBundle mainBundle] 而Interface Builder 没有访问mainBundle。您必须改用[NSBundle bundleForClass:self.classForCoder]。 BOOM,实时渲染现在可以使用了!
注意:如果您遇到自动布局问题,请尝试在 Xib 中禁用 Safe Area Layout Guides。
为方便起见,我将整个代码留在这里,因此您只需复制/粘贴(对于所有过程,请关注original answer):
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
如果您遇到一些问题,请告诉我,但它应该几乎可以开箱即用 :)
【讨论】:
这是您一直想要的答案。您可以只创建您的CustomView 类,将它的主实例放在带有所有子视图和插座的xib 中。然后,您可以将该类应用于情节提要或其他 xib 中的任何实例。
无需摆弄 File's Owner,或将 outlet 连接到代理或以特殊方式修改 xib,或添加自定义视图的实例作为其自身的子视图。
这样做:
UIView 更改为 NibView(或从 UITableViewCell 更改为 NibTableViewCell)就是这样!
它甚至可以与 IBDesignable 一起使用,在故事板的设计时呈现您的自定义视图(包括来自 xib 的子视图)。
您可以在此处阅读更多信息: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
您可以在此处获取开源 BFWControls 框架: https://github.com/BareFeetWare/BFWControls
汤姆?
【讨论】:
如果有人感兴趣,这里是 Xamarin.iOS 版本的@Garfbargle 的代码第 4 步
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);
}
}
【讨论】: