前言
iOS開(kāi)發(fā)布局有多套方案可以實(shí)現(xiàn),大致有幾種方法。NSLayoutConstraint, Masonry, Snapkit(Swift環(huán)境布局框架),也可以用setframe來(lái)實(shí)現(xiàn)簡(jiǎn)單的布局。
- NSLayoutConstraint:蘋(píng)果官方推出的約束方式,在XIB或故事板中可以使用拖拽快速實(shí)現(xiàn),純代碼實(shí)現(xiàn)起來(lái)顯得相當(dāng)繁瑣。
- Masony: 一款經(jīng)典的三方布局框架,由Objective-C語(yǔ)言編寫(xiě),適用于純代碼布局,相對(duì)于官方的NSLayoutConstraint是用代碼布局而言,可以在一個(gè)block中實(shí)現(xiàn)對(duì)單一控件實(shí)現(xiàn)完整布局,有著很高的布局效率,缺點(diǎn)是有侵入性,代碼的可以閱讀性不夠好。
- Snapkit: Swift版本的Masony,適用于由Swift語(yǔ)言搭建的項(xiàng)目,布局是在閉包中完成的。
- setFrame方法:利用系統(tǒng)的setFrame方法,可以實(shí)現(xiàn)快速的控件靜態(tài)布局。setFrame方式需要準(zhǔn)確的計(jì)算出控件的origin(原點(diǎn)坐標(biāo)),size(尺寸),繁瑣的計(jì)算導(dǎo)致該方法在使用中顯得不夠靈活,在屏幕適配中顯得很麻煩。
- 還有其他的一些布局方式,github上可以找到很多類(lèi)型的解決方案。有的太重量級(jí),有的太繁瑣,有的和系統(tǒng)布局的兼容性存在問(wèn)題。
我的Idea
根據(jù)多年的iOS開(kāi)發(fā)經(jīng)驗(yàn)。我自己對(duì)setFrame做了一個(gè)簡(jiǎn)單的封裝,實(shí)現(xiàn)了對(duì)控件的簡(jiǎn)單布局。使用起來(lái)棒棒噠,如果下列代碼能夠吸引你,那么請(qǐng)繼續(xù)看下去:
[_detailAddressLabel setFrameFromLayout:^(UIViewVirtualLayout *layout) {
layout.size = CGSizeMake(80.0, 40.0);
layout.yCenter = cell.contentView.h / 2.0;
layout.x = 15.0;
}];
很顯然的,上述代碼其實(shí)就是被我含蓄地setFrame了,但是我?guī)缀鯖](méi)有做什么計(jì)算我就把這個(gè)布局給完成了。一個(gè)地址Label,設(shè)置它的尺寸為 80 * 40, 縱坐標(biāo)中心與它所在的cell的中心對(duì)齊,橫坐標(biāo)為15,是不是很簡(jiǎn)單呢。
我們?cè)賮?lái)看看Masony是怎么布局的;
[_detailAddressLabel makeConstraints:^(MASConstraintMaker *make) {
make.width.equalTo(80.0);
make.height.equalTo(40.0);
make.left.equalTo(cell.contentView.left).offset(15.0);
make.centerY.equalTo(cell.contentView);
}];
看起來(lái)出入不大,但是我可以告訴你,從程序的運(yùn)行角度來(lái)說(shuō),通過(guò)我封裝UIViewVirtualLayout方式來(lái)實(shí)現(xiàn)對(duì)控件的布局運(yùn)行效率要高很多,關(guān)鍵是封裝原理十分淺顯易懂。 極大地降低了程序的體積,還降低了冗余。
當(dāng)然setFrame的布局是靜態(tài)的,如果控件相對(duì)于參照控件,布局是發(fā)生變化的,就需要在變化時(shí)去調(diào)用一下該方法,比如在tableViewCell或collectionViewCell中,就可以在cell的layoutSubViews方法中去調(diào)用setFrameFromLayout:方法。這個(gè)方法可用于創(chuàng)建布局和刷新布局,免去了像Masony中UpdateConstrains方法的麻煩。
實(shí)現(xiàn)
具體實(shí)現(xiàn)簡(jiǎn)單到爆,只怕沒(méi)想到,誰(shuí)都能做到。
在實(shí)現(xiàn)中我對(duì)UIView設(shè)置了一個(gè)類(lèi)別,專(zhuān)用于對(duì)UIView及其子類(lèi)進(jìn)行改變frame,當(dāng)然一次只能改變一個(gè)參數(shù),對(duì)于改變frame中的一個(gè)參數(shù)來(lái)說(shuō),是非常高效的,免去了計(jì)算坐標(biāo)的麻煩,但是如果要一起把UIView的所有坐標(biāo)屬性設(shè)定,則需要反復(fù)調(diào)用setFrame,所以對(duì)于布局來(lái)說(shuō)這樣還不是一個(gè)很好的解決方案。為了布局的快速實(shí)現(xiàn)并提高運(yùn)行效率,這里引入了UIViewVirtualLayout類(lèi)(后面簡(jiǎn)稱(chēng)layout),我們利用layout來(lái)實(shí)現(xiàn)對(duì)frame的賦值,知道layout全部參數(shù)設(shè)定完成后再去賦值給view的frame,在這期間只對(duì)frame刷新一次。
UIView+Layout類(lèi)別中的布局實(shí)現(xiàn)代碼
- (void)setFrameFromLayout:(VirtualLayoutBlock)layoutBlock {
UIViewVirtualLayout * layout = [[UIViewVirtualLayout alloc] init];
layout.frame = self.frame;
layoutBlock(layout);
[self setFrame:layout.frame];
}
顯然的,layout相當(dāng)于是一個(gè)布局的數(shù)據(jù)緩存,當(dāng)他設(shè)定好值,再把值賦值給view的frame,完成它的使命。
代碼已經(jīng)上傳到github,具體實(shí)現(xiàn)請(qǐng)參見(jiàn) https://github.com/cba023/QuicklySetFrame
謝謝。
2018-05-17