XIB自定义UIView
在XIB中使用autolayout是非常方便的,所以有的时候我们需要使用XIB来创建自定义UIView。但是不像UITableViewCell,创建Class的时候可以选择勾选自动创建XIB,UIView只能单独创建XIB,下面整理一下通过XIB创建自定义UIView的步骤:
- 创建自定义的UIView,例如MyXibView extends UIView;
- 创建
不相同名字
的XIB,使用View模板,例如MyXib.xib; - 在XIB中设置View的Class为MyXibView;
- 在MyXibView.m中实现一个类方法,代码如下:
+ (MyXibView*) myXibView {
NSArray* array = [[NSBundle mainBundle] loadNibNamed:@"MyXib" owner:nil options:nil];
return [array objectAtIndex:0];
}
后续的操作就如你正常的进行,在XIB中放置控件,并设置autolayout约束,关联代码文件中的IBOutlet和IBAction,进行相关的业务逻辑处理;
这里XIB与Class的必须要使用不同的名字,否则会出各种问题:
- 在XIB的ViewController中frame问题,设置了frame无效,必须要在
viewWillLayoutSubviews
方法重设。- 在纯代码的ViewController中会奔溃,
[UIViewController _loadViewFromNibNamed:bundle:] loaded the "MyView" nib but the view outlet was not set.
。猜测原因是[NSBundle loadNibNamed]方法会寻找Nib同名的Class,默认认为这个Class是UIViewController,需要一个IBOutlet,但是这里没有连接IBOutlet,而且对应的也只是一个UIView。
initWithCoder
在测试上面自定义方法的时候我有想到initWithCoder
方法,也参考了initWithFrame
方法的实现,结果就死循环了,loadNibNamed
方法会调用initWithCoder
方法,而initWithCoder
方法又调用了loadNibNamed
方法。
initWithCoder方法是当前控件被其他的XIB或者Storyboard文件引用
的时候调用的,自己有一个对应的XIB文件,然而外部的XIB或者Storyboard文件中也有相应的布局,那么这两个布局冲突
了。如果不实现initWithCoder
方法,XIB或者Storyboard引用的时候自己的XIB中的内容和布局都不会存在。
所以如果需要在外部的XIB或者Storyboard文件中使用自定义的控件,不要使用XIB这种方式创建,应该试用纯代码的方式创建。
参考链接:iPhone-load-a-uiview-from-a-nib-file
IB_DESIGNABLE和IBInspectable
使用纯代码自定义UIView的时候可以使用这两个属性,让使用者在XIB或者Storyboard中实时看到修改属性的效果。
具体使用方法是在Interface上加上IB_DESIGNABLE,在属性上加上IBInspectable,重写属性的setter方法,对UIView的样式进行修改。
#import <UIKit/UIKit.h>
IB_DESIGNABLE
/**
自定义导航栏
*/
@interface CustomNavi : UIView
/**
是否需要返回按钮
*/
@property (nonatomic, assign) IBInspectable BOOL needBackButton;
/**
显示的标题
*/
@property (nonatomic, copy) IBInspectable NSString *title;
/**
背景色
*/
@property (nonatomic, copy) IBInspectable UIColor *bgColor;
@end
// ------------------------- 我是分割线 -------------------------
#import "CustomNavi.h"
@implementation CustomNavi {
UIButton *_backButton;
UILabel *_titleLabel;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initUI];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initUI];
}
return self;
}
/**
初始化,代码创建标题Label
*/
- (void)initUI {
_titleLabel = [[UILabel alloc] init];
[self addSubview:_titleLabel];
}
/**
设置标题,居中显示
@param title 标题
*/
- (void)setTitle:(NSString *)title {
[_titleLabel setText:title];
[_titleLabel sizeToFit];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
_titleLabel.center = CGPointMake(screenWidth/2.0f, 22);
}
/**
设置是否需要返回按钮
*/
- (void)setNeedBackButton:(BOOL)needBackButton {
if (needBackButton) {
self.backButton.hidden = !needBackButton;
} else {
_backButton.hidden = !needBackButton;
}
}
/**
懒加载方式创建返回按钮
*/
- (UIButton *)backButton {
if (_backButton == nil) {
_backButton = [UIButton buttonWithType:UIButtonTypeCustom];
_backButton.frame = CGRectMake(8, 6, 28, 28);
[_backButton setTitle:@"<" forState:UIControlStateNormal];
[self addSubview:_backButton];
}
return _backButton;
}
/**
设置背景颜色
*/
- (void)setBgColor:(UIColor *)bgColor {
self.backgroundColor = bgColor;
}
@end
XIB创建UIView方式2
XIB创建UIView除了上面的方法外,还有一种方式。
- 创建XIB文件,创建Class文件,两个文件名随意,可以相同也可以不相同。
- 选择XIB的File's Owner,设置Class为创建的Class文件名。
- 在Class中定义一个IBOutlet的view,将XIB中的View连线至这个IBOutlet。
- 在XIB中放置控件,并设置autolayout约束,关联代码文件中的IBOutlet和IBAction。
- 重写Class中的init方法:
@interface CustomXibView : UIView
@property (nonatomic, weak) IBOutlet UIView *contentView;
@end
@implementation CustomXibView
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
[self commonInit];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self commonInit];
}
return self;
}
- (void)commonInit {
// 这里因为与XIB名字相同,所以使用[self class]
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil];
[self addSubview:self.contentView];
self.contentView.frame = self.bounds;
// 把内容放到最下面
[self sendSubviewToBack:self.contentView];
}
参考链接:Creating a Custom View using a xib in Objective-C
这种方式创建的UIView其实外面会套个壳,frame修改的时候需要同步到contentView,但是优点是可以同时在存代码和XIB中使用。