自定義view創(chuàng)建
1.純代碼的方式創(chuàng)建自定義View
自定義view的基本步驟
1.重寫 - (instancetype)initWithFrame方法,在此方法中創(chuàng)建并添加子控件。
2.提供一個(gè)便利的構(gòu)造方法,通常為 類方法,快速創(chuàng)建一個(gè)實(shí)例對象
3.重寫 - (void)layoutSubviews方法,在此方法中設(shè)置子控件的frame,
一定要調(diào)用[super layoutSubviews]
4.設(shè)置模型屬性,在set方法中,給對應(yīng)的子控件賦值。
具體實(shí)現(xiàn)代碼
XYBookView.h 頭文件
#import <UIKit/UIKit.h>
@class XYBook;
@interface XYBookView : UIView
// 只放一個(gè)數(shù)據(jù)屬性用來賦值,內(nèi)部布局,放到.m 中自己管,不暴露給外界
@property (nonatomic, strong) XYBook *book;
@end
實(shí)現(xiàn)文件 .m文件
#import "XYBookView.h"
#include "XYBook.h" //模型
@interface XYBookView ()
// 兩個(gè)內(nèi)部子控件在內(nèi)部包裝起來,不給外界看到
@property (nonatomic, weak) UIImageView *icon;
@property (nonatomic, weak) UILabel *label;
@end
@implementation XYBookView
// 1.重寫initWithFrame:方法,創(chuàng)建子控件并添加到自己上面
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// 1. 創(chuàng)建書圖標(biāo)
UIImageView *icon = [UIImageView new];
self.icon = icon;
[self addSubview:self.icon];
// 2.書名
UILabel *bookName = [UILabel new];
bookName.textAlignment = NSTextAlignmentCenter;
self.label = bookName;
[self addSubview:self.label];
}
return self;
}
// 2.重寫layoutSubviews,給自己內(nèi)部子控件設(shè)置frame
- (void)layoutSubviews {
[super layoutSubviews];
CGSize size = self.frame.size;
self.icon.frame = CGRectMake(0, 0, size.width , size.height * 0.7);
self.label.frame = CGRectMake(0, size.height * 0.7, size.width, size.height *(1 - 0.7));
}
// 3.調(diào)用模型的set方法,給書的子控件賦值,
- (void)setBook:(XYBook *)book {
_book = book;
self.icon.image = [UIImage imageNamed:book.icon];
self.label.text = book.name;
}
@end
以上是純代碼實(shí)現(xiàn)的View的封裝,寫起來會(huì)麻煩點(diǎn)。
使用純代碼封裝創(chuàng)建自定義View的時(shí)候需要注意:
1.一般來說我們的自定義類繼承自UIView,首先在initWithFrame:方法中將需要的子控件加入view中。
注意:這里只是加入到view中,并沒有設(shè)置各個(gè)子控件的尺寸。并且是在initWithFrame方法中而不是init方法
2.initWithFrame:中添加子控件。
layoutSubviews中設(shè)置子控件frame。
對外設(shè)置數(shù)據(jù)接口,重寫setter方法給子控件設(shè)置顯示數(shù)據(jù)。(model)
在view controller里面使用init/initWithFrame:方法創(chuàng)建自定義類,并且給自定義類的frame賦值。
對自定義類對外暴露的數(shù)據(jù)接口進(jìn)行賦值即可。
2.關(guān)聯(lián)xib的方式創(chuàng)建自定義View
xib關(guān)聯(lián)自定義view的步驟:
1. 創(chuàng)建一個(gè)自定義的view:Cocoa Touch Class
創(chuàng)建UIView時(shí)候 Also create XIB file 的選項(xiàng)是不能被勾選的,與自定義cell不同
2.創(chuàng)建一個(gè)同名的xib: User Interface -> View
3.設(shè)置xib的File`s Owner的Custome Class屬性為自定義的view:
4.然后在自定義的view里面重寫你需要初始化的方法:
NSArray *nibView = [[NSBundle mainBundle] loadNibNamed:@"xib的名字"owner:self options:nil];
UIView *backView = [nibView objectAtIndex:0];
backView.frame = frame;
[self addSubview:backView];
方式一 :
//重寫initWithFrame構(gòu)造方法
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
//ARRewardView : 自定義的view名稱
NSArray *nibView = [[NSBundle mainBundle] loadNibNamed:@"ARRewardView"owner:self options:nil];
UIView *backView = [nibView objectAtIndex:0];
backView.frame = frame;
[self addSubview:backView];
}
return self;
}
方式二:
//寫一個(gè)創(chuàng)建自定義view的類方法
+ (instancetype)initCreatView {
return [[[NSBundle mainBundle] loadNibNamed:@"ARRewardView"owner:self options:nil] objectAtIndex:0];
}
使用關(guān)聯(lián)xib自定義View編碼是,需要注意:
系統(tǒng)的調(diào)用流程: initWithCoder —> awakeFromNib —> layoutSubviews
(1).加載XIB后,系統(tǒng)會(huì)自動(dòng)調(diào)用 - (id)initWithCoder:(NSCoder *)aDecoder
方法來初始化控件,其中aDecoder是一個(gè)解析器,對XIB進(jìn)行解析;
不能在這個(gè)方法中給XIB里自定義view的子控件進(jìn)行初始化,因?yàn)閕nitWithCoder:方法是
‘處于正在初始化’,有些細(xì)節(jié)還沒有初始化完畢,可能還沒給子控件進(jìn)行連線等,
但是可以在initWithCoder:方法里對自定義View進(jìn)行初始化,但不能設(shè)置View的Frame值,
且一般XIB的初始化操作在awakeFromNib里進(jìn)行
(2).如過還需要用代碼添加子控件,可以通過重寫initWithCoder:方法,
在方法里面用代碼添加子控件和初始化,添加的子控件的Frame值也要在layoutSubviews方法里設(shè)置。
(3).當(dāng)控件從XIB中創(chuàng)建完畢后會(huì)調(diào)用awakeFromNib方法,XIB的所有的初始化操作應(yīng)該在這個(gè)方法里進(jìn)行,
但不能在這個(gè)方法中對子控件設(shè)置Frame的值
(4).如果需要重新設(shè)置子控件的Frame值,應(yīng)該在layoutSubviews方法里進(jìn)行設(shè)置,
因?yàn)楦缚丶腇rame只要改變就就會(huì)調(diào)用該方法
(5).用XIB封裝自定義的View,控件從XIB中創(chuàng)建的過程不會(huì)調(diào)用init方法和initWithFrame:方法
(使用方式二方法創(chuàng)建)
(6).XIB里自定義View必須設(shè)置成自動(dòng)布局,即把View的 ‘Show the File inspector’ 里面的 ‘Use Auto Layout’ 前面的鉤去掉;這樣設(shè)置后才可以在layoutSubviews里重新設(shè)置自定義View的子控件Button的Frame值
小結(jié)
一個(gè)控件有兩種創(chuàng)建方式
通過代碼創(chuàng)建
初始化一定會(huì)調(diào)用 -(instancetype)initWithFrame:方法
通過Xib\StoryBoard創(chuàng)建
如果使用xib/storyboard方式創(chuàng)建控件,那么在創(chuàng)建時(shí)一定會(huì)調(diào)用initWithCoder:方法。
初始化完成之后,回調(diào)用awakeFromNib方法
通過兩種加載方式,可以發(fā)現(xiàn):有時(shí)候我們希望在控件初始化時(shí)做一些初始化的操作,
如添加子控件,設(shè)置屬性等,這時(shí)候需要根據(jù)控件的加載方式來選擇
-(instancetype)initWithFrame:,
-(instancetype)initWithCoder:,
awakeFromNib
三個(gè)方法中的哪個(gè)方法進(jìn)行初始化。
常見問題
①使用xib關(guān)聯(lián)自定義view中, 方式一的創(chuàng)建方法可能出現(xiàn)修改不了frame的問題
HotProductView * proView = [[HotProductView alloc]initWithFrame:CGRectMake(x, y, w, h)];
這樣創(chuàng)建自定義view設(shè)置frame時(shí)發(fā)現(xiàn)設(shè)置不起作用或者不對, 解決辦法是 在-(void)drawRect:(CGRect)rect里面重新設(shè)置frame
HotProductView.m
@interface HotProductView ()
{
CGRect myframe;
}
@end
@implementation HotProductView
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSArray *nibs=[[NSBundle mainBundle]loadNibNamed:@"HotProductView" owner:nil options:nil];
self=[nibs objectAtIndex:0];
myframe = frame;
}
return self;
}
-(void)drawRect:(CGRect)rect {
self.frame=myframe;//關(guān)鍵點(diǎn)在這里
}
@end
②使用xib關(guān)聯(lián)自定義view中, 方式二的創(chuàng)建方法
在initWithCoder:里面訪問屬性,比如self.button,會(huì)發(fā)現(xiàn)它是nil的,因?yàn)榇藭r(shí)自定義控件正在初始化,self.button可能還未賦值(self.button是一個(gè)IBOutlet,IBOutlet本質(zhì)上就相當(dāng)于Xcode找到這個(gè)對應(yīng)的屬性,然后UIButton button = … , [self.view addSubview: button]這種操作,而這一切的操作都是相當(dāng)于在CYLView view = [[CYLView alloc] initWithCoder: nil]方法之后執(zhí)行的。上面的代碼就相當(dāng)于用代碼的方式實(shí)現(xiàn)Xcode在storyboard中加載CYLView),所以如果在這個(gè)方法中進(jìn)行初始化操作是可能會(huì)失敗的。
所以建議在awakeFromNib方法中進(jìn)行初始化的額外操作。因?yàn)閍wakeFromNib是在初始化完成后調(diào)用,所以在這個(gè)方法里面訪問屬性(IBOutlet)就可以保證不為nil。
---------- 數(shù)據(jù)模型的.h文件
@interface SSData : NSObject
@property (nonatomic,strong) NSString *buttonStr;
@property (nonatomic,strong) NSString *lableStr;
@end
---------- 數(shù)據(jù)模型的.m文件
#import "SSData.h"
@implementation SSData
@end
---------- 新建自定義控件類的.h文件
#import "SSData.h"
@interface ANewView : UIView
@property (nonatomic,strong) SSData *data;
+ (instancetype)aNewView;//方式二創(chuàng)建類的方法
@end
---------- 新建自定義控件類的.m文件
#import "ANewView.h"
@interface ANewView ()
@property (weak, nonatomic) IBOutlet UIButton *button;
@property (nonatomic,weak) IBOutlet UILabel *lable;
@property (nonatomic,strong) UIImageView *imageView;
@end
@implementation ANewView
+ (instancetype)aNewView {
//UINib *nib = [UINib nibWithNibName:NSStringFromClass(self) bundle:nil];
//ANewView *view = [[nib instantiateWithOwner:nil options:nil]lastObject];
return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];;
}
//重寫initWithCoder:用代碼添加子控件
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
//可以用代碼創(chuàng)建子控件并對其初始化
self.imageView = [[UIImageView alloc] init];
[self addSubview:self.imageView];
self.imageView.backgroundColor = [UIColor yellowColor];
}
return self;
}
//當(dāng)XIB完全初始化完畢后,會(huì)調(diào)用這個(gè)方法,可以在這個(gè)方法中對XIB的子控件進(jìn)行初始化
-(void)awakeFromNib {
//一定要調(diào)用父類的awakeFromNib方法
[super awakeFromNib];
self.button.backgroundColor = [UIColor yellowColor];
self.lable.backgroundColor = [UIColor orangeColor];
}
//給子控件重新布局
- (void)layoutSubviews {
//一定要調(diào)用父類的layoutSubviews方法
[super layoutSubviews];
//設(shè)置子控件的Frame
CGFloat superW = self.frame.size.width;
CGFloat superH = self.frame.size.height;
//XIB里自定義View必須設(shè)置成自動(dòng)布局,不然Button的Frame不能成功賦值
self.button.frame = CGRectMake(0, 0, superW, superH / 3 - 5);
CGFloat lableY = self.button.frame.origin.y + self.button.frame.size.height + 5;
self.lable.frame = CGRectMake(0, lableY, superW, superH / 3 - 5);
CGFloat imageY = self.lable.frame.origin.y + self.lable.frame.size.height + 5;
self.imageView.frame = CGRectMake(0,imageY, superW, superH / 3 - 5);
}
//重寫set方法給子控件設(shè)置數(shù)據(jù)
- (void)setData:(SSData *)data {
//必須先給成員變量賦值,不賦值,以后調(diào)用get方法取值就取不到值
_data = data;
//設(shè)置成員變量的數(shù)據(jù)
[self.button setTitle:data.buttonStr forState:UIControlStateNormal];
self.lable.text = data.lableStr;
}
@end
---------- XIB封裝的自定義View的調(diào)用
#import "ViewController.h"
#import "ANewView.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//數(shù)據(jù)模型
SSData *dataView = [[SSData alloc] init];
dataView.buttonStr = @"Hello";
dataView.lableStr = @"World";
//創(chuàng)建View
ANewView *newView = [ANewView aNewView];
newView.frame = CGRectMake(100,100, 200, 100);
newView.data = dataView;
[self.view addSubview:newView];
//重新給View對象賦值
dataView.buttonStr = @"天天";
dataView.lableStr = @"編程";
newView.frame = CGRectMake(80,200, 250, 200);
newView.data = dataView;
}
@end
后續(xù)如果有問題會(huì)繼續(xù)補(bǔ)充.....
最后介紹一下 drawRect:和layoutSubview的區(qū)別
layoutSubviews方便數(shù)據(jù)計(jì)算,drawRect方便視圖重繪。
我是楚簡約,感謝您的閱讀,
喜歡就點(diǎn)個(gè)贊唄,“?喜歡”,
鼓勵(lì)又不花錢,你在看,我就繼續(xù)寫~
非簡書用戶,可以點(diǎn)右上角的三個(gè)“...”,然后"在Safari中打開”,就可以點(diǎn)贊咯~