使用代碼自定義UIView注意一二三

當(dāng)一撮樣式一樣的視圖在工程中被多次使用的時候,為了方便使用,我們會想把他們抽成一個單獨(dú)的類,進(jìn)行視圖的自定義.

比如我們要做一個這樣的東西:


這一塊由兩個東西組成:一個imageView和一個label。首先我們新建一個繼承自UIView的類MyView.

在MyView的.m文件里,你可以根據(jù)自己的意愿將兩個子控件設(shè)置成MyView的屬性或者成員變量,這里我們設(shè)置為屬性。

@property (nonatomic, strong) UIImageView *imageView;

@property (nonatomic, strong) UILabel *label;

那接下來,就是要向自定義的view里面添加控件咯。

通常的思路是重寫UIView的構(gòu)造方法。那么這里要說第一個注意了:

1.要重寫UIView的initWithFrame:方法而不是init方法

為什么呢?因?yàn)楫?dāng)外部調(diào)用init的方法的時候,其內(nèi)部也會默默地調(diào)用initWithFrame:方法,你不能保證別的同事在調(diào)用你的類的時候不會直接調(diào)用initWithFrame:方法,這時如果你僅重寫了init方法,那么兩個子控件便無從創(chuàng)建.

于是我們寫成這樣:

- (instancetype)initWithFrame:(CGRect)frame {

if (self = [super initWithFrame:frame]) {

/* 添加子控件的代碼*/

}

return self;

}


接下開始添加子控件,不知道還會不會有小伙伴是這樣寫的:

- (instancetype)initWithFrame:(CGRect)frame {

if (self = [super initWithFrame:frame]) {

self.imageView = [[UIImageView alloc]init];

self.imageView.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.width);

[self addSubview:self.imageView];

}

return self;

}

這樣有什么問題嗎?如果全部寫好運(yùn)行起來,能看到imageView嗎?答案是不確定的.問題出現(xiàn)在給imageView的frame賦值那里.

imageView的寬和高直接用self.frame.size.width,但這個時候self.frame可能是沒有值的.

上面我們說過,如果外部調(diào)用了MyView的init方法,也會執(zhí)行到這里,這時候frame還沒有賦值.

所以第二個注意:

2.不要在構(gòu)造方法里面直接取自身(self,或者說本視圖)的寬高,這時候取到的寬高是不準(zhǔn)的.

我想初學(xué)自定義tableViewCell的小伙伴都遇到過類似這樣的問題:

重寫cell的初始化方法向cell內(nèi)添加子控件時

(假設(shè)cell的高度設(shè)為100,想要添加一個label在cell的底部),

于是這樣寫:

label.frame = CGRectMake(0,self.frame.size.height - 20, 100, 20),

運(yùn)行出來卻發(fā)現(xiàn)添加的label并不在我們期望的位置(底部),

而是在cell比較偏上的位置(實(shí)際y的值是44-20而不是100-20).

然后在debug的時候發(fā)現(xiàn):雖然cell的高度已經(jīng)設(shè)定成為100,但在初始化方法里面取到的cell的高度仍然是默認(rèn)的44.

這其實(shí)也是剛才說的原因?qū)е碌?我們不能在控件的構(gòu)造方法里面取其frame或者bounds,這時候取值是不準(zhǔn)確的.

所以在重新構(gòu)造方法的時候,我們只需要把控件放進(jìn)去,暫時先不用考慮他們在什么位置:

- (instancetype)initWithFrame:(CGRect)frame {

if (self = [super initWithFrame:frame]) {

self.imageView = [[UIImageView alloc]init];

[self addSubview:self.imageView];

self.label = [[UILabel alloc]init];

self.label.textAlignment = NSTextAlignmentCenter;

[self addSubview:self.label];

}

return self;

}

那么在什么時候設(shè)置子控件的frame呢?

第三個注意:

3.在layoutSubViews方法里面布局子控件

如下:

- (void)layoutSubviews {

// 一定要調(diào)用super的方法

[super layoutSubviews];

// 確定子控件的frame(這里得到的self的frame/bounds才是準(zhǔn)確的)

CGFloat width = self.bounds.size.width;

CGFloat height = self.bounds.size.height;

self.imageView.frame = CGRectMake(0, 0, width, width);

self.label.frame = CGRectMake(0, width, width, height - width);

}

這里要注意的就是需要在布局之前一定要先調(diào)用父類的layoutSubviews方法.

由于在這個方法里可以獲取MyView準(zhǔn)確的寬和高,我們直接取它的寬高來設(shè)置imageView和label的寬高就可以

當(dāng)然,子控件的創(chuàng)建不一定要寫在MyView的構(gòu)造方法里面,既然聲明成為屬性,使用懶加載(重寫屬性的get方法)也是一個不錯的選擇.

作者:CoderAO

鏈接:http://www.itdecent.cn/p/68b383b129f9

來源:簡書

著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容