-
Notifications
You must be signed in to change notification settings - Fork 192
Texture 布局 Layout
这是 Texture 文档系列翻译,其中结合了自己的理解和工作中的使用体会。如果哪里有误,希望指出。
Layout API 是为了替代 UIKit Auto Layout 的高性能布局方案。Auto layout 在布局复杂视图时,成本成倍增加。Texture 的 Layout API 具有许多优点:
- 快速:与手动布局一样快,比 auto layout 快很多。
- 异步和并发:布局在后台线程计算,不影响用户交互。
- 声明性(declarative):布局用不可变数据结构声明。这使布局代码更易于开发、文档编写、代码审查、测试、调试、配置和维护。
- 可缓存:布局结果是不可变的数据结构,因此可以在后台对其预先计算并缓存,以提高用户感知性能。
- 可扩展:易于在类之间共享代码。
如果你熟悉 Flexbox,会发现 Texture layout 和 Flexbox 有众多相似之处,然而 Texture Layout API 并未实现所有 CSS 功能。
Texture 的布局系统围绕两个基本概念:
- 布局规范 Layout Specs。
- 布局元素 Layout Elements。
Layout spec 是 layout specification 的简称,没有物理存在。Layout spec 通过描述子布局元素相互关系来充当布局元素的容器。只能在layoutSpecThatFits:
方法内创建、修改ASLayoutSpec
,一旦将其传递回 ASDK,ASLayoutSpec
的isMutable
属性将被设置为 NO。此后尝试修改ASLayoutSpec
会引起断言。
Texture 提供了多个ASLayoutSpec
的子类,如ASInsetLayoutSpec
、ASStackLayoutSpec
、ASBackgroundLayoutSpec
等。
Layout spec 包含并排布 layout element。
ASDisplayNode
和ASLayoutSpec
遵守ASLayoutElement
协议,这意味着可以将 node 和 layout spec 组成 layout spec。
ASLayoutElement
协议有几个可用于创建复杂布局的属性。此外,layout spec 具有自己的属性集,可用于调整布局元素的位置。
通过下图可以看到如何将ASTextNode
(黄色突出显示)、ASVideoNode
(顶部图像)和ASStackLayoutSpec
组合在一起以创建复杂的布局。
ASVideoNode
上的播放按钮使用ASCenterLayoutSpec
和ASOverlayLayoutSpec
布局。
有些元素基于现有内容具有固有大小(intrinsic size)。例如,ASTextNode
能够根据字符串计算自身大小。以下是具有 intrinsic size 的 node:
ASImageNode
ASTextNode
ASButtonNode
其他 node 要么没有 intrinsic size,要么在加载外部资源前没有固有大小。例如,ASNetworkImageNode
在从 URL 下载完图片前没有固有大小。以下是不具有固有大小的 node:
ASVideoNode
ASVideoPlayerNode
ASNetworkImageNode
ASEditableTextNode
这些缺少初始固有大小的 node,必须使用ASRatioLayoutSpec
、ASAbsoluteLayoutSpec
或style
对象的size
属性为其设置初始大小。
在ASDisplay
和ASLayoutSpec
上调用asciiArtString
、asciiArtName
,返回该类及其子类的ascii-art
表现形式。如果为 node 或 layout spec 设置.debugName
,该属性也会被显示到 ascii art 中。如下所示:
-----------------------ASStackLayoutSpec----------------------
| -----ASStackLayoutSpec----- -----ASStackLayoutSpec----- |
| | ASImageNode | | ASImageNode | |
| | ASImageNode | | ASImageNode | |
| --------------------------- --------------------------- |
--------------------------------------------------------------
你也可以输出ASLayoutElement
的style
对象,这在调试大小属性时特别有用。
(lldb) po _photoImageNode.style
Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}
下面的示例摘取自example project demo。
实现上述布局方案:
- 垂直的
ASStackLayoutSpec
。 - 水平的
ASStackLayoutSpec
。 - 使用
ASInsetLayoutSpec
偏移整个 header。
下图显示了布局元素的组成(node 和 layout spec):
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
// When the username / location text is too long,
// shrink the stack to fit onscreen rather than push content to the right, offscreen
ASStackLayoutSpec *nameLocationStack = [ASStackLayoutSpec verticalStackLayoutSpec];
nameLocationStack.style.flexShrink = 1.0;
nameLocationStack.style.flexGrow = 1.0;
// If fetching post location data from server,
// check if it is available yet and include it if so
if (_postLocationNode.attributedText) {
nameLocationStack.children = @[_usernameNode, _postLocationNode];
} else {
nameLocationStack.children = @[_usernameNode];
}
// Horizontal stack
ASStackLayoutSpec *headerStackSpec = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:40
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[nameLocationStack, _postTimeNode]];
// Inset the horizontal stack
return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 10, 0, 10) child:headerStackSpec];
}
旋转设备可以查看间距变化。
实现上述布局方案:
- 使用
ASInsetLayoutSpec
inset 文本。 - 使用
ASOverlayLayoutSpec
将 inset 后的文本覆盖到图片上。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
CGFloat photoDimension = constrainedSize.max.width / 4.0;
_photoNode.style.preferredSize = CGSizeMake(photoDimension, photoDimension);
// INFINITY is used to make the inset unbounded
UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];
return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode overlay:textInsetSpec];
}
实现上述布局方案:
- 使用
ASCornerLayoutSpec
将图标放到图片边角。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
_iconNode.style.preferredSize = CGSizeMake(40, 40);
_photoNode.style.preferredSize = CGSizeMake(150, 150);
return [ASCornerLayoutSpec cornerLayoutSpecWithChild:_photoNode
corner:_iconNode
location:ASCornerLayoutLocationTopRight];
}
实现上述布局方案:
- 使用
ASInsetLayoutSpec
inset 文本。 - 使用
ASCenterLayoutSpec
剧中文本。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
UIEdgeInsets insets = UIEdgeInsetsMake(0, 12, 4, 4);
ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets
child:_titleNode];
return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringY
sizingOptions:ASCenterLayoutSpecSizingOptionMinimumX
child:inset];
}
布局方案如下:
- 使用
ASInsetLayoutSpec
inset 文本。 - 使用垂直方向的
ASStackLayoutSpec
将分割线堆叠在文本的顶部和底部。
下图显示了布局方案的组成:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
// flexGrow、flexShrink 均为 BOOL 类型
_topSeparator.style.flexGrow = 1.0;
_bottomSeparator.style.flexGrow = 1.0;
ASInsetLayoutSpec *insetContentSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(20, 20, 20, 20)
child:_textNode];
return [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:0
justifyContent:ASStackLayoutJustifyContentCenter
alignItems:ASStackLayoutAlignItemsStretch
children:@[_topSeparator, insetContentSpec, _bottomSeparator]];
}
可以使用以下ASLayoutSpec
的子类组建复杂布局。
ASWrapperLayoutSpec
ASStackLayoutSpec
ASInsetLayoutSpec
ASOverlayLayoutSpec
ASBackgroundLayoutSpec
ASRatioLayoutSpec
-
ASRelativeLayoutSpec
ASCenterLayoutSpec
ASAbsoluteLayoutSpec
ASCornerLayoutSpec
ASCenterLayoutSpec
继承自ASRelativeLayoutSpec
。
可以通过继承ASLayoutSpec
创建自定义布局规范。
ASWrapperLayoutSpec
可以包装ASLayoutElement
,并根据 layout element 大小计算子视图布局。
ASWrapperLayoutSpec
适合于从layoutSpecThatFits:
返回单个子节点,该子 node 可选设置大小。如果还需要设置位置信息,请使用ASAbsoluteLayoutSpec
。
// return a single subnode from layoutSpecThatFits:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
return [ASWrapperLayoutSpec wrapperWithLayoutElement:_subnode];
}
// set a size (but not position)
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
_subnode.style.preferredSize = CGSizeMake(constrainedSize.max.width,
constrainedSize.max.height / 2.0);
return [ASWrapperLayoutSpec wrapperWithLayoutElement:subnode];
}
在 Texture 的所有 layoutSpecs 中,ASStackLayoutSpec
是最有用、最强大的。ASStackLayoutSpec
使用 flexbox 算法确定子视图的位置和大小。Flexbox 旨在不同尺寸的屏幕上提供一致的布局,在堆栈布局中(stack layout),可以在垂直或水平方向对齐项目,Stack layout 可以嵌套使用。这使得可以使用 stack layout 创建几乎所有布局。
Stack layout 可以垂直或水平堆叠 children。初始布局时,children 在主轴方向无限大布局,交叉轴方向采用布局约束。在主轴方向所有 children 大小相加,如果和小于布局规范的最小值,设置了flexGrow
的 children 将被拉伸;如果和大于布局规范的最大值,设置了flexShrink
的 children 将被压缩;如果拉伸、压缩后仍然不符合布局规范,children 的justifyContent
决定 children 布局方式。
例如:堆叠方向为垂直方向,min-width = 100, max-width = 300, min-height = 200, max-height = 500。初始布局时,所有 children 按照min-width = 100, max-width = 300, min-height = 0, max-height = INFINITY 布局。如果 children 高度和小于200,设置了flexGrow
属性的 children 将会拉伸,如果 children 高度和大于500,设置了flexShrink
属性的 children 将被压缩。每个 child 压缩高度为 总高度 - 500 / 可压缩 child 数量。如果压缩后高度依然大于500,justifyContent
属性决定 children 布局方式。
ASStackLayoutSpec
除了ASLayoutElement
属性外,还具有以下属性:
-
direction
:设置 children 堆叠方向。如果设置了verticalAlignment
或horizontalAlignment
,将再次解析。从而导致justifyContent
和alignItems
相应地更新。 -
spacing
:child 间距。 -
horizontalAlignment
:指定 children 水平对齐方式。根据堆栈方向,设置对齐方式将导致justifyContent
和alignItems
被更新。后续堆栈方向改变时,此属性依然有效。 -
verticalAlignment
:指定 children 垂直对齐方式。根据堆栈方向,设置对齐方式将导致justifyContent
和alignItems
被更新。后续堆栈方向改变时,此属性依然有效。 -
justifyContent
:指定沿主轴的对齐方式。justifyContent
属性决定了浏览器如何沿 flex 容器主轴和内连轴(inline axis)在内容项之间及其周围分配空间。采用该属性前会先根据flexGrow
、flexShrink
进行拉伸压缩,拉伸、压缩后依然超出、小于可用空间,会根据该属性进行布局。例如,justifyContent
设置为ASStackLayoutJustifyContentStart
时,如果最小占用空间超出规范大小,则超出部分在右下角;如果小于规范大小,则居左上角显示。点击justify-content查看更为详细介绍。 -
alignItems
:指定 children 沿交叉轴的排列方式。 -
flexWrap
:children 排布在一列还是多列。默认排布为一列。 -
alignContent
:如果排布为多列,沿交叉轴排布方式。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
ASStackLayoutSpec *mainStack = [ASStackLayoutSpec stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
spacing:6.0
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsCenter
children:@[_iconNode, _countNode]];
// Set some constrained size to the stack
mainStack.style.minWidth = ASDimensionMakeWithPoints(60.0);
mainStack.style.maxHeight = ASDimensionMakeWithPoints(40.0);
return mainStack;
}
可以查看Flexbox playground了解每个属性的具体布局方式。
在布局传递期间,ASInsetLayoutSpec
减去 inset 之后,将其 constrainedSize.max 传递给 child。child 确定其最终尺寸后,添加上 inset 即为最终尺寸。由于ASInsetLayoutSpec
的布局是根据 child 尺寸确定的,因此 child 必须具有固有大小,或显式设置其大小。
如果UIEdgeInsets
值设置为INFINITY
,inset spec 将使用 child 的固有大小。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
...
UIEdgeInsets *insets = UIEdgeInsetsMake(10, 10, 10, 10);
ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:textNode];
...
}
ASOverlayLayoutSpec
布局 child(蓝色),并在其顶部拉伸另一个组件作为覆盖层(红色)。
Overlay 的大小根据 child 大小计算而来。在上图中,child 是蓝色部分,child 的大小作为constrainedSize
传递给 overlay 布局元素。因此,child 必须具有固有大小,或显式设置大小。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
_blueNode.style.preferredSize = CGSizeMake(150, 150);
ASInsetLayoutSpec *redInsetLayoutSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(30, -30, -30, 30) child:_redNode];
return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_blueNode overlay:redInsetLayoutSpec];
}
ASBackgroundLayoutSpec
布局一个组件 child(蓝色),并拉伸另一个组件作为背景(红色)。
背景组件的大小根据 child(蓝色)大小决定。布局时 child 大小会作为constrainedSize
传递给背景组件(红色)。因此,child 必须具有固有大小,或显式设置大小。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
_blueNode.style.preferredSize = CGSizeMake(150, 150);
ASInsetLayoutSpec *redInsetLayoutSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(-30, 30, 30, -30) child:_redNode];
return [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:_blueNode background:redInsetLayoutSpec];
}
ASBackgroundLayoutSpec
布局时,子节点添加的顺序会影响布局结果。必须先添加背景组件,后添加 child组件。使用自动子节点管理(automatic subnode management,简称ASM)并不能确保其添加顺序。
ASCenterLayoutSpec
将 child 布局于最大constrainedSize
中。
如果没有限制ASCenterLayoutSpec
的宽度、高度,则其会缩放到 child 大小。
ASCenterLayoutSpec
包含以下属性:
-
centeringOptions
:确定 child 如何在 center spec 内居中,选项包括:none、X、Y、XY。 -
sizingOptions
:确定 center spec 占用空间。选项包括:Default、MinimumX、MinimumY、MinimumXY。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
sizingOptions:ASCenterLayoutSpecSizingOptionDefault
child:subnode]
}
ASRatioLayoutSpec
用于固定组件纵横比,其可以缩放。ASRatioLayoutSpec
必须提供宽度或高度,用以限制组件大小。
由于ASNetworkImageNode
和ASVideoNode
在服务器返回内容前没有固有大小,经常使用ASRatioLayoutSpec
为ASNetworkimageNode
和ASVideoNode
提供固有大小。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
// Half Ratio
ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(100, 100));
return [ASRatioLayoutSpec ratioLayoutSpecWithRatio:0.5
child:subnode];
}
ASRelativeLayoutSpec
根据水平、垂直位置标志符在布局范围内进行布局。类似于九宫格图片,child 可以位于四角、边线中间或中心。
ASRelativeLayoutSpec
类非常强大,可以查看ASRelativeLayoutSpec
的calculateLayoutThatFits:
方法了解具体实现细节:
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
...
ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
ASStaticSizeDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
ASRelativeLayoutSpec *relativeSpec = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionStart
verticalPosition:ASRelativeLayoutSpecPositionStart
sizingOption:ASRelativeLayoutSpecSizingOptionDefault
child:foregroundNode]
ASBackgroundLayoutSpec *backgroundSpec = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:relativeSpec background:backgroundNode];
...
}
ASAbsoluteLayoutSpec
通过设置layoutPosition
属性指定 children 具体位置。与其他布局方式相比,ASAbsoluteLayoutSpec
不灵活且难以维护。
ASAbsoluteLayoutSpec
只有一个属性:
-
sizing
:ASAbsoluteLayoutSpecSizing
指定 spec 占用空间,ASAbsoluteLayoutSpecSizingDefault
尽可能占用更多空间;ASAbsoluteLayoutSpecSizingSizeToFit
计算出的大小是所有 frame 的和。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
CGSize maxConstrainedSize = constrainedSize.max;
// Layout all nodes absolute in a static layout spec
guitarVideoNode.style.layoutPosition = CGPointMake(0, 0);
guitarVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width, maxConstrainedSize.height / 3.0);
nicCageVideoNode.style.layoutPosition = CGPointMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
nicCageVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
simonVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height - (maxConstrainedSize.height / 3.0));
simonVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width/2, maxConstrainedSize.height / 3.0);
hlsVideoNode.style.layoutPosition = CGPointMake(0.0, maxConstrainedSize.height / 3.0);
hlsVideoNode.style.preferredSize = CGSizeMake(maxConstrainedSize.width / 2.0, maxConstrainedSize.height / 3.0);
return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode]];
}
ASCornerLayoutSpec
可以便捷的在四个角布局元素。将元素放置在角落的简单方法是使用声明性代码表达式,而非手动计算坐标。ASCornerLayoutSpec
可以很方便的实现这项任务。
ASCornerLayoutSpec
可以计算自身大小。ASCornerLayoutSpec
的用途之一是为头像添加角标,这时无需担心超出头像部分的 frame 会影响整个布局大小。默认情况下,角标元素大小不会计算进布局大小;如果需要计算进去,手动设置wrapsCorner
属性为YES
。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
...
// Layout the center of badge to the top right corner of avatar.
ASCornerLayoutSpec *cornerSpec = [ASCornerLayoutSpec cornerLayoutSpecWithChild:self.avatarNode corner:self.badgeNode location:ASCornerLayoutLocationTopRight];
// Slightly shift center of badge inside of avatar.
cornerSpec.offset = CGPointMake(-3, 3);
...
}
ASLayoutSpec
是所有布局方案的父类。其主要功能是管理所有 children,但也可用于创建子类。只有极少高级用户需要创建ASLayoutSpec
的子类,尽可能利用上面提供的类组合创建复杂布局。
ASLayoutSpec
的另一用途是为ASStackLayoutSpec
提供空间,此时需要设置flexGrow
或flexShrink
。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize {
...
// ASLayoutSpec as spacer
ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
spacer.style.flexGrow = 1.0;
stack.children = @[imageNode, spacer, textNode];
...
}
- ASStackLayoutElement Properties 可用于任何 node 或 layoutSpec,但只对 stack spec 的 child 有效。
- ASAbsoluteLayoutElement Properties 只对 absolute spec 的 child 有效。
- ASLayoutElement properties 对所有 node 和 layout spec 有效。
属性 | 描述 |
---|---|
CGFloat .style.spacingBefore |
在 stack 方向、该对象前面的额外空间。 |
CGFloat .style.spacingAfter |
在 stack 方向、该对象后面的额外空间 |
CGFloat .style.flexGrow |
如果 stack 的和小于允许的最小值,该对象是否拉伸。 |
CGFloat .style.flexShrink |
如果 stack 的和大于允许的最大值,该对象是否压缩。 |
ASDimension .style.flexBasis |
在应用flexGrow 、flexShrink 属性并分配剩余空间前,在 stack 方向上的初始大小。 |
ASStackLayoutAlignSelf .style.alignSelf |
对象沿交叉轴方向对齐方式,重写alignItems 属性。选项包括:ASStackLayoutAlignSelfAuto、ASStackLayoutAlignSelfStart、ASStackLayoutAlignEnd、ASStackLayoutAlignSelfCenter、ASStackLayoutAlignSelfStretch。 |
CGFloat .style.ascender |
用于基线对齐方式。从对象顶部至基线的距离。 |
CGFloat .style.descender |
用于基线对齐方式。从对象基线至顶部的距离。 |
属性 | 描述 |
---|---|
CGPoint .style.layoutPosition |
对象在ASABsoluteLayoutSpec 规范中的CGPoint 位置。 |
属性 | 描述 |
---|---|
ASDimension .style.width |
width 属性指定ASLayoutElement 内容区域宽度,minWidth 和maxWidth 属性会重写width 。即当width 值小于minWidth 时,以minWidth 为准。默认为ASDimensionAuto 。 |
ASDimension .style.height |
height 属性指定ASLayoutElement 内容区域高度,minHeight 和maxHeight 属性会重写width 。默认为ASDimensionAuto 。 |
ASDimension .style.minWidth |
minWidth 属性用于设定给定元素的最小宽度。minWidth 的值会重写maxWidth 和width 。默认为ASDimensionAuto 。 |
ASDimension .style.maxWidth |
maxWidth 属性用于设定给定元素的最大宽度。maxWidth 属性会阻止width 的值大于maxWidth 。maxWidth 属性重写width ,minWidth 属性重写maxWidth 。默认为ASDimensionAuto 。 |
ASDimension .style.minHeight |
minHeight 属性用于设定给定元素的最小高度。minHeight 属性会阻止height 的值小于minHeight 。minHeight 会重写maxHeight 和height 。默认为ASDimensionAuto 。 |
ASDimension .style.maxHeight |
maxHeight 属性用于设定给定元素的最大高度。maxHeight 属性会阻止height 的值大于maxHeight 。maxHeight 会重写height ,minHeight 会重写maxHeight 。默认为ASDimensionAuto 。 |
CGSize .style.preferredSize |
为布局元素设置推荐大小。如果设置了可选值minSize 、maxSize ,且preferredSize 超过了其限制,则采用minSize 、maxSize 的值。如果未设置preferredSize ,则默认采用calculateSizeThatFits: 计算出的固有大小。该属性可选设置。如果 node 没有固有大小或需要设置为其他大小,则需要设置 preferredSize 属性。例如,为ASDisplayNode 设置不同于图片大小的值。当 size 为相对大小时,调用 height 、width 的取值方法会触发断言。 |
CGSize .style.minSize |
为布局元素设置最小尺寸,可选属性。如果设置了该属性,则强制满足该属性。如果父视图最小尺寸小于子视图最小尺寸,则强制满足子视图最小尺寸,父视图尺寸会超出其自身限制。 |
CGSize .style.maxSize |
为布局元素设置最大尺寸,可选属性。如果设置了该属性,则强制满足该属性。如果子视图最大尺寸小于父视图限制,则采用子视图尺寸限制。 |
ASLayoutSize .style.preferredLayoutSize |
为布局元素提供相对大小,可选属性。ASLayoutSize 使用百分比而非 point 指定大小。例如,宽度为父视图 50%。如果提供了可选的minLayoutSize 、maxLayoutSize ,且preferredLayoutSize 超过了minLayoutSize 、maxLayoutSize 的限制,则采用minLayoutSize 、maxLayoutSize 属性的值。如果未提供,则采用calculateSizeThatFits: 计算的固有大小。 |
ASLayoutSize .style.minLayoutSize |
为布局元素提供相对大小的最小值,可选属性。如果提供了该属性,则强制满足该限制。如果父级布局最小相对尺寸小于子级最小相对尺寸,则强制满足子级最小尺寸。 |
ASLayoutSize .style.maxLayoutSize |
为布局元素提供相对大小的最大值,可选属性。如果提供了该属性,则强制满足该限制。如果父级布局最大相对尺寸小于子级最大相对尺寸,则强制满足子级最大尺寸。 |
下面介绍布局大小的相关单位。
ASDimension
本质上是一个普通的CGFloat
,其支持 point、百分比或自动值。
同一 API 支持固定值和相对值。
// dimension returned is relative (%)
ASDimensionMake(@"50%");
ASDimensionMakeWithFraction(0.5);
// dimension returned in points
ASDimensionMake(@"70pt");
ASDimensionMake(70);
ASDimensionMakeWithPoints(70);
ASDimension
用于设置ASStackLayoutSpec
child 的flexBasis
属性。flexBasis
指定 stack 轴向初始大小。
在下图中,水平 stack 左侧占比 50%,右侧占比 60%。
通过设置横向 stack children 的flexBasis
属性可以实现上述需求。
self.leftStack.style.flexBasis = ASDimensionMake(@"40%");
self.rightStack.style.flexBasis = ASDimensionMake(@"60%");
[horizontalStack setChildren:@[self.leftStack, self.rightStack]];
ASLayoutSize
与CGSize
相似,但其宽度和高度可以使用 points 和百分比表示。高度和宽度可以是不同表示类型。
ASLayoutSizeMake(ASDimension width, ASDimension height);
ASLayoutSize
用于设置布局元素的preferredLayoutSize
、minLayoutSize
和maxLayoutSize
属性。其允许同一 API 同时使用固定大小和相对大小。
// Dimension type "Auto" indicates that the layout element may
// be resolved in whatever way makes most sense given the circumstances
ASDimension width = ASDimensionMake(ASDimensionUnitAuto, 0);
ASDimension height = ASDimensionMake(@"50%");
layoutElement.style.preferredLayoutSize = ASLayoutSizeMake(width, height);
如果不需要相对尺寸,可以使用preferredSize
、minSize
、maxSize
属性。这些属性使用常规CGSize
值。
layoutElement.style.preferredSize = CGSizeMake(30, 160);
通常情况下,不需同时设置高度和宽地。这种情况下可以使用ASDimension
设置所需值。
layoutElement.style.width = ASDimensionMake(@"50%");
layoutElement.style.minWidth = ASDimensionMake(@"50%");
layoutElement.style.maxWidth = ASDimensionMake(@"50%");
layoutElement.style.height = ASDimensionMake(@"50%");
layoutElement.style.minHeight = ASDimensionMake(@"50%");
layoutElement.style.maxHeight = ASDimensionMake(@"50%");
UIKit
没有提供最小、最大CGSize
结构。因此,创建了ASSizeRange
以支持最小和最大CGSize
结构。
ASSizeRange
通常用在布局API内部,但layoutSpecThatFits:
的参数是ASSizeRange
。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize;
传递给layoutSpecThatFits:
的 constrainedSize 参数包含了 node 需满足的最小、最大尺寸,用以规范 node 尺寸。
Texture 也可以使用动画,具体可以查看Layout Transition API
上一篇:Texture 核心概念
下一篇:Texture 便捷方法