iOS 自定義view創(chuàng)建和使用問題積累

自定義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];
}
2.創(chuàng)建xib.png
3.關(guān)聯(lián)xib.png

使用關(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)贊咯~


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,546評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,570評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,505評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,017評論 1 313
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,786評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,219評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,287評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,438評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,971評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,796評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,995評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,540評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,230評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,918評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,697評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內(nèi)容