前言
Texture的基本使用單元是node. ASDisplayNode是一個UIView層之上的封裝铅匹,就像UIView是對CALayer的封裝一樣押赊。跟View不一樣的是,node是線程安全(比如uiview的操作就不是線程安全的包斑,在非UI線程無法操作UIView)的流礁,就是說你在非主線程對node進(jìn)行初始化以及配置它們的層級操作都是安全的。
今天的主題是布局罗丰,Texture擁有自己的一套成熟布局方案神帅,雖然學(xué)習(xí)成本略高,但至少比蓋原萌抵。而且AutoLayout寫起來舒服找御,重點是性能遠(yuǎn)好于AutoLayout元镀,Texture文檔上也。指出了這套布局方案的的優(yōu)點:
- 快速:與手動布局代碼一樣快霎桅,比自動布局更快
- 異步和并發(fā):可以在后臺線程上計算布局栖疑,以便不中斷用戶交互。
- 聲明性:使用不可變數(shù)據(jù)結(jié)構(gòu)顯示布局滔驶。這使得布局代碼更易于開發(fā)遇革,文檔,代碼審查揭糕,測試澳淑,調(diào)試,配置文件和維護(hù)插佛。
- 可緩存:布局結(jié)果是不可變數(shù)據(jù)結(jié)構(gòu)杠巡,因為它們可以在后臺預(yù)先計算并緩存以提高用戶感知性能。
- 可擴(kuò)展:易于在類之間共享代碼雇寇。
章節(jié)
一氢拥、快速了解
基礎(chǔ)元素 | 含義 | 基本概念 |
---|---|---|
Layout Specs | 布局規(guī)則 | LayoutSpecs包含并排列LayoutElements。這意味著您可以從Nodes和其他LayoutSpecs構(gòu)成LayoutSpecs |
Layout Elements | 布局元素 | ASDisplayNodes和ASLayoutSpecs都符合<ASLayoutElement>協(xié)議 |
ASLayoutElement Properties(布局元素屬性)
屬性 | 類型 | 描述 |
---|---|---|
.style.width | ASDimension | 設(shè)置元素的寬度锨侯。 會被minWidth和maxWidth覆蓋嫩海。默認(rèn)為ASDimensionAuto |
.style.height | ASDimension | 設(shè)置元素的高度。 會被minHeight和maxHeight覆蓋囚痴。默認(rèn)為ASDimensionAuto叁怪。 |
.style.minHeight | ASDimension | 設(shè)置元素的最大高度。 它防止height屬性的已使用值變得大于為maxHeight指定的值深滚。 maxHeight的值覆蓋height奕谭,但minHeight覆蓋maxHeight。默認(rèn)為ASDimensionAuto |
.style.maxHeight | ASDimension | 如果子元素的堆棧大小的總和大于最大大小痴荐,那么這個對象是否應(yīng)該縮小呢血柳? |
.style.minWidth | ASDimension | 設(shè)置元素的最小寬度。它防止width屬性的使用值變得小于為minWidth指定的值生兆。 minWidth的值覆蓋maxWidth和width难捌。默認(rèn)為ASDimensionAuto |
.style.maxWidth | ASDimension | 設(shè)置元素的最大寬度。 它防止width屬性的使用值變得大于為maxWidth指定的值鸦难。 maxWidth的值覆蓋width根吁,但minWidth覆蓋maxWidth。默認(rèn)為ASDimensionAuto |
.style.preferredSize | **CGSize ** | 提供布局元素的建議大小合蔽。 如果提供了可選的minSize或maxSize击敌,且preferredSize超過這些,則將強(qiáng)制執(zhí)行minSize或maxSize, 如果未提供此可選值辈末,則布局元素的大小將默認(rèn)為其提供的內(nèi)在內(nèi)容大小calculateSizeThatFits: |
.style.minSize | **CGSize ** | ~ |
.style.maxSize | **CGSize ** | ~ |
.style.preferredLayoutSize | ASLayoutSize | ~ |
.style.minLayoutSize | ASLayoutSize | ~ |
.style.maxLayoutSize | ASLayoutSize | ~ |
1愚争、一些Node需要固定大小
一些元素具有一個”固有大小“映皆,基于他們可用內(nèi)容。
ASTextNode可以根據(jù)其屬性字符串計算其大小轰枝,其他具有固有大小的Node包括:
ASImageNode
ASButtonNode
在從URL下載圖像之前捅彻,ASNetworkImageNode不知道它的大小。這些種類包括:
ASVideoNode
ASVideoPlayerNode
ASNetworkImageNode
注意:
缺少初始固有大小的這些Node必須設(shè)置它們的初始大小鞍陨,使用ASRatioLayoutSpec(“比例布局規(guī)則”)步淹,ASAbsoluteLayoutSpec(“絕對布局規(guī)則”)或者對象的size屬性。
2诚撵、Layout調(diào)試
在任何ASDisplayNode或ASLayoutSpec上調(diào)用-asciiArtString缭裆,會返回對象及其子對象的字符圖。
(可選)如果在任何Node或layoutSpec上設(shè)置.debugName寿烟,那么也將包含在字符圖澈驼。
例如:
-----------------------ASStackLayoutSpec----------------------
| -----ASStackLayoutSpec----- -----ASStackLayoutSpec----- |
| | ASImageNode | | ASImageNode | |
| | ASImageNode | | ASImageNode | |
| --------------------------- --------------------------- |
--------------------------------------------------------------
#可以在任何ASLayoutElement(node或layoutSpec)上打印對象樣式,調(diào)整大小屬性時極其方便筛武。
例如:
(lldb) po _photoImageNode.style
Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}
3缝其、需要使用原生控件
let node = ASDisplayNode { () -> UIView in
let view = SomeView()
return view
}
二、Layout Specs(布局規(guī)則)
ASDisplayNode 在初始化之后會檢查是否有子視圖徘六,如果有就會調(diào)用以下方法進(jìn)行布局内边,所以對視圖進(jìn)行布局需要重寫這個方法(該方法返回的必須是 ASLayoutSpec)
func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
以下ASLayoutSpec的常用布局子類,用于組成簡單或復(fù)雜的布局
布局規(guī)則 | 說明 |
---|---|
ASInsetLayoutSpec | 插入布局 |
ASOverlayLayoutSpec | 覆蓋布局 |
ASBackgroundLayoutSpec | 背景布局 |
ASCenterLayoutSpec | 中心布局 |
ASRatioLayoutSpec | 比例布局 |
ASStackLayoutSpec | 堆疊布局 |
ASAbsoluteLayoutSpec | 絕對布局 |
1. ASInsetLayoutSpec(插入布局)
在布局過程中待锈,ASInsetLayoutSpec通過constrainedSize.max傳遞插入減掉后的CGSize給子項漠其,
一旦子項確定它的最終尺寸,插入規(guī)則將其最終尺寸加上其插入邊距向上傳遞竿音,
由于插圖布局規(guī)則的大小基于其子項的大小和屎,所以子項必須具有固有大小或明確設(shè)置其大小。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
let insets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
let headerWithInset = ASInsetLayoutSpec(insets: insets, child: textNode)
return headerWithInset
}
2. ASOverlayLayoutSpec(覆蓋布局)
ASOverlayLayoutSpec布局一個組件(foregroundNode)谍失,作為覆蓋伸展到另個組件(backgroundNode)之前覆蓋布局的大小眶俩,
是根據(jù)子項的大小計算得出的。下圖中快鱼,子項是backgroundNode,然后子項的大小作為constrainedSize傳遞給覆蓋布局元素(foregroundNode)纲岭,子項(backgroundNode)必須具有固有大小或在其上設(shè)置的大小抹竹。
override func layoutSpecThatFits(_ constrainedSize:ASSizeRange) - > ASLayoutSpec
{
let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
return ASOverlayLayoutSpec(child:backgroundNode,overlay:foregroundNode)
}
3. ASBackgroundLayoutSpec(背景布局)
ASBackgroundLayoutSpec布局一個組件(backgroundNode)止潮,作為背景伸展到另一個組件(foregroundNode)之后背景布局的大小窃判,是根據(jù)子項的大小計算得出的。下圖中喇闸,子項是藍(lán)色層袄琳,然后询件,子項的大小作為constrainedSize傳遞給背景布局元素(backgroundNode),子項(foregroundNode)必須具有固有大小或在其上設(shè)置的大小唆樊。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
return ASBackgroundLayoutSpec(child: foregroundNode, background: backgroundNode)
}
4. ASCenterLayoutSpec(中心布局)
ASCenterLayoutSpec將其子項居中在其最大值中constrainedSize宛琅。
如果中心規(guī)格的寬度或高度不受約束,它會縮小到子項的大小逗旁。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 60.0, height: 100.0))
let centerSpec = ASCenterLayoutSpec(centeringOptions: .XY, sizingOptions: [], child: subnode)
return centerSpec
}
ASCenterLayoutSpec的兩個屬性:
屬性 | 說明 | 值 |
---|---|---|
centeringOptions | 確定中心位置 | None嘿辟,X,Y片效,XY |
sizingOptions | 確定中心占用空間 | Default红伦,minimum X,minimum Y淀衣,minimum XY |
5. ASRatioLayoutSpec(比例布局)
ASRatioLayoutSpec布局縮放固定寬高比昙读,此規(guī)則必須具有作為constrainedSize傳遞給它的寬度或高度,因為它使用此值來縮放自身膨桥。
使用比例布局為ASNetworkImageNode或ASVideoNode提供固有大小是非常常見的蛮浑,因為兩者在服務(wù)器返回內(nèi)容之前都沒有內(nèi)在大小。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
// 一半比例
let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 100, height: 100.0))
let ratioSpec = ASRatioLayoutSpec(ratio: 0.5, child: subnode)
return ratioSpec
}
6. ASAbsoluteLayoutSpec(絕對布局)
在ASAbsoluteLayoutSpec中国撵,可以通過設(shè)置其layoutPosition屬性來指定其子元素的確切位置(x / y坐標(biāo))陵吸,絕對布局比其他類型的布局更不靈活和難以維護(hù)。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
let maxConstrainedSize = constrainedSize.max
guitarVideoNode.style.layoutPosition = CGPoint.zero
guitarVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width, height: maxConstrainedSize.height / 3.0)
nicCageVideoNode.style.layoutPosition = CGPoint(x: maxConstrainedSize.width / 2.0, y: maxConstrainedSize.height / 3.0)
nicCageVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
simonVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height - (maxConstrainedSize.height / 3.0))
simonVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
hlsVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height / 3.0)
hlsVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)
return ASAbsoluteLayoutSpec(children: [guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode])
}
ASAbsoluteLayoutSpec屬性:
屬性 | 說明 | 值 |
---|---|---|
sizing | 大小 | Default / Size to Fit |
確定絕對規(guī)格將占用多少空間介牙。
7. ASStackLayoutSpec(堆疊布局)
在ASDK中的所有l(wèi)ayoutSpecs中壮虫,ASStackLayoutSpec是非常強(qiáng)大的,ASStackLayoutSpec使用flexbox算法來確定其子節(jié)點的位置和大小环础,F(xiàn)lexbox旨在在不同的屏幕尺寸上提供一致的布局囚似,在堆疊布局中,以垂直或水平堆疊對齊item线得。堆疊布局可以是另一個堆疊布局的子布局饶唤,這使得可以使用堆疊布局規(guī)則創(chuàng)建幾乎任何布局。
override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
let mainStack = ASStackLayoutSpec(direction: .horizontal,
spacing: 6.0,
justifyContent: .start,
alignItems: .center,
children: [titleNode, subtitleNode])
mainStack.style.minWidth = ASDimensionMakeWithPoints(60.0)
mainStack.style.maxHeight = ASDimensionMakeWithPoints(40.0)
return mainStack
}
ASStackLayoutSpec除了ASLayoutElement還有7個屬性:
屬性 | 說明 | 描述 |
---|---|---|
direction | 方向 | 指定堆疊方向,如果設(shè)置了horizontalAlignment和verticalAlignment,它們將被再次解決贯钩,導(dǎo)致justifyContent和alignItems被相應(yīng)地更新募狂。 |
spacing | 間距 | 每個子元素之間的距離。 |
horizontalAlignment | 水平對齊 | 指定子元素如何水平排列,取決于堆疊方向角雷,設(shè)置對齊會導(dǎo)致justifyContent或alignItems被更新祸穷。未來方向更改后,對齊將保持有效勺三。因此雷滚,優(yōu)選那些性質(zhì)。 |
verticalAlignment | 豎直對齊 | 指定子元素如何垂直排列,取決于堆疊方向吗坚,設(shè)置對齊會導(dǎo)致justifyContent或alignItems被更新祈远。未來方向更改后呆万,對齊將保持有效。因此车份,優(yōu)選那些性質(zhì)谋减。 |
justifyContent | 對齊內(nèi)容 | 每個子元素之間的距離。 |
alignItems | 對齊Item | 子元素沿著橫軸的方向躬充。 |
baselineRelativeArrangement | 基線相對布局 | 如果YES逃顶,則從頂視圖的最后基線到底視圖的頂部測量兩個視圖之間的垂直間距。 |
ASStackLayoutElement Properties(堆疊布局元素屬性)
屬性 | 類型 | 描述 |
---|---|---|
.style.spacingBefore | **CGFloat ** | 在堆疊方向上放置此對象之前的額外空間充甚。 |
.style.spacingAfter | **CGFloat ** | 在堆疊方向上放置此對象之后的額外空間以政。 |
.style.flexGrow | **CGFloat ** | 如果子元素的堆疊大小的總和小于最小大小,那么這個對象是否增長伴找? |
.style.flexShrink | **CGFloat ** | 如果子元素的堆疊大小的總和大于最大大小盈蛮,那么這個對象是否縮小技矮? |
.style.flexBasis | ASDimension | 使用flexGrow或flexShrink屬性并分配剩余空間之前抖誉,在堆棧維度(水平或垂直)中指定此對象的初始大小。 |
.style.alignSelf | **ASStackLayoutAlignSelf ** | 沿著橫軸的對象的方向衰倦,覆蓋alignItems袒炉。(ASStackLayoutAlignSelfAuto,ASStackLayoutAlignSelfStart樊零, ASStackLayoutAlignSelfEnd我磁, ASStackLayoutAlignSelfCenter, ASStackLayoutAlignSelfStretch) |
.style.ascender | **CGFloat ** | 用于基線對準(zhǔn)驻襟。從對象的頂部到其基線的距離夺艰。 |
.style.descender | **CGFloat ** | 用于基線對準(zhǔn)。從對象的底部部到其基線的距離 |