在Storyboard中使用由xib定义的view

前言

在iOS开发中,使用storyboard的某些场景下我们可能希望同时使用xib定义一些可以重复利用的view,并在storyboard中调用。本文将分享一种此类xib bridge的简单实现方式。

实现思路

本方法的思路是将xib的File’s Owner所对应的UIView作为placeholderView,其作用只是在storyboard中起到占位作用,并承载storyboard中与xib自定义view相关的AutoLayout约束,其背景色将被设为[UIColor clearColor],并且不显示任何内容。显示自定义内容的任务将交给一个UIViewcontentView,它将作为placeholderView的子视图。

为了使在storyboard中作用于placeholderView的AutoLayout约束能够自动的作用于contentView,可以向placeholderView添加NSLayoutRelationEqual约束,让placeholderViewcontentView的上下左右四个NSLayoutAttribute分别完全相等,这样contentView在storyboard中的的frame将完全和placeholderView相同,从而达到目的。

以上方法同样可以适用于xib的某个子view是另外一个xib的情形。

实现方法

首先进行如下准备工作

  • 创建一个xib,用于实现子view的自定义内容,本文中命名为CoverView.xib
  • 创建一个UIView的子类,作为placeholderView的基类,用于进行xib bridge相关的添加约束和添加contentView等工作,本文中命名为XibBridgeBaseView
  • 创建一个XibBridgeBaseView的子类,用于存储xib中自定义内容的相关属性并进行相关操作,本文中命名为CoverView

实现子view的xib文件

首先需要在identity inspector中将xib文件的File’s Owner设置成为创建好的CoverView
Files-Owner-identity-inspector

接下来我们就可以在xib中自动创建的UIView子视图中进行自定义UI了,此时可以在CoverView类中创建该UIView子视图(这里命名为contentView)以及其他UI组件的IBActionIBOutlet等。
setup-IBOutlet-and-IBAction

实现占位视图的基类

作为placeholderView的基类,XibBridgeBaseView中定义了如下的一个方法,用于从nib中载入contentView并添加进占位视图的子视图中,其中XibBridgeBaseViewinitWithCoder:方法将使用其派生子类的类名作为xib的名字,因此子类和其所对应的xib文件应该使用相同的命名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@implementation XibBridgeBaseView
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupXibBridgeWithPlaceholderViewNibName:NSStringFromClass([self class])];
}
return self;
}

- (void)setupXibBridgeWithPlaceholderViewNibName:(NSString *)placeholderViewNibName {
UIView *contentView =[[[NSBundle mainBundle] loadNibNamed:placeholderViewNibName
owner:self
options:nil] objectAtIndex:0];

[self setBackgroundColor:[UIColor clearColor]];
[self addSubview:contentView];
[self setXibBridgeConstraintsToContentView:contentView];
}

其中- (void)setXibBridgeConstraintsToContentView:(UIView *)contentView方法主要是实现前文提到的通过向占位视图添加NSLayoutRelationEqual约束让placeholderViewcontentView的上下左右四个NSLayoutAttribute分别完全相等:

1
2
3
4
5
6
7
8
9
10
11
12
13
@implementation XibBridgeBaseView
- (void)setXibBridgeConstraintsToContentView:(UIView *)contentView {
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(contentView)]];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(contentView)]];
//为保证AutoLayout生效,必须加上下面这句话
contentView.translatesAutoresizingMaskIntoConstraints = NO;
}

实现占位视图的实际类

作为placeholderView的实际类,每创建一个需要被桥接的xib时就要创建一个对应的实际类(本文中为CoverView类),CoverView中将包含在xib中定义的自定义UI相关的属性和操作:

1
2
3
4
5
@interface CoverView : XibBridgeBaseView

@property (weak, nonatomic) IBOutlet UILabel *headerLabel;
@property (weak, nonatomic) IBOutlet UIButton *submitButton;
@property (strong, nonatomic) IBOutlet UIView *contentView;
1
2
3
4
@implementation CoverView
- (IBAction)submitButtonClicked:(UIButton *)sender {
NSLog(@"Hello World!");
}

因为我们是用父storyboard或者xib来调用placeholderView的,实际使用中只需要将placeholderView的实际类继承于基类xibBridgeBaseView,即可实现桥接功能。该placeholderView的其他初始化工作可以放在- awakeFromNib中进行

1
2
3
4
5
6
@implementation CoverView
- (void)awakeFromNib {
self.contentView.backgroundColor = [UIColor clearColor];
[self.contentView.layer setBorderColor:[[UIColor whiteColor] CGColor]];
[self.contentView.layer setBorderWidth:1.0];
}

在父的storyboard或者xib中调用placeholderView

做完以上步奏后,只需在父storyboard或者xib中拖一个UIView来作为placeholderView,并在identity inspector中将其class属性设置成为对应的placeholderView的实际类即可。
set-class-for-placeholderView-in-storyboard

结语

在使用如上方法来进行xib桥接的过程中需要注意一下几点使用方式:

  • 在父的storyboard或者xib中,只需要对拖入的placeholderView添加AutoLayout约束即可
  • 在子xib中各个UI组件只需要跟contentView建立AutoLayout约束即可
  • 对于placeholderView的identity inspector相关参数需要在子xib中对File’s Owner进行设置
  • 对于placeholderView的attribute inspector相关参数需要在父storyboard或者xib中进行设置

对于xib桥接问题大神SUNNYXX给出了一个更高端的解决方案,利用到了iOS runtime相关的技术。


本文个人博客地址: http://wty.im/2016/02/29/use-view-defined-by-xib-in-storyboard/
Github: https://github.com/wty21cn/