自定義可重用控件——來自現(xiàn)實開發(fā)中的真實需求,基本上有兩種方法:
- 純代碼寫。對于界面不太復雜的控件,這種做法比較受歡迎,畢竟自己可以掌控一切。
- 用XIB來寫。對于界面稍微復雜一點的,可以用這種方式;在IB中拖拽,使用自動布局調整控件UI邏輯,比寫代碼快捷又直觀。
本文就簡單討論一下用XCode7寫基于UIView的控件的基本過程。
第一步:創(chuàng)建一個基于UIView的Cocoa Touch Class,例如類名為OperationBarView
XCode中:File -> New ->File -> Cocua Touch Class,如下圖所示。注意,這時下面的“Also create XIB file”是不能選中的。XCode只允許基類為UIViewController或者UITableViewCell的子類同時生成XIB File。

第二步:創(chuàng)建XIB File,命名為OperationBarView.xib
這里的文件名可以和類名不同,但是建議使用相同的名字。
在XCode中,F(xiàn)ile –> New ->File -> User Interface -> View

第三步:關聯(lián)XIB和類。
在“Project Navigator”里面,選擇剛才創(chuàng)建的OperationBarView.xib打開該文件,選擇“File’s Owner”,然后在右側的“Identity Insepctor”里面,指定Custom Class為第一步中創(chuàng)建的類OperationBarView。

第四步:指定View的Size類型
第二步創(chuàng)建出來的View的默認Size類型為Inferred,大多情況下,這個Size就是屏幕的大小,而且無法改變。我們要設計自己的控件的大小,可以將該Size類型設定為Freedom。
在打開的XIB文件中選擇View,右側的Attributes Insepctor里面指定。

這樣就可以將View界面調整為自己所需要的大小。
第五步:設計自己的UI
根據自己的需要設計UI元素,布局關系,例如:

第六步:為View創(chuàng)建一個IBOutlet對象
在打開的XIB中,Control-Drag View對象到OperationBarView.h,并創(chuàng)建連接。

第七步:加載NIB
在OperationBarView.m文件中,利用初始化方法中加載NIB文件,并把NIB文件中的對象加入對象圖。
(instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
NSString *nibName = NSStringFromClass([self class]);
[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
第八步:在VC中使用自定義控件
好了,至此,我們就創(chuàng)建了一個可以重用的UI組件,可以在其他地方使用了。我們在一個VC上拖曳一個UIView,然后指定其Class為OperationBarView,設置其背景為黑色以容易區(qū)分。(請忽略截圖中的其他元素,因為示例中同時也在測試其他的控件,但和本主題無關)

來看一下運行效果:

從運行效果看,新建的自定義View并沒有填滿我們指定的View(黑色區(qū)域)。什么地方沒做對呢?我們來理一下這里的黑色背景的View和我們的自定制View之間的關系:
拖曳到VC上的UIView,我們指定了他的Class為OperationBarView。因此,這個UIView被創(chuàng)建出來的OperationBarView對象則為OperationBarView.xib的File’s Owner對象;也就是OperationBarView.m中所引用的self對象。
OperationBarView.m中所引用的self是一個UIView
self.view所引用的UIView則是我們自定義控件的根View
只有將self.view所引用的View加入到self的SubView里面,自定義控件才會被顯示。
-
但是self.view的位置、大小和self并不一致,需要我們自己來設定。
一旦理清了上面的關系,就會明白實際運行是自定義控件并沒有完全占滿所定義的黑色區(qū)域,那是因為我們沒有為其設置顯示位置、大小等屬性。我們可以在加載完NIB后,為self.view添加約束,讓其自動填滿self。這里我們只能用代碼來添加約束,如下:instancetype)initWithCoder:(NSCoder *)aDecoder { if (self = [super initWithCoder:aDecoder]) { NSString *nibName = NSStringFromClass([self class]); [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]; [self addSubview:self.view]; //方案1:設置自動約束, [self.view setTranslatesAutoresizingMaskIntoConstraints:NO];** [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; }
這樣一來,運行效果就比較正常了,如下圖所示:

其實還有個更簡單的方案,就是設置self.view的frame屬性,可以達到同樣的效果,如下:
instancetype)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder])
{
NSString *nibName = NSStringFromClass([self class]);
[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
[self addSubview:self.view];
//方案2:設置frame
self.view.frame = self.bounds;
}
return self;
}