Texture快速开始(C)

Texture快速开始(C)

1.1: Motivation & Benefits

对于复杂的视图层次结构来说,UIKit的布局方式将变得比较低效。Layout API是作为UIKit Auto Layout的高性能替代品创建的。Texture的布局API比使用UIKit的自动布局有以下优秀的特点:

  • 1: 快速:与手动布局代码一样快,比自动布局快得多
  • 2: 异步和并发:布局可以在后台线程上计算,因此用户交互流畅和及时响应。
  • 3: 声明性:布局是用不可变的数据结构声明的。这使得布局代码更容易开发,文档,代码审查,测试,调试,配置文件和维护。
  • 4: 可缓存:布局结果是不可变的数据结构,因此它们可以在后台预先计算并缓存以提高用户感知的性能。
  • 5: 可扩展:易于在类之间共享代码。

1.2: 受CSS Flexbox的启发

熟悉Flexbox的人会注意到这两个系统有许多相似之处。但是,Texture的Layout API不会重新实现所有的CSS。

1.3: 基本概念

Texture的布局系统围绕着两个基本概念:

  • 布局规格
  • 布局元素

1.4: 布局规范

布局规范(简称“布局规范”)没有实体存在。相反,通过了解这些子布局元素如何相互关联,布局规范充当其他布局元素的容器。

Texture提供了ASLayoutSpec的几个子类,从一个插入单个子元素的简单布局规范到更复杂的布局规范,这些布局规范将多个子元素排列在不同的堆栈配置中。

1.5: 布局元素

布局规范包含并排列布局元素。

所有ASDisplayNodesASLayoutSpecs都符合协议。这意味着您可以从节点和其他布局规格构建布局规格。cool!

ASLayoutElement协议有几个属性可以用来创建非常复杂的布局。此外,布局规范还有其自己的一组属性,可用于调整布局元素的排列。

1.6: 结合布局规范和布局元素来制作复杂的用户界面

在这里,您可以看到如何组合ASTextNodes(以黄色突出显示),ASVideoNode(顶部图像)和ASStackLayoutSpec(“堆栈布局规格”)来创建复杂布局。

Texture快速开始(C)_第1张图片
layoutSpec

使用 ASCenterLayoutSpec(“中央布局规范”)和ASOverlayLayoutSpec(“覆盖布局规范”)放置ASVideoNode顶部的播放按钮(顶部图像)。

Texture快速开始(C)_第2张图片
image

1.7: 有些节点需要尺寸集

一些元素基于必须给定固定大小。例如,ASTextNode可以根据其属性字符串计算其大小。有些node需要固定尺寸的包括

  • ASImageNode
  • ASTextNode
  • ASButtonNode

所有其他节点在加载外部资源之前,不具有固有大小或缺少固有大小。例如,ASNetworkImageNode在从URL下载图像之前不知道其大小。 这些元素包括

  • ASVideoNode
  • ASVideoPlayerNode
  • ASNetworkImageNode
  • ASEditableTextNode

缺少初始固有尺寸的这些节点必须使用ASRatioLayoutSpecASAbsoluteLayoutSpec或样式对象上的尺寸属性为它们设置初始尺寸。

1.8: 布局调试

在任何ASDisplayNodeASLayoutSpec上调用-asciiArtString都会返回对象及其子对象的ascii-art表示形式。 或者,如果您在任何节点或布局规范上设置.debugName,那么它也将包含在ascii art中。 下面是一个例子。

-----------------------ASStackLayoutSpec----------------------
|  -----ASStackLayoutSpec-----  -----ASStackLayoutSpec-----  |
|  |       ASImageNode       |  |       ASImageNode       |  |
|  |       ASImageNode       |  |       ASImageNode       |  |
|  ---------------------------  ---------------------------  |
--------------------------------------------------------------

您还可以在任何ASLayoutElement(节点或布局规范)上打印样式对象。这在调试大小属性时特别有用。

(lldb) po _photoImageNode.style
Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}

二: Layout Examples

查看布局规范示例项目以使用下面的代码。

带左右对齐文本的简单标题

Texture快速开始(C)_第3张图片
image

要创建这个布局,我们将使用:

  • 垂直ASStackLayoutSpec
  • 和水平ASStackLayoutSpec
  • ASInsetLayoutSpec插入整个标题

下图显示了布局元素的组成(节点+布局规格)。


Texture快速开始(C)_第4张图片
image
- (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];
}

2.1:带有嵌入文字叠加层的照片

Texture快速开始(C)_第5张图片
image

要创建这个布局,我们将使用:

  • ASInsetLayoutSpec插入文本
  • ASOverlayLayoutSpec将插图文字说明覆盖在照片上方
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  _photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);

  // INIFINITY 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];
}

[ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode overlay:textInsetSpec]中的_photoNode的尺寸为平铺视图的大小 因此此时_photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2)无用

2.2:带有初始图标叠加的照片
Texture快速开始(C)_第6张图片
image

要创建此布局,我们将使用:
ASAbsoluteLayoutSpec放置照片和图标,这些照片和图标已经使用其ASLayoutable属性单独调整大小和放置

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  _iconNode.style.preferredSize = CGSizeMake(40, 40);
  _iconNode.style.layoutPosition = CGPointMake(150, 0);
  
  _photoNode.style.preferredSize = CGSizeMake(150, 150);
  _photoNode.style.layoutPosition = CGPointMake(40 / 2.0, 40 / 2.0);
  
  return [ASAbsoluteLayoutSpec absoluteLayoutSpecWithSizing:ASAbsoluteLayoutSpecSizingSizeToFit
                                                   children:@[_photoNode, _iconNode]];
}

2.3: 简单的插入文本单元格
Texture快速开始(C)_第7张图片
image

要重新创建上面Pinterest的搜索视图中使用的单个单元格的布局,我们将使用:

  • ASInsetLayoutSpec插入文本
  • 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];
}

2.4: 顶部和底部分隔线
Texture快速开始(C)_第8张图片
image

要创建上面的布局,我们将使用a:

  • 一个ASInsetLayoutSpec来插入文本
  • 垂直ASStackLayoutSpec将文本顶部和底部的两个分隔线叠加起来

下图显示了可布局对象的组成(布局规格+节点)。


Texture快速开始(C)_第9张图片
image

以下代码也可以在ASLayoutSpecPlayground示例项目中找到。

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  _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]];
}

Layout Specs

以下ASLayoutSpec子类可用于组成简单或非常复杂的布局。

  • ASWrapperLayoutSpec

  • ASStackLayoutSpec

  • ASInsetLayoutSpec

  • ASOverlayLayoutSpec

  • ASBackgroundLayoutSpec

  • ASCenterLayoutSpec

  • ASRelativeLayoutSpec

  • ASAbsoluteLayoutSpec

  • ASCornerLayoutSpec

    • 您也可以将ASLayoutSpec子类化,以制定自己的自定义布局规格。
ASWrapperLayoutSpec

ASWrapperLayoutSpec是一个简单的ASLayoutSpec子类,可以包装ASLayoutElement并根据在layout元素上设置的大小来计算子级的布局。

ASWrapperLayoutSpec是从-layoutSpecThatFits轻松返回单个子节点的理想选择。 (可选)可以在此子节点上设置大小信息。然而,如果您需要设置大小以外的位置,请改用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];
}
ASStackLayoutSpec (Flexbox Container)

Texture中的所有layoutSpecs中,ASStackLayoutSpec是最有用和最强大的。 ASStackLayoutSpec使用flexbox算法确定其子元素的位置和大小. Flexbox旨在在不同的屏幕尺寸上提供一致的布局. 在堆栈布局中,您可以在垂直或水平堆栈中对齐项目。堆栈布局可以是另一个堆栈布局的子级,这使得可以使用堆栈布局规范创建几乎任何布局。

ASStackLayoutSpec除了其属性外,还具有7个属性:

  • direction: 指定子级堆叠的方向。如果设置了horizo​​ntalAlignmentverticalAlignment,它们将再次解析,导致justifyContentalignItems相应地更新
  • spacing: 每个子node之间的空间量
  • horizontalAlignment(主轴方向): 指定子项如何水平对齐。根据堆栈方向,设置对齐方式将导致justifyContentalignItems被更新。以后的方向更改后,路线将保持有效。对这些属性而言是首选。
  • verticalAlignment(纵轴方向): 指定子项如何垂直对齐。根据堆栈方向,设置对齐方式将导致justifyContentalignItems被更新。以后的方向更改后,路线将保持有效。因此,对这些属性而言是首选。
  • justifyContent: 它定义了沿主轴的对齐方式
  • alignItems: 子级沿横轴的方向
  • flexWrap: 子级是堆叠成单行还是多行。默认为单行。
  • 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在Texture中的工作方式与在Web中CSS中的工作方式相同。例如,默认值是不同的,并且没有flex参数。有关更多信息,请参见Web Flexbox差异。

ASInsetLayoutSpec

在布局传递期间,ASInsetLayoutSpec减去其inset之后,将其constrainedSize.max CGSize传递给其子级。子项确定其最终尺寸后,ASInsetLayoutSpec将通过其最终尺寸,即其子项的尺寸加上其ASInsetLayoutSpec边距。由于ASInsetLayoutSpec的版式规格是根据其子代的尺寸来确定的,因此子代必须具有内在的尺寸或明确设置其尺寸。

Texture快速开始(C)_第10张图片
image

如果在UIEdgeInsets中将INFINITY设置为值,则插入规范将仅使用子级的固有尺寸。看到一个例子。

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ...
  UIEdgeInsets *insets = UIEdgeInsetsMake(10, 10, 10, 10);
  ASInsetLayoutSpec *headerWithInset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:textNode];
  ...
}
ASOverlayLayoutSpec

ASOverlayLayoutSpec布置其子项(蓝色),并在其顶部拉伸另一个组件作为覆盖层(红色)。

Texture快速开始(C)_第11张图片
ASOverlayLayouSpec-diagram.png

覆盖规格的尺寸是根据子级的尺寸计算得出的。在下图中,子级是蓝色层。然后,将子级的尺寸作为constrainedSize传递给叠加布局元素(红色层)。因此,重要的是子级(蓝色层)必须具有固有大小或在其上设置的大小。

当将自动子节点管理与ASOverlayLayoutSpec一起使用时,节点有时会以错误的顺序出现。这是一个已知问题,将很快得到解决。当前的解决方法是手动添加节点,必须将叠加布局元素(红色)作为子节点添加到子布局元素(蓝色)之后的父节点。

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);
  ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
  return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:backgroundNode overlay:foregroundNode];
}
ASBackgroundLayoutSpec

ASBackgroundLayoutSpec布置一个组件(蓝色),并拉伸其后的另一个组件作为背景(红色)。

Texture快速开始(C)_第12张图片
ASBackgroundLayoutSpec-diagram.png

背景规格的大小是根据孩子的大小计算得出的。在下图中,子级是蓝色层。然后,将孩子的尺寸作为constrainedSize传递到背景布局元素(红色层)。因此,重要的是子级(蓝色层)必须具有固有大小或在其上设置的大小。

当将自动子节点管理与ASOverlayLayoutSpec一起使用时,节点有时会以错误的顺序出现。这是一个已知问题,将很快得到解决。当前解决方法是手动添加节点,必须将子布局元素(蓝色)作为子节点添加到子背景元素(红色)之后的父节点。

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ASDisplayNode *backgroundNode = ASDisplayNodeWithBackgroundColor([UIColor redColor]);
  ASDisplayNode *foregroundNode = ASDisplayNodeWithBackgroundColor([UIColor blueColor]);

  return [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:foregroundNode background:backgroundNode];
}

注意:添加子节点的顺序对于此布局规范很重要。必须将背景对象作为子节点添加到父对象,然后再添加到前景对象。目前,使用ASM不能保证此订单!

ASCenterLayoutSpec

ASCenterLayoutSpec将其子项置于其最大constrainedSize之内。

Texture快速开始(C)_第13张图片
ASCenterLayoutSpec-diagram.png

如果中心规格的宽度或高度不受限制,则会缩小到子级的大小。

ASCenterLayoutSpec具有两个属性:

  • centeringOptions。确定子spec如何在中心规格内居中。选项包括:无,X​​,Y,XY。
  • sizingOptions。确定中心规格将占用多少空间。选项包括:默认,最小X,最小Y,最小XY。
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(70, 100));
  return [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY
                                                    sizingOptions:ASCenterLayoutSpecSizingOptionDefault
                                                            child:subnode]
}
ASRatioLayoutSpec

ASRatioLayoutSpec以固定的纵横比布局组件,该比例可以缩放. 此规范必须具有作为约束大小传递给它的宽度或高度,因为它使用此值进行缩放

Texture快速开始(C)_第14张图片
ASRatioLayoutSpec-diagram.png

使用比率规范为ASNetworkImageNodeASVideoNode提供固有大小是非常普遍的,因为在内容从服务器返回之前,这两者都不具有固有大小。


- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  // Half Ratio
  ASStaticSizeDisplayNode *subnode = ASDisplayNodeWithBackgroundColor([UIColor greenColor], CGSizeMake(100, 100));
  return [ASRatioLayoutSpec ratioLayoutSpecWithRatio:0.5 child:subnode];
}
ASRelativeLayoutSpec

对组件进行布局,并根据垂直和水平位置说明符将其放置在布局范围内。与“ 9部分”图像区域类似,可以将孩子放置在4个角的任何一个或4个边缘中的任何一个的中间以及中心。

这是一个非常强大的类,但过于复杂而无法在本概述中进行介绍。有关更多信息,请查看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

ASAbsoluteLayoutSpec中,您可以通过设置子元素的layoutPosition属性来指定其子元素的确切位置(x / y坐标)。绝对布局比其他类型的布局更不灵活且更难维护。

ASAbsoluteLayoutSpec具有一个属性:

  • sizing。确定绝对规格将占用多少空间。选项包括:“默认”和“适合大小”。请注意,“适合大小”选项将复制旧的ASStaticLayoutSpec的行为。
- (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可以实现此目标.

Texture快速开始(C)_第15张图片
ASCornerLayoutSpec.png

ASCornerLayoutSpec会很好针对自己的尺寸计算, 最好的解释情况是,在用户头像图像的一角添加一个小的徽标视图,而无需担心过少的徽标框(超出头像图像框)可能会影响整体布局尺寸, 默认情况下,仅当您手动打开wrapsCorner属性时,才会将角元素的大小添加到布局大小中

从2.7及更高版本引入ASCornerLayoutSpec

ASLayoutSpec

ASLayoutSpec是主要类,因为所有布局规范都被子类化. 它的主要工作是处理所有子代管理,但也可以用于创建自定义布局规范. 尽管只有超级高级才需要/需要创建ASLayoutSpec的自定义子类。而是尝试使用提供的布局规格并将它们组合在一起以创建更高级的布局

当应用.flexGrow和/或.flexShrink时,ASLayoutSpec的另一种用法是与其他子级一起用作ASStackLayoutSpec中的间隔符。

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
  ...
  // ASLayoutSpec as spacer
  ASLayoutSpec *spacer = [[ASLayoutSpec alloc] init];
  spacer.style.flexGrow = 1.0;

  stack.children = @[imageNode, spacer, textNode];
  ...
}

你可能感兴趣的:(Texture快速开始(C))