使用Texture(aka AsyncDisplayKit)的8个月

我必须承认我爱React&React Native。 我从2014年开始使用React,从2016年开始使用React Native。我之所以喜欢RN,并不是因为它具有跨平台的机制,而是因为它在设计用户界面时具有务实的方法以及能够将屏幕组成的能力。 组件可以具有自己的状态,如果状态发生了变化,则取决于此确切状态的用户界面将重新呈现以反映新值。

它加快了开发速度,也使开发人员避免了状态不反映值的错误。 我的大部分时间都花在开发本机应用程序上,为此,我一直试图在实践上找到与React Native相当的应用程序,但没有用。 直到我偶然发现AsyncDisplayKit(现在称为“ Texture”)。

我虽然应该分享到目前为止的经验。


我要搜索的是一个包含以下内容的框架/库:

  • 设计用户界面时的确定性行为。 我对UIKit关于约束冲突的警告感到厌倦,尤其是在使用xib和情节提要时。
  • 易于使用,创建任何UI都需要可理解的代码。
  • 能够调整复杂组件的大小以适合其内容,尤其是在包含图像和属性文本时。 众所周知,在调整包含其他UIView的复杂UIView的大小以适应其全部内容时,UIKit很难掌握。 旋转设备时情况会变得更糟。
  • 它必须能够与可通过Cocoapods获得的大量自定义控件和视图一起使用。
  • 一定要快。 快速,快速。 毕竟,我们是在谈论本机iOS。

Facebook为此提供了ComponentKit和Yoga。 但是,我发现ComponentKit的语法很难使用,尽管它非常快并且受React启发。 在撰写本文时,YogaKit(Yoga的iOS API)仍处于测试阶段,但即使不是,我也发现它的语法有些冗长。

绊倒Texture(以前称为AsyncDisplayKit)感觉就像呼吸新鲜空气。

  • 这很简单。 它的文档只有几页,并且根据9种布局系统(在撰写本文时)来进行节点放置,其中可能最常用的是5种。 Texture中的布局系统可预测,快速且易于使用。
  • 开箱即用即可兼容Objective C和Swift。 没有惊喜。
  • 它具有一个简单的动画引擎,该引擎将使用类似React Native的方法来计算从一种布局到另一种布局的过渡,该方法只需几行代码(通常只有一行)即可在同一视图中更改UI。 涵盖了我四处移动视图时所需的一半情况。
  • 当需要使用自定义控件时,它与UIKit惊人地兼容。
  • 它很快。 快速燃烧。 它进行有关在后台线程中进行渲染的计算,并仅将主线程用于显示。 它使用C ++计算对象放置。 为了达到我使用普通UIKit对Texture所做的性能,与我使用Texture相比,我需要编写数千行代码。

遵循React和Angular在UI组件化方面的领导,Texture大致基于Flexbox布局方法。

一切都是纹理中的节点,它是标准UIView的包装。 ASTableNode是UITableView的包装,ASDisplayNode是UIView的包装,ASImageNode是UIImageView的包装。 ASDisplayNode具有一些属性,这些属性定义诸如调整大小时的行为以及默认大小是什么。 还提供了便利功能,例如背景色,拐角半径等。如果要使用除标准UIView之外的其他功能(例如来自外部框架的视图或可可豆荚),则可以轻松初始化ASDisplayNode并传递同时执行内容视图,这将使您可以在将要使用任何其他ASDisplayNode的任何地方使用它。

节点的放置是通过创建ASLayoutSpec(它们是放置指导者)来确定的。 ASStackLayoutSpec是最常见的。 它将节点内部的节点垂直或水平放置,并通过元素,轴等之间的间距进行参数化。内部的每个组件的大小调整都不会具有与其他组件相同的优先级。 flexGrow为0.5表示此组件的增长将少于堆栈中其余组件的增长(后者为1.0),并且将导致对象之间的大小相似。 堆栈内部所有元素的flex为1.0意味着所有元素将尝试具有相同的大小。 LayousSpecs可以放入其他LayoutSpecs中。

我被要求开发一个具有复杂业务逻辑的应用程序,涉及乘船旅行。 用户将输入起点和终点位置,以及旅程的起点,终点日期和乘客。

因此,我设计了以下屏幕,其中红色方块是ASDisplayNode(由其中的其他节点组成)

用户将能够输入多个路线。 因此,我要做的就是随意创建另一个节点,并在父节点上调用“ refreshLayout()”。 结果:

但是等等,滚动呢? 如果用户添加许多路线怎么办? 那些节点不知道任何父节点。 如果将它们包装在ASScrollNode中(两行代码),则内容和框架将自动计算。 是。 真。 没有更多的自动布局问题。

以下是布置外部内容的代码(设置滚动视图,将内容视图包装在其中)

  self.outerScrollNode.layoutSpecBlock = {(_,_)-> ASLayoutSpec在 
返回ASWrapperLayoutSpec.init(layoutElement:self.contentView)
}

self.node.layoutSpecBlock = {(_,_)-> ASLayoutSpec在
返回ASWrapperLayoutSpec.init(layoutElement:self.outerScrollNode)
}

以下是您在这些屏幕截图中看到的布局的整个布局代码:

 覆盖func layoutSpecThatFits(_ constrainedSize:ASSizeRange)-> ASLayoutSpec { 
让分隔符= ASDisplayNode()
spacer.style.flexGrow = 1.0
spacer.backgroundColor = UIColor.clear
//添加路线和删除路线按钮
让addDeleteRouteButtonsStack = ASStackLayoutSpec.horizo​​ntal()
addDeleteRouteButtonsStack.spacing = 10
addDeleteRouteButtonsStack.justifyContent = .center
addDeleteRouteButtonsStack.alignItems = .center
addDeleteRouteButtonsStack.children = [
ASCenterLayoutSpec(horizo​​ntalPosition:.center,verticalPosition:.center,sizingOption:.minimumWidth,child:addRouteControl),
分隔器,
ASCenterLayoutSpec(水平位置:.center,垂直位置:.center,sizingOption:.minimumWidth,子元素:deleteRouteControl)
]

//手动vstack设置(任意子类的子级)
var vStackChildren = [ASLayoutElement]()
self.requestRouteNodes.forEach {
vStackChildren.append(节点)
}
vStackChildren.append(ASInsetLayoutSpec.init(insets:.init(top:10,left:20,bottom:20,right:10),child:addDeleteRouteButtonsStack))

vStackChildren.append(ASInsetLayoutSpec(insets:UIEdgeInsets(top:0,left:2,bottom:0,right:2),child:self.bottomContentNode))
让verticalStack = ASStackLayoutSpec.vertical()
verticalStack.spacing = 30
verticalStack.children = vStackChildren
返回verticalStack
}

是的 整个布局代码。 layoutSpecThatFits()中的每个单元格都是可重用的组件,具有自己的生命周期,能够计算自己的高度并占用所需的空间。 而且由于调整了每个节点的大小以使用其所需的空间,因此将所有这些节点封装到一个外部节点中,将为ASScrollNode提供正确计算滚动,内容偏移和内容大小所需的所有信息。

您甚至可能不再需要表视图(除非您的列表很大且具有可重复的元素)。

因此,创建大型表单也很容易,因为您不必再​​担心可重复使用的元素。

另一个示例:具有一条路线的旅行,并且启用了另一条多路线的旅行(在用户请求之后)。 注意正方形。 两个彩色正方形表示这是相同的确切节点。 但是通过将相同种类的2个节点封装到一个外部节点(带有白色背景)中,我们给人的印象是这本身就是一个完整的节点

是的,我将其放入表节点中。 无需更改单元格类别。 没有布局问题。 没有不同的单元格。 我只是将两个单元节点放入一个外部节点,以创建一个更大的单元。

使用UIKit不能轻松做到这一点。

当然不是。

Texture是一个强大且极其快速的布局引擎,以Flexbox为蓝本。 Texture擅长的地方是可重用和可重构的用户界面。 但是在开始替换UIKit之前,让我们看一下Texture的一些警告。

纹理不如UIKit灵活,尤其是在设计指示放弃网格布局的情况下。 因此,使用Texture无法实现视差标题,UIKit的物理引擎以及指示放弃网格布局的动画。 您可能还不得不放弃可滑动表格单元之类的事情,或者浪费大量时间实施Apple免费提供的功能。

使用Texture时,您还必须注意代码,与使用UIKit编写的代码相比(尤其是使用情节提要的代码),代码往往变得更大。 您应该保持清晰的编码结构和纪律,以避免长期无法使用其代码。

在团队中工作时,我的印象好坏参半。 我非常感谢Texture的重构功能,与版本控制系统的协作(您是否尝试过使用故事板合并来自多个人员的更改?)以及忘记调整滚动视图中组件大小的能力简直是疯狂的释放。 但是,我不欣赏的是我们正在创建的数百个ASDisplayNode子类,这使导航到该项目变得很困难。

关于性能,我在这篇文章中提到纹理在性能方面非常快。 但是,UIKit也在快速发展。 UIKit擅长的地方在于,即使进行最复杂的动画和转换(尤其是使用iOS 10的新动画引擎),也可以感觉很快。 优秀的软件工程师将能够通过消除无用的AutoLayout计算,从UIKit中榨取大量性能。 使用Texture,可以轻松实现高性能,但是就功能而言,您可能会达到一个顶峰,使您无法继续前进。 您可能很难实现某些应用程序具有的所有漂亮动画,例如弹跳元素,可以与UIPanGestureRecognizer一起使用的好玩的组件,以及老实说,在严格的布局准则下效果不佳的任何动画。

简而言之,在以下情况下,团队应选择“纹理”:

  • 该项目包括许多可重复使用的UI组件,我需要灵活的选择,无论我需要表视图(UITableViewCell)还是视图控制器(普通UIView)内部的视图
  • 该项目将不会使用UIKitDynamics或任何其他布局系统。
  • 他们需要或想要构造可重用的表单,列表,视图
  • 他们不在乎在应用程序实现功能之前就花时间在脚手架上。 在能够创建应用程序将使用的基本组件之前,您将发现您的工作效率受到了影响。 在这一点之后,它将真正得到回报。
  • 团队成员可以将头围在flex box或React的布局引擎上
  • 团队需要极好的UI性能,而不必花费大量时间弄清楚Auto Layout的复杂性。

在以下情况下,任何团队都应避免使用Texture:

  • 他们有一个需要使用UIKitDynamics的项目,或者将来应该具有使用它的能力。
  • 快速原型应做。
  • 团队更喜欢使用情节提要
  • 为了保持一致的文件/项目/变量命名,开发人员团队没有纪律
  • 团队尝试了React的布局系统并对此感到讨厌。

8个月进入Texture(以前称为AsyncDisplayKit)并进行计数,我很喜欢它。 我在这篇文章中几乎没有涉及到它的表面(我建议您访问Texture的网页并亲自查看其余的这些功能),并且随着我对这项技术的熟练程度越来越高,我越来越欣赏它的潜力。

但是,尽管Pinterest广泛使用它,但我认为它将永远无法完全替代UIKit。 但是它是如此强大,值得一试,看看它是否适合您的需求。 我知道,对于两个以上的大型项目,它非常适合我。

重新架构Pinterest的iOS应用

引入Texture,这是AsyncDisplayKit的新家