現(xiàn)狀
1揽碘、用代碼寫的公共view次屠,用起來比較簡單,不論是代碼方式還是IB方式雳刺,都很簡單劫灶;
2、用xib寫的組件掖桦,需要調用者來加載本昏,用代碼方式加入到原有的視圖體系中;如果調用者是用IB畫界面的枪汪,那么就要用代碼設限制或者定frame涌穆,不是很方便
改進點
將加載代碼從調用者移到公共組件內(nèi)部,讓IB開發(fā)界面的調用者只要拉一個IBOutlet就可以訪問了料饥,不需要own這個公共組件對象,和系統(tǒng)組件一樣方便使用朱监。
xib加載過程
函數(shù)調用情況
- 不會自動調用的函數(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;
}
- 自動調用的函數(shù)
- (void)awakeFromNib {
[self loadViewFromXib];
}
- initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder: aDecoder];
if (self) {
[self loadViewFromXib];
}
return self;
}
選擇的加載函數(shù)
- (void)awakeFromNib {
[self loadViewFromXib];
}
自動加載岸啡,通過IBOutlet的方式訪問對象,調用者不需要own這個組件
加載的代碼
- (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 設置為自定義的組件類
- xib中頂級的view就是load函數(shù)加載等到的view赫编,只是個容器
- 將容器view以addSubview的方式添加到自定義的組件類(self)
- 代碼加限制巡蘸,將容器view和自定義的組件類(self)完全重合奋隶,使self成了最底層的容器
加載完后執(zhí)行的函數(shù)
// View在加載完成之后,會自動調用這個函數(shù)悦荒,這里可以更新約束
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
// 其它代碼
// 變換約束之后唯欣,重新布局,以便取到正確的frame
[self layoutIfNeeded];
// 這個函數(shù)執(zhí)行完搬味,一般要加這句境氢,更新View
[self setNeedsDisplay];
}
自動函數(shù)加載順序
場景:ViewController作為調度者。用xib做一個自定義的view名字叫ChartView碰纬,供外部調用萍聊。xib中用代碼做一個自定義的view,叫PlotView悦析,用來畫圖表寿桨。經(jīng)過斷點實驗,加載順序如下
- 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
這個調用關系就是導致有些函數(shù)放在
- (void)viewDidLoad
和- (IBAction)buttonTouched:(UIButton *)sender
中有不同表現(xiàn)的原因
難點
- 一般xib的File's Owner是ViewController强戴,所以Xcode默認讓xib對應一個ViewController
- tableViewCell的File's Owner是空的(NSObject)亭螟,最頂層的view容器設置為自定義的Cell類,所以需要對應的ViewController來加載這個cell骑歹,調用者ViewController擁有own這個cell(strong引用)
- 一般的xib公共組件预烙,F(xiàn)ile's Owner是空的(NSObject),所以需要調用者加載陵刹,調用這own這個組件默伍。做法就是將最頂層view設置為自定義的組件類。這個跟tableViewCell的用法很像衰琐。
- 將xib的File's Owner設置為自定義的類之后也糊,頂層的view容器的own是自定義的類自身self,所以這個view的引用是strong羡宙,雖然是IBOutlet狸剃,跟其它的不一樣
@property (strong, nonatomic) IBOutlet UIView *containerView;
- 自定義類的owner是調用者的xib文件,所以自定義類本身(self)在調用者類中可以是弱引用狗热,weak钞馁,像普通的IBOutlet一樣
@property (weak, nonatomic) IBOutlet PAButtonWithBadgeView *buttonWithBadgeView;
注意點
由于自定義類本身是最底層的view容器,而xib中的View容器完全覆蓋在上面匿刮,所有擋住了僧凰,這個導致設置自定義的背景色不起作用。解決的方法是重寫基類的設置函數(shù)
// self是最底層容器熟丸,會被self.containerView擋住训措,導致看起來代碼設背景色不起作用,所以覆蓋基類的函數(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;
}
} ```
> 遺留問題:
這樣做,代碼設背景色绩鸣,看上去是變了怀大;但是,直接在調用者xib中設置自定義組件的背景色呀闻,還是不起作用的
# 備選方案
在自定義組件的xib中化借,頂層view容器的背景色設置為clearColor。這樣自定義組件的背景色完全由自定義組件自身來決定捡多。調用者在xib和代碼設置背景色都能成功蓖康。但是這樣會導致如下不完美之處:
* 在自定義組件設計的xib中,背景色就是一片空白局服,失去了所見即所得的優(yōu)勢钓瞭。
* 在實際使用過程中,自定義組件都是用view拉一個占位符淫奔,其它的具體內(nèi)容還是要切換到自定義組件的xib中去看具體內(nèi)容山涡。背景色在這里,更有意義唆迁。
**正是為了發(fā)揮xib更直觀的優(yōu)勢鸭丛,所以采用了上面那種不完美的方案√圃穑基于一個假設:在總的xib上的占位符上用IB設背景色鳞溉,沒有什么意義。將背景色和自定義組件的xib具體內(nèi)容結合起來看更有意義鼠哥。**
# 實例代碼
[zhangxusong888/PAButtonWithBadgeViewTest](https://github.com/zhangxusong888/PAButtonWithBadgeViewTest)
作為對比熟菲,下面這個例子是純代碼寫的控件
[zhangxusong888/PAProcessBarTest](https://github.com/zhangxusong888/PAProcessBarTest)