flexBox布局之yogakit

flexBox布局之yogakit

本次分享目的:

介紹一種有別于frame手算布局的布局框架,更快速,大大提升產(chǎn)生力

背景:

  • 多終端,多尺寸設(shè)備的現(xiàn)狀使得UI布局越來(lái)越重要

隨著這幾年前端技術(shù)的崛起,前端UI布局系統(tǒng)也在其中占據(jù)了越來(lái)越重要的位置。不管是在移動(dòng)端、桌面端還是Web端,特別是不同設(shè)備的屏幕大小和分辨率千變?nèi)f化,如何構(gòu)建良好的布局系統(tǒng)得越來(lái)越重要。

  • 現(xiàn)狀:各平臺(tái)各自布局方案,維護(hù)成本高,無(wú)法共享

目前的情況是各個(gè)平臺(tái)都有自己的一套解決方案。iOS平臺(tái)有自動(dòng)布局系統(tǒng),Android有容器布局系統(tǒng),而Web端有基于CSS的布局系統(tǒng)。多種布局系統(tǒng)共存所帶來(lái)的弊端是很明顯的,平臺(tái)間的共享變得很困難,而每個(gè)平臺(tái)都需要專人來(lái)開(kāi)發(fā)維護(hù),增加了開(kāi)發(fā)成本。

  • React Native中實(shí)現(xiàn)了一種跨平臺(tái)的基于CSS的布局系統(tǒng)

React Native里引入了一種跨平臺(tái)的基于CSS的布局系統(tǒng),它實(shí)現(xiàn)了Flexbox規(guī)范。是他的一個(gè)子集

  • 不斷完善發(fā)展后形成了一款跨平臺(tái)的布局引擎:Yoga

隨著這個(gè)系統(tǒng)的不斷完善,F(xiàn)acebook對(duì)它進(jìn)行重啟發(fā)布,并借助Yoga,不僅可以在React Native里,還能在各個(gè)平臺(tái)上快速地構(gòu)建UI布局。

Yoga簡(jiǎn)單介紹:

Yoga是基于C實(shí)現(xiàn)的?;贑實(shí)現(xiàn)的Yoga比之前Java實(shí)現(xiàn)在性能上提升了33%。使用C實(shí)現(xiàn)可以更容易地跟其它平臺(tái)集成。到目前為止,Yoga已經(jīng)有以下幾個(gè)平臺(tái)的綁定:Java(Android)、Objective-C(UIKit)、C#(.NET)。而且已經(jīng)有很多項(xiàng)目在使用Yoga,比如React Native、Components for Android、weex, luaView等等。

不同于其它的一些布局框架,比如Masonry,它們要么不夠強(qiáng)大,要么不支持跨平臺(tái)。Yoga遵循了Flexbox規(guī)范,同時(shí)又將布局元素抽象,為各個(gè)不同平臺(tái)暴露出一組標(biāo)準(zhǔn)的接口,這樣不同的平臺(tái)只需實(shí)現(xiàn)這些接口就可以了。

Facebook把Yoga開(kāi)源了,除了完全遵循Flexbox規(guī)范,在未來(lái)為Yoga加入更多特性,這些特性將超出Flexbox的范疇。

Yoga官方網(wǎng)站:https://facebook.github.io/yoga/

Flexbox布局概念:

Flexbox布局( Flexible Box 或CSS3 彈性布局),是CSS3中的一種新的布局模式。使用Flexbox來(lái)布局更容易,可以使用更少的代碼,更簡(jiǎn)單的方式實(shí)現(xiàn)更復(fù)雜的布局,例如對(duì)齊方式,排列方向,排列順序(這也是Flexbox布局的核心能力所在),彈性盒中的子元素通過(guò)在各個(gè)方向放置就可以以彈性的尺寸適應(yīng)父元素的顯示區(qū)域。獨(dú)立顯示被設(shè)定成只針對(duì)可見(jiàn)元素,而不是基于代碼的聲明和導(dǎo)航順序。

image

Flexbox布局教學(xué):http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html

YogaKit使用:

iOS 中使用 YogaKit,它是 C 實(shí)現(xiàn)的一個(gè)封裝。

YogaKit 將 YGLayout 暴露作為 UIView 的一個(gè)分類。這個(gè)分類在 UIView 中增加了一個(gè) configureLayout(block:) 方法。block 閉包帶一個(gè) YGLayout 參數(shù),你可以用這些數(shù)據(jù)來(lái)配置 view的布局屬性。

一些基本的布局屬性:

justifyContent : item項(xiàng)目 在容器主軸方向上的對(duì)齊

flexdirection:決定主軸的方向(即 item的排列方向)。

padding : 添加一個(gè)內(nèi)白

marginXXX: 增加一個(gè)邊距

width:寬

hight:高

alignItems:item項(xiàng)目在交叉軸上如何對(duì)齊

alignSelf:容器自身的對(duì)齊位置

通過(guò)將每個(gè)子 view 配置所需的 Yoga 屬性來(lái)構(gòu)建布局。然后,調(diào)用根 view 上的 YGLayout 的 applyLayout(preservingOrigin:) 方法。然后布局會(huì)被計(jì)算并應(yīng)用到根 view 和 subview 上。

源碼文件:

image
  • 其中yogakit目錄下是oc是接口
  • 核心文件在yoga.m中
  • 相比較masonry很輕量級(jí)
  • 可以和frame布局一起使用

demo:

  • 免去計(jì)算滾動(dòng)視圖的高度,

  • 免去計(jì)算tableview的高度;

YogaKit布局源碼解析:

oc布局執(zhí)行入口:

- (void)applyLayoutPreservingOrigin:(BOOL)preserveOrigin

執(zhí)行下面方法:

- (CGSize)calculateLayoutWithSize:(CGSize)size

要求布局必須在主線程。

然后節(jié)點(diǎn)布局計(jì)算:

 void YGNodeCalculateLayout(const YGNodeRef node,
                            const float parentWidth,
                            const float parentHeight,
                            const YGDirection parentDirection) 

這是一個(gè)包裝YGNodelayoutImpl函數(shù)。它決定了布局請(qǐng)求是否冗余,可以跳過(guò)。輸入?yún)?shù)是一樣的YGNodelayoutImpl返回參數(shù)是true的:

bool YGLayoutNodeInternal(const YGNodeRef node,
                      const float availableWidth,
                      const float availableHeight,
                      const YGDirection parentDirection,
                      const YGMeasureMode widthMeasureMode,
                      const YGMeasureMode heightMeasureMode,
                      const float parentWidth,
                      const float parentHeight,
                      const bool performLayout,
                      const char *reason,
                      const YGConfigRef config)

最后布局算法實(shí)現(xiàn):
static void YGNodelayoutImpl(const YGNodeRef node,
const float availableWidth,
const float availableHeight,
const YGDirection parentDirection,
const YGMeasureMode widthMeasureMode,
const YGMeasureMode heightMeasureMode,
const float parentWidth,
const float parentHeight,
const bool performLayout,
const YGConfigRef config)

性能比較:

image

上圖是3個(gè)布局算法在嵌套情況下的性能比較圖。FlexBox的性能接近于原生的Frame。而嵌套情況下的Autolayout的性能很差;

布局算法主要步驟:

  1. 對(duì)特殊節(jié)點(diǎn)和情況進(jìn)行預(yù)處理:
    • 內(nèi)容節(jié)點(diǎn):采用 measure 方法,通過(guò)回調(diào)視圖對(duì)文本實(shí)際計(jì)算得到的尺寸確定寬高
    • 葉子節(jié)點(diǎn):沒(méi)有子視圖的,直接求解,不進(jìn)行遞歸計(jì)算
    • 對(duì)不需要計(jì)算布局的節(jié)點(diǎn)直接求解
  2. 確定節(jié)點(diǎn)內(nèi)每個(gè)子節(jié)點(diǎn)的 flexBasis
  3. 對(duì)節(jié)點(diǎn)內(nèi)所有子節(jié)點(diǎn)遍歷,對(duì)元素分行并計(jì)算主軸和交叉軸對(duì)齊:
    • 將子節(jié)點(diǎn)分行
    • 計(jì)算當(dāng)前行內(nèi)元素在主軸上的尺寸,計(jì)算當(dāng)前行剩余可分配空間
    • 計(jì)算當(dāng)前行內(nèi)元素在主軸上的位置,計(jì)算當(dāng)前行內(nèi)元素在交叉軸上的尺寸
    • 計(jì)算當(dāng)前行內(nèi)元素在交叉軸上的位置
  4. 計(jì)算節(jié)點(diǎn)的多行對(duì)齊,更新元素在交叉軸上的位置
  5. 計(jì)算節(jié)點(diǎn)的最終尺寸和位置
  6. 計(jì)算絕對(duì)定位子節(jié)點(diǎn)的尺寸和位置
  7. 設(shè)置子節(jié)點(diǎn)的 trailing 位置

預(yù)處理:

內(nèi)容節(jié)點(diǎn):

對(duì) Label、TextField 等文本節(jié)點(diǎn)和 ImageView 等由內(nèi)容決定的節(jié)點(diǎn)直接通過(guò)外部傳入的 measure 回調(diào)拿到尺寸。

measure 方法可以通過(guò)協(xié)議讓具體的視圖來(lái)實(shí)現(xiàn)。

葉子節(jié)點(diǎn)

對(duì)沒(méi)有孩子的葉子節(jié)點(diǎn),由于不需要遞歸計(jì)算內(nèi)部子節(jié)點(diǎn)的布局,因此可以直接通過(guò)指定的模式算出估計(jì)尺寸,跳過(guò)接下來(lái)的流程。

非布局節(jié)點(diǎn)

同樣,在對(duì)布局樹(shù)的多次遞歸過(guò)程中,對(duì)于只需知道子節(jié)點(diǎn)尺寸而不需要知道位置的情況,會(huì)把 performLayout 標(biāo)志位置為 NO 來(lái)跳過(guò)計(jì)算量消耗較大的計(jì)算位置的流程。

確定 flexBasis

這一步確定容器中每個(gè)子元素的在主軸上的 flexBasis 值。flexBasis 是每個(gè)元素的在未擴(kuò)展和壓縮前的基準(zhǔn)尺寸,父容器用來(lái)計(jì)算主軸的剩余空間,然后根據(jù)擴(kuò)展和壓縮比例系數(shù)為每個(gè) Flex 子元素調(diào)整尺寸。由于絕對(duì)定位子節(jié)點(diǎn)不參與 Flex 布局,因此不需要計(jì)算 flexBasis 值,在這一步中,會(huì)先把絕對(duì)定位子節(jié)點(diǎn)存儲(chǔ)在鏈表中,在 Flex 布局完成后再單獨(dú)計(jì)算所有絕對(duì)定位節(jié)點(diǎn)的布局。

緩存機(jī)制

CSSLayout 算法中的緩存分為兩個(gè)層次,即把渲染樹(shù)中所有節(jié)點(diǎn)的布局結(jié)果和估計(jì)結(jié)果都緩存起來(lái),只有當(dāng)兩棵渲染樹(shù)計(jì)算條件完全匹配時(shí)才會(huì)觸發(fā);另一種復(fù)用要求較低的,只把中間的估算結(jié)果緩存起來(lái),內(nèi)部緩存最近 16 次的計(jì)算結(jié)果, 在渲染樹(shù)增量更新、插入節(jié)點(diǎn)等部分更新情況下避免重復(fù)估算尺寸,但對(duì)節(jié)點(diǎn)的布局仍需計(jì)算。

相關(guān)參考資料:

https://facebook.github.io/yoga/

https://juejin.im/entry/59924874518825485e1d5129

https://zhuanlan.zhihu.com/p/26856290

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

相關(guān)閱讀更多精彩內(nèi)容

  • FlexBox - YogaKit FlexBox是一套通用的布局協(xié)議,YogaKit實(shí)現(xiàn)了這個(gè)協(xié)議,iOS端可以...
    觀星閱讀 2,958評(píng)論 0 0
  • 前段時(shí)間接手了一個(gè)基于cef1的項(xiàng)目,由于其特別限定的場(chǎng)景,在查詢了對(duì)html5的支持后,整站都采用了flexbo...
    JSoon閱讀 12,426評(píng)論 1 10
  • 簡(jiǎn)書(shū)的Markdown貌似不支持插入iframe,所以文章里的JSFiddle示例都改做截圖了,如果有需要可以點(diǎn)擊...
    kangflict閱讀 1,240評(píng)論 2 8
  • Texture快速開(kāi)始(C) 1.1: Motivation & Benefits 對(duì)于復(fù)雜的視圖層次結(jié)構(gòu)來(lái)說(shuō),U...
    iYeso閱讀 1,589評(píng)論 0 3
  • Yoga 主體部分翻譯自 https://facebook.github.io/yoga/docs 部分參考 阮一...
    songkl閱讀 5,432評(píng)論 1 8

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