現(xiàn)狀
1、用代碼寫的公共view,用起來比較簡單,不論是代碼方式還是IB方式,都很簡單;
2、用xib寫的組件,需要調(diào)用者來加載,用代碼方式加入到原有的視圖體系中;如果調(diào)用者是用IB畫界面的,那么就要用代碼設(shè)限制或者定frame,不是很方便
改進(jìn)點(diǎn)
將加載代碼從調(diào)用者移到公共組件內(nèi)部,讓IB開發(fā)界面的調(diào)用者只要拉一個(gè)IBOutlet就可以訪問了,不需要own這個(gè)公共組件對(duì)象,和系統(tǒng)組件一樣方便使用。
xib加載過程
函數(shù)調(diào)用情況
- 不會(huì)自動(dòng)調(diào)用的函數(shù)
- (instancetype)init {
self = [super init];
if (self) {
[self loadViewFromXib];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self loadViewFromXib];
}
return self;
}
- 自動(dòng)調(diào)用的函數(shù)
- (void)awakeFromNib {
[self loadViewFromXib];
}
- initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder: aDecoder];
if (self) {
[self loadViewFromXib];
}
return self;
}
選擇的加載函數(shù)
- (void)awakeFromNib {
[self loadViewFromXib];
}
自動(dòng)加載,通過IBOutlet的方式訪問對(duì)象,調(diào)用者不需要own這個(gè)組件
加載的代碼
- (void)loadViewFromXib {
NSArray *views = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
UIView *view = [views firstObject];
if (nil != view) {
UIView *superview = self;
[superview addSubview:view];
UIEdgeInsets padding = UIEdgeInsetsMake(0, 0, 0, 0);
[view mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
}
}
- 需要將xib的File's Owner 設(shè)置為自定義的組件類
- xib中頂級(jí)的view就是load函數(shù)加載等到的view,只是個(gè)容器
- 將容器view以addSubview的方式添加到自定義的組件類(self)
- 代碼加限制,將容器view和自定義的組件類(self)完全重合,使self成了最底層的容器
加載完后執(zhí)行的函數(shù)
// View在加載完成之后,會(huì)自動(dòng)調(diào)用這個(gè)函數(shù),這里可以更新約束
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
// 其它代碼
// 變換約束之后,重新布局,以便取到正確的frame
[self layoutIfNeeded];
// 這個(gè)函數(shù)執(zhí)行完,一般要加這句,更新View
[self setNeedsDisplay];
}
自動(dòng)函數(shù)加載順序
場景:ViewController作為調(diào)度者。用xib做一個(gè)自定義的view名字叫ChartView,供外部調(diào)用。xib中用代碼做一個(gè)自定義的view,叫PlotView,用來畫圖表。經(jīng)過斷點(diǎn)實(shí)驗(yàn),加載順序如下
- ChartView:
- (void)awakeFromNib - ViewController:
- (void)viewDidLoad - ViewController:
- (void)viewWillAppear:(BOOL)animated - ViewController:
- (void)viewWillLayoutSubviews - ViewController:
- (void)viewDidLayoutSubviews - ChartView:
- (void)drawRect:(CGRect)rect - PlotView:
- (void)drawRect:(CGRect)rect - ViewController:
- (void)viewDidAppear:(BOOL)animated
這個(gè)調(diào)用關(guān)系就是導(dǎo)致有些函數(shù)放在
- (void)viewDidLoad和- (IBAction)buttonTouched:(UIButton *)sender中有不同表現(xiàn)的原因
難點(diǎn)
- 一般xib的File's Owner是ViewController,所以Xcode默認(rèn)讓xib對(duì)應(yīng)一個(gè)ViewController
- tableViewCell的File's Owner是空的(NSObject),最頂層的view容器設(shè)置為自定義的Cell類,所以需要對(duì)應(yīng)的ViewController來加載這個(gè)cell,調(diào)用者ViewController擁有own這個(gè)cell(strong引用)
- 一般的xib公共組件,F(xiàn)ile's Owner是空的(NSObject),所以需要調(diào)用者加載,調(diào)用這own這個(gè)組件。做法就是將最頂層view設(shè)置為自定義的組件類。這個(gè)跟tableViewCell的用法很像。
- 將xib的File's Owner設(shè)置為自定義的類之后,頂層的view容器的own是自定義的類自身self,所以這個(gè)view的引用是strong,雖然是IBOutlet,跟其它的不一樣
@property (strong, nonatomic) IBOutlet UIView *containerView; - 自定義類的owner是調(diào)用者的xib文件,所以自定義類本身(self)在調(diào)用者類中可以是弱引用,weak,像普通的IBOutlet一樣
@property (weak, nonatomic) IBOutlet PAButtonWithBadgeView *buttonWithBadgeView;
注意點(diǎn)
由于自定義類本身是最底層的view容器,而xib中的View容器完全覆蓋在上面,所有擋住了,這個(gè)導(dǎo)致設(shè)置自定義的背景色不起作用。解決的方法是重寫基類的設(shè)置函數(shù)
// self是最底層容器,會(huì)被self.containerView擋住,導(dǎo)致看起來代碼設(shè)背景色不起作用,所以覆蓋基類的函數(shù)
- (void)setBackgroundColor:(UIColor *)backgroundColor {
if (nil != backgroundColor) {
self.backgroundColor = backgroundColor;
[self setContainerColor:backgroundColor];
}
}
- (void)setContainerColor:(UIColor *)color {
if (nil != color) {
self.containerView.backgroundColor = color;
}
} ```
> 遺留問題:
這樣做,代碼設(shè)背景色,看上去是變了;但是,直接在調(diào)用者xib中設(shè)置自定義組件的背景色,還是不起作用的
# 備選方案
在自定義組件的xib中,頂層view容器的背景色設(shè)置為clearColor。這樣自定義組件的背景色完全由自定義組件自身來決定。調(diào)用者在xib和代碼設(shè)置背景色都能成功。但是這樣會(huì)導(dǎo)致如下不完美之處:
* 在自定義組件設(shè)計(jì)的xib中,背景色就是一片空白,失去了所見即所得的優(yōu)勢(shì)。
* 在實(shí)際使用過程中,自定義組件都是用view拉一個(gè)占位符,其它的具體內(nèi)容還是要切換到自定義組件的xib中去看具體內(nèi)容。背景色在這里,更有意義。
**正是為了發(fā)揮xib更直觀的優(yōu)勢(shì),所以采用了上面那種不完美的方案?;谝粋€(gè)假設(shè):在總的xib上的占位符上用IB設(shè)背景色,沒有什么意義。將背景色和自定義組件的xib具體內(nèi)容結(jié)合起來看更有意義。**
# 實(shí)例代碼
[zhangxusong888/PAButtonWithBadgeViewTest](https://github.com/zhangxusong888/PAButtonWithBadgeViewTest)
作為對(duì)比,下面這個(gè)例子是純代碼寫的控件
[zhangxusong888/PAProcessBarTest](https://github.com/zhangxusong888/PAProcessBarTest)