級別: ★☆☆☆☆
標簽:「iOS布局」「iOS frame」「iOS frame bounds」
作者: Xs·H
審校: QiShare團隊
在 沐靈洛 線下分享iOS UIButton根據內容自動布局時,有和前端同學討論到iOS的常用布局方式。討論過程十分熱鬧,不容易記錄,但作者認為討論結果有必要記錄一下,希望能幫助到一些同學。
作者將iOS常用布局方式歸納為Frame、Autoresizing、Constraint、StackView和Masonry五種,并將逐一介紹。
本篇文章介紹Frame
。
frame
是UIView
的屬性,用來描述UIView
及其子類所表示的視圖的位置(origin
)和大小(size
)。frame
是iOS
布局中最基本、最常用和最容易被開發者接受的布局方式。一般來說,可以通過以下方式很方便地創建并顯示一個視圖,如下。
- (void)viewDidLoad {
[super viewDidLoad];
_contentView = [[QiFrameContentView alloc] initWithFrame:self.view.bounds];
_contentView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:_contentView];
}
在上述代碼中,作者在viewDidLoad
中,將一個淺灰色的contentView
添加到了self.view
上,并將其frame
設置為了self.view.bounds
。顯然,作者希望淺灰色的contentView
完全蓋住self.view
(默認白色)。使用模擬器運行一下,初始效果如作者所愿,但將模擬器旋轉方向后,淺灰色的contentView
沒有一起旋轉,在右側漏出了一部分白色的self.view
,如下。
上述現象其實是frame特性的一種表現。在僅使用frame來布局視圖時,視圖的位置和大小是被唯一確定了的,不會跟隨父視圖的變化而變化,除非在某個時間點再次設置了frame。
作者希望contentView
可以跟著self.view
一起旋轉,始終保持完全覆蓋的效果,于是做了如下修改。
- (void)viewDidLoad {
[super viewDidLoad];
_contentView = [[QiFrameContentView alloc] initWithFrame:CGRectZero];
_contentView.backgroundColor = [UIColor lightGrayColor];
[self.view addSubview:_contentView];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
_contentView.frame = self.view.bounds;
}
為實現想要的小伙,作者在viewWillLayoutSubviews
方法中重新設置了contentView
的frame
,并將viewDidLoad
中初始化contentView
時設置的frame
改為了CGRectZero
。因為在每一次self.view的frame變化后和self.view的子view發生變化前都會觸發viewWillLayoutSubviews方法。
借鑒上面的原理,作者在contentView
上添加4
個subView
,實現4
等分的效果,如下圖。
實現上圖效果的代碼如下:
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
_subView1 = [[UIView alloc] initWithFrame:CGRectZero];
_subView1.backgroundColor = [[UIColor redColor] colorWithAlphaComponent:.6];
[self addSubview:_subView1];
_subView2 = [[UIView alloc] initWithFrame:CGRectZero];
_subView2.backgroundColor = [[UIColor greenColor] colorWithAlphaComponent:.6];
[self addSubview:_subView2];
_subView3 = [[UIView alloc] initWithFrame:CGRectZero];
_subView3.backgroundColor = [[UIColor blueColor] colorWithAlphaComponent:.6];
[self addSubview:_subView3];
_subView4 = [[UIView alloc] initWithFrame:CGRectZero];
_subView4.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:.6];
[self addSubview:_subView4];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat margin = 20.0;
CGFloat padding = 20.0;
CGFloat width = (self.bounds.size.width - margin * 2 - padding) / 2;
CGFloat height = (self.bounds.size.height - margin * 2 - padding) / 2;
_subView1.frame = CGRectMake(margin, margin, width, height);
_subView2.frame = CGRectMake(margin + width + padding, margin, width, height);
_subView3.frame = CGRectMake(margin, margin + height + padding, width, height);
_subView4.frame = CGRectMake(margin + width + padding, margin + height + padding, width, height);
/*
_subView4.qi_width = width;
_subView4.qi_height = height;
_subView4.qi_top = _subView3.qi_top;
_subView4.qi_left = _subView3.qi_right + padding;
*/
}
其中,通過打點可知contentView
中的layoutSubviews
方法與viewController
中的viewWillLayoutSubviews
方法成對觸發,并且layoutSubviews
晚于后者。
PS:bounds與frame有一定區別。
bounds只用來描述視圖的尺寸,就像一頁A4紙,不論把它放在桌子上還是地板上,它的bounds都不發生變化。
frame除了能夠描述視圖的尺寸外還能描述視圖的位置。再如A4紙,從桌子上挪到地板上,它的frame就發生變化了。
另外,為了更方便、直觀地使用frame
布局視圖,可以使用類似上面代碼中注釋的代碼形式。具體的實現細節,可以在QiLayoutDemo中查看。