在故事板中重复使用 uiview xib

Reuse a uiview xib in storyboard

我通常喜欢在 Interface Builder 中创建和设计我的 uiview。有时我需要在 xib 中创建一个视图,该视图可以在情节提要中的多个视图控制器中重复使用。

适用于 Xcode6.3.1

创建一个名为 'ReuseableView'

的新 UIView
  • 文件 > 新建 > 文件 > 源 > Cocoa 触摸 Class > UIView

创建一个名为 'ReuseableView'

的匹配 xib 文件
  • 文件 > 新建 > 文件 > 用户界面 > 查看

设置xib的文件属主

  1. select xib
  2. select 文件所有者
  3. 在身份检查器中将自定义 class 设置为 'ReusableView'。

    • 注意:不要在xib上设置视图的自定义class。只有文件所有者!

从 ReuseableView.xib 中的视图到您的 ReuseableView.h 界面创建一个出口

  1. 打开助理编辑器
  2. Control + 从视图拖动到您的界面

添加 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;
}

在故事板中测试您的可重用视图

  1. 打开你的故事板
  2. 添加视图
  3. 设置该视图的自定义 Class

运行 观察!

swift2 中的 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]))
    }

在故事板中重用和渲染 xib。

测试 Swift 2.2 & Xcode 7.3.1

1 ---- 创建一个名为 'DesignableXibView'

的新 UIView
  • 文件 > 新建 > 文件 > 源 > Cocoa 触摸 Class > UIView

2 ---- 创建一个匹配的 xib 文件命名为 'DesignableXibView'

  • 文件 > 新建 > 文件 > 用户界面 > 查看

3----设置xib的文件属主

  1. select xib
  2. select 文件所有者
  3. 在身份检查器中将自定义 class 设置为 'DesignableXibView'。

  • 注意:不要在xib上设置视图的自定义class。只有文件所有者!

4 ---- 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 ---- 在故事板中测试您的可重用视图

  1. 打开你的故事板
  2. 添加视图
  3. 设置该视图的自定义 Class
  4. 等一下... 砰!!

Swift 3&4 Update to the accepted answer

1.创建一个名为 'DesignableXibView'

的新 UIView
  • 文件 > 新建 > 文件 > 源 > Cocoa 触摸 Class > UIView

2。创建一个名为 'DesignableXibView'

的匹配 xib 文件
  • 文件 > 新建 > 文件 > 用户界面 > 查看

3。设置xib

的文件所有者

Select "DesignableXibView.xib" > "File's Owner" > 在身份检查器中将 "Custom Class" 设置为 'DesignableXibView'。

  • 注意:不要在xib上设置视图的自定义class。只有 文件所有者!

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 在故事板中测试您的可重用视图

  • 打开你的故事板

  • 添加视图

  • 设置该视图的自定义 Class

如果有人感兴趣,这里是@Garfbargle

的代码第 4 步的 Xamarin.iOS 版本
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);
        }
    }

这是您一直想要的答案。您可以只创建 CustomView class,将它的主实例放在带有所有子视图和插座的 xib 中。然后你可以将 class 应用到故事板或其他 xib 中的任何实例。

无需 fiddle 文件所有者,或将出口连接到代理或以特殊方式修改 xib,或添加自定义视图的实例作为其自身的子视图。

只需这样做:

  1. 导入 BFWControls 框架
  2. 将您的 superclass 从 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

汤姆

对于任何试图将接受的答案(@Garfbargle)改编为Objective-C

的人

仅仅将 Swift 转换为 Objective-C 是不够的。我很难在情节提要中允许实时渲染。

翻译整个代码后,在设备(或模拟器)上 运行 时视图可以很好地加载,但 Storyboard 中的实时渲染不起作用。这样做的原因是我使用了 [NSBundle mainBundle] 而 Interface Builder 没有访问 mainBundle。您必须使用的是 [NSBundle bundleForClass:self.classForCoder]。 BOOM,实时渲染现在可以工作了!

注意:如果您遇到自动布局问题,请尝试在 Xib 中禁用 Safe Area Layout Guides

为方便起见,我将全部代码留在这里,这样您只需copy/paste(对于所有过程,请遵循):

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

如果您遇到一些问题请告诉我,但它几乎应该立即可用:)