FlexBox - YogaKit
FlexBox是一套通用的布局協(xié)議,YogaKit實現(xiàn)了這個協(xié)議,iOS端可以使用YogaKit來實現(xiàn)FlexBox布局。FlexBox和UIStackView以及Android的LineLayout有相通的地方,優(yōu)勢在于FlexBox是跨平臺的,功能上也更強一點。
YogaKit依據(jù)你的設(shè)置計算出相關(guān)的view的frame,直接設(shè)置frame,所以和AutoLayout可以混合使用,對同一個view進(jìn)行設(shè)置,以AutoLayout的設(shè)置為準(zhǔn)。
YogaKit是從從上往下進(jìn)行計算的,使用過程中需要保證flex container的frame有值,這樣它的flex item才會計算出frame,否則都是CGRectZero。
flex direction
布局延伸的方向,確定了主軸和副軸,添加的元素沿著主軸的方向進(jìn)行排列。
-
Row
水平方向從左往右進(jìn)行延伸,主軸為水平方向,副軸為豎直方向
-
Row Reverse
水平方向從左往右進(jìn)行延伸,主軸為水平方向,副軸為豎直方向
-
Column
豎直方向從上往下進(jìn)行延伸,主軸為豎直方向,副軸為水平方向
-
Column Reverse
豎直方向從下往上進(jìn)行延伸,主軸為豎直方向,副軸為水平方向
justify & align-items
justify 進(jìn)一步明確了元素在主軸方向如何排列,align-items 進(jìn)一步明確了元素在副軸方向如何排列
- flex start
- center
- end
- space between
- space around
- space evenly
- stretch
flex-wrap (適用于父類容器上)
設(shè)置或檢索伸縮盒對象的子元素超出父容器時是否換行
padding & margin
iOS 上 padding 對應(yīng)的是 content inserts,但是 iOS 大部分控件都沒有padding,相信很多人都寫過一個繼承自 UILabel的控件來提供設(shè)置 content inserts 的控件,有了YogaKit,那個類以后用不上了。
padding 指的是自身內(nèi)邊距,margin 指的是外邊距,@"H:|-20-[redView]-20-|",這條VFL里的20就是margin。
display
是否顯示這個元素,如果為none,則不顯示,也不參與計算
markDirty
標(biāo)記元素需要重新計算位置,只對葉子節(jié)點生效。
// 獲取驗證碼 -> 重發(fā)
- (void)codeButtonClicked:(UIButton *)button {
[button setTitle:@"重發(fā)" forState:UIControlStateNormal];
button.superview.yoga.marginTop = YGPointValue(0);
[button.yoga markDirty];
[button.superview.yoga applyLayoutPreservingOrigin:YES];
}
flexGrow
flexGrow決定剩余空間怎么分配,含義類似于layout_weight.如果flex item的flexGrow為0,該flex item不會占用剩余的空間。如果多個flex item的flexGrow不為0,則按flexGrow的值按比例進(jìn)行劃分。
flexShrink
flexShrink決定空間不足,怎么縮放
align-items、align-self、align-content、
-
align-item
屬性需要施加在 flex 容器上,它規(guī)定的是 flex 容器中所有 item 在副軸中的對齊方式
// flex item在副軸拉伸填滿剩余空間 layout.alignItems = YGAlignStretch; -
align-self
屬性則施加在 flex 容器中的 item 上,允許單個項目有與其他項目不一樣的對齊方式,它覆蓋了外部容器規(guī)定的 align-items 屬性,同樣也只規(guī)定在交叉軸上的對齊方式
-
align-content
對比 align-items 和 align-self 直接移動 item 自身在交叉軸上的基線,align-content 移動的是容器自身的 flex line,并僅對多行的項目起作用
常見問題
-
Pod
使用
pod 'YogaKit'安裝的時候,這個庫包含了一個swift文件,對于純OC的項目,這會報錯。可以改成
pod 'IGListKit','2.1.0' pod 'Yoga','1.14.0'然后將YogaKit的代碼(刪除掉swfit文件后)copy到項目中,這部分不使用cocopods進(jìn)行管理,這樣改動比較小
-
AutoLayout
YogaKit布局設(shè)置的是view的frame,所以如果對視圖添加約束,視圖最終的大小以約束設(shè)置為準(zhǔn)。
涉及到其他的視圖時,參與計算的是YogaKit自己計算出來的view.frame,而不是約束設(shè)置的值。
實踐
標(biāo)簽流式布局
- (void)tagLayout {
NSArray *tags = @[@"投資理財",@"超高收益",@"七日年化收益",@"支付寶",@"微信",@"云閃付",@"花唄"];
UIView *tagBgView = [[UIView alloc] init];
tagBgView.backgroundColor = [UIColor lightGrayColor];
[tagBgView configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
layout.isEnabled = YES;
layout.flexDirection = YGFlexDirectionRow;
layout.marginTop = YGPointValue(100);
layout.paddingBottom = YGPointValue(10);
layout.width = YGPointValue([UIScreen mainScreen].bounds.size.width);
// 不設(shè)置高度,讓高度自適應(yīng)
layout.flexWrap = YGWrapWrap;
}];
[self.view addSubview:tagBgView];
for (NSString *obj in tags) {
UILabel *label = [[UILabel alloc] init];
label.text = obj;
label.backgroundColor = [UIColor orangeColor];
[label configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
layout.isEnabled =YES;
layout.marginLeft = YGPointValue(10);
layout.marginTop = YGPointValue(10);
}];
[tagBgView addSubview:label];
}
self.view.yoga.isEnabled = YES;
[self.view.yoga applyLayoutPreservingOrigin:NO];
}
<img src="https://i.loli.net/2019/10/10/sdIMwUluPVoCBrN.png" alt="截屏2019-10-10下午5.30.21.png" style="zoom:50%;" />
更新布局
很多時候,我們的視圖大小是依據(jù)視圖內(nèi)容來決定的,比如按鈕的寬依據(jù)title進(jìn)行調(diào)整,title變了,寬也要變,暫時只找到用markDirty實現(xiàn)的方法。
FlexBox是一套通用的布局協(xié)議,YogaKit實現(xiàn)了這個協(xié)議,iOS端可以使用YogaKit來實現(xiàn)FlexBox布局。FlexBox和UIStackView以及Android的LineLayout有相通的地方,優(yōu)勢在于FlexBox是跨平臺的,功能上也更強一點。
YogaKit依據(jù)你的設(shè)置計算出相關(guān)的view的frame,直接設(shè)置frame,所以和AutoLayout可以混合使用,對同一個view進(jìn)行設(shè)置,以AutoLayout的設(shè)置為準(zhǔn)。
YogaKit是從從上往下進(jìn)行計算的,使用過程中需要保證flex container的frame有值,這樣它的flex item才會計算出frame,否則都是CGRectZero。
flex direction
布局延伸的方向,確定了主軸和副軸,添加的元素沿著主軸的方向進(jìn)行排列。
-
Row
水平方向從左往右進(jìn)行延伸,主軸為水平方向,副軸為豎直方向
-
Row Reverse
水平方向從左往右進(jìn)行延伸,主軸為水平方向,副軸為豎直方向
-
Column
豎直方向從上往下進(jìn)行延伸,主軸為豎直方向,副軸為水平方向
-
Column Reverse
豎直方向從下往上進(jìn)行延伸,主軸為豎直方向,副軸為水平方向
justify & align-items
justify 進(jìn)一步明確了元素在主軸方向如何排列,align-items 進(jìn)一步明確了元素在副軸方向如何排列
- flex start
- center
- end
- space between
- space around
- space evenly
- stretch
flex-wrap (適用于父類容器上)
設(shè)置或檢索伸縮盒對象的子元素超出父容器時是否換行
padding & margin
iOS 上 padding 對應(yīng)的是 content inserts,但是 iOS 大部分控件都沒有padding,相信很多人都寫過一個繼承自 UILabel的控件來提供設(shè)置 content inserts 的控件,有了YogaKit,那個類以后用不上了。
padding 指的是自身內(nèi)邊距,margin 指的是外邊距,@"H:|-20-[redView]-20-|",這條VFL里的20就是margin。
display
是否顯示這個元素,如果為none,則不顯示,也不參與計算
markDirty
標(biāo)記元素需要重新計算位置,只對葉子節(jié)點生效。
// 獲取驗證碼 -> 重發(fā)
- (void)codeButtonClicked:(UIButton *)button {
[button setTitle:@"重發(fā)" forState:UIControlStateNormal];
button.superview.yoga.marginTop = YGPointValue(0);
[button.yoga markDirty];
[button.superview.yoga applyLayoutPreservingOrigin:YES];
}
flexGrow
flexGrow決定剩余空間怎么分配,含義類似于layout_weight.如果flex item的flexGrow為0,該flex item不會占用剩余的空間。如果多個flex item的flexGrow不為0,則按flexGrow的值按比例進(jìn)行劃分。
flexShrink
flexShrink決定空間不足,怎么縮放
align-items、align-self、align-content、
-
align-item
屬性需要施加在 flex 容器上,它規(guī)定的是 flex 容器中所有 item 在副軸中的對齊方式
// flex item在副軸拉伸填滿剩余空間 layout.alignItems = YGAlignStretch; -
align-self
屬性則施加在 flex 容器中的 item 上,允許單個項目有與其他項目不一樣的對齊方式,它覆蓋了外部容器規(guī)定的 align-items 屬性,同樣也只規(guī)定在交叉軸上的對齊方式
-
align-content
對比 align-items 和 align-self 直接移動 item 自身在交叉軸上的基線,align-content 移動的是容器自身的 flex line,并僅對多行的項目起作用
常見問題
-
Pod
使用
pod 'YogaKit'安裝的時候,這個庫包含了一個swift文件,對于純OC的項目,這會報錯。可以改成
pod 'IGListKit','2.1.0' pod 'Yoga','1.14.0'然后將YogaKit的代碼(刪除掉swfit文件后)copy到項目中,這部分不使用cocopods進(jìn)行管理,這樣改動比較小
-
AutoLayout
YogaKit布局設(shè)置的是view的frame,所以如果對視圖添加約束,視圖最終的大小以約束設(shè)置為準(zhǔn)。
涉及到其他的視圖時,參與計算的是YogaKit自己計算出來的view.frame,而不是約束設(shè)置的值。
實踐
標(biāo)簽流式布局
- (void)tagLayout {
NSArray *tags = @[@"投資理財",@"超高收益",@"七日年化收益",@"支付寶",@"微信",@"云閃付",@"花唄"];
UIView *tagBgView = [[UIView alloc] init];
tagBgView.backgroundColor = [UIColor lightGrayColor];
[tagBgView configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
layout.isEnabled = YES;
layout.flexDirection = YGFlexDirectionRow;
layout.marginTop = YGPointValue(100);
layout.paddingBottom = YGPointValue(10);
layout.width = YGPointValue([UIScreen mainScreen].bounds.size.width);
// 不設(shè)置高度,讓高度自適應(yīng)
layout.flexWrap = YGWrapWrap;
}];
[self.view addSubview:tagBgView];
for (NSString *obj in tags) {
UILabel *label = [[UILabel alloc] init];
label.text = obj;
label.backgroundColor = [UIColor orangeColor];
[label configureLayoutWithBlock:^(YGLayout * _Nonnull layout) {
layout.isEnabled =YES;
layout.marginLeft = YGPointValue(10);
layout.marginTop = YGPointValue(10);
}];
[tagBgView addSubview:label];
}
self.view.yoga.isEnabled = YES;
[self.view.yoga applyLayoutPreservingOrigin:NO];
}

更新布局
很多時候,我們的視圖大小是依據(jù)視圖內(nèi)容來決定的,比如按鈕的寬依據(jù)title進(jìn)行調(diào)整,title變了,寬也要變,暫時只找到用markDirty實現(xiàn)的方法。