新大陆:AsyncDisplayKit

image

APP性能的优化,一直都是任重而道远,对于现在须要承载更多信息的APP来讲更是突出,值得庆幸的苹果在这方面作得至少比安卓让开发者省心。UIKit 控件虽然在大多数状况下都能知足用户对于流畅性的需求,但有时候仍是难以达到理想效果。html

AsyncDisplayKit(如下简称ASDK) 的出现至少又给了开发者一个不错的选择。毕竟Paper(虽然 Facebook 已经关闭了这个应用)当年有着炫酷的效果的同时依然保持较好的流畅性也得益于 ASDK 的加入。在Paper发布的几个月后 Facebook 就干脆从中剥离出来成为一个独立的库,就在前两天 ASDK 恰好发布了 2.0 版本。node

目前据我所知国内比较知名有 轻芒阅读(豌豆荚一览) 、 即刻Yep 在用ASDK。
即刻 来讲包括 消息盒子主题的详情页动态通知个人喜欢评论页最近热门即刻小报他关注的人关注他的人以及搜索页 都用到了 ADSK。git

目前 AsyncDisplayKit 已经从 facebook 迁移至 TextureGroup 新的项目地址是 Texturegithub

控件

Texture 几乎涵盖了经常使用的控件,下面是 TextureUIKit 的对应关系,有些封装能够说很是良心。objective-c

Nodes:api

Texture UIKit
ASDisplayNode UIView
ASCellNode UITableViewCell/UICollectionViewCell
ASTextNode UILabel
ASImageNode UIImageView
ASNetworkImageNode UIImageView
ASVideoNode AVPlayerLayer
ASControlNode UIControl
ASScrollNode UIScrollView
ASControlNode UIControl
ASEditableTextNode UITextView
ASMultiplexImageNode UIImageView

Node Containers网络

Texture UIKit
ASViewController UIViewController
ASTableNode UITableView
ASCollectionNode UICollectionView
ASPagerNode UICollectionView

子父类关系:app

  • ASDisplayNodeasync

    • ASCellNodeide

      • ASTextCellNode
    • ASCollectionNode

      • ASPagerNode
    • ASControlNode

      • ASButtonNode
      • ASImageNode

        • ASMapNode
        • ASMultiplexImageNode
        • ASNetworkImageNode

          • ASVideoNode
      • ASTextNode
      • ASTextNode2
    • ASEditableTextNode
    • ASScrollNode
    • ASTableNode
    • ASVideoPlayerNode

ASDisplayNode:

做用同等于UIView,是全部 Node 的父类,须要注意的是 ASDisplayNode 其实拥有一个view属性,因此ASDisplayNode及其子类均可以经过这个view来添加UIKit控件,这样一来 TextureUIKit混用是彻底没问题的。

ASDisplayNode 中添加 UIKit

UIView *otherView = [[UIView alloc] init];
otherView.frame = ...;
[self.view addSubview:otherView];

ASDisplayNode *gradientNode = [[ASDisplayNode alloc] initWithViewBlock:^UIView * _Nonnull{
    UIView *view = [[UIView alloc] init];
    return view;
}];

第二种的初始化最终生成的就是 block 返回的 UIKit 对象,但外部表现出来的是 ASDisplayNode。这样子的好处在于布局,关于布局,后面会讲到。

UIKit 中添加 ASDisplayNode

ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.image = [UIImage imageNamed:@"iconShowMore"];
imageNode.frame = ...;
[self addSubnode:imageNode];
self.imageNode = imageNode;

ASCellNode:

做用同等于 UITableViewCellUICollectionViewCell,自带 indexPath 属性,有些时候颇有用。

ASTextNode

做用同等于UILabel,和 UILabel 不一样的是 ASTextNode 必须经过 attributedText 添加文字。

ASTextNode2

在 ASTextNode 基础修复了一些 Bug

ASImageNode

做用同等于 UIImageView,可是只能设置静态图片,若是须要使用网络图片,请使用 ASNetworkImageNode

ASNetworkImageNode

做用同等于 UIImageView,若是使用网络图片请使用此类,Texture 用的是第三方的图片加载库PINRemoteImageASNetworkImageNode 其实并不支持 gif,若是须要显示 gif 推荐使用FLAnimatedImage

ASButtonNode

做用同等于 UIButton,须要注意的是下面这个两个属性

@property (nonatomic, assign) CGFloat contentSpacing;// 设置图片和文字的间距
@property (nonatomic, assign) ASButtonNodeImageAlignment imageAlignment;// 图片和文字的排列方式,

简直要抱头痛哭一下😭,imageAlignment 能够设置两个值:

ASButtonNodeImageAlignmentBeginning, // 图片在前,文字在后
ASButtonNodeImageAlignmentEnd// 文字在前,图片在后

ASTableNode

做用同等于 UITableView,可是实现上并无采用 UITableView 的重用机制,而是经过用户滚动对须要显示的视图进行add 和 不须要的进行remove 的操做(我猜的)。另外重要的一点:ASTableNode 并无像 UITableView 同样提供一个-tableView:heightForRowAtIndexPath:协议方法来决定每一个 Cell 的高度,而是由 ASCellNode 自己决定。这样带来的另一个好处是,动态高度的实现可谓是易如反掌,具体能够看官方 Demo 中的 Kittens

如何正确的使用

对于现有的项目中出现的并不严重的性能问题,个人建议是用对应的 Texture 控件代替便可。

好比把 UIImageView -> ASImageNode/ASNetworkImageNodeUILabel -> ASTextNode之类的,而不是把原有的 UITableView -> ASTableNodeUICollectionView -> ASCollectionNode

在 Cell 中替换 UIImageView

ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.image = [UIImage imageNamed:@"iconShowMore"];
imageNode.frame = ...;
[self.contentView addSubnode:imageNode];
self.imageNode = imageNode;

缘由有如下几点:

  1. ASCellNode内部的布局会用到 Texture 自己有一套布局方案,然而这套布局学习成本略高。
  2. 包括 ASTableNodeASCollectionNode 和原生的 UITableViewUICollectionView有较大的 API 改变,侵略性较大,不太利于后期维护。
  3. 第三方的支持问题,例如 DZNEmptyDataSet 对于 ASTableNodeASCollectionNode 的支持仍是有点问题。

因此当你尚未作好应付上面三个问题的准备,简单的 UIKit -> Texture 替换才是正确选择。

布局

阅读 Texture 布局篇

其余

刷新列表

不管是 ASTableNode 仍是 ASCollectionNode 当列表中已经有数据显示了,调用 reloadData 你会发现列表会闪一下。最多见的案例是上拉加载更多获取到新数据后调用 reloadData 刷新列表用户体验会比较差,事实上官方文档在 [Batch Fetching API] 给出了解决办法:

- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context 
{
  // Fetch data most of the time asynchronoulsy from an API or local database
  NSArray *newPhotos = [SomeSource getNewPhotos];

  // Insert data into table or collection node
  [self insertNewRowsInTableNode:newPhotos];

  // Decide if it's still necessary to trigger more batch fetches in the future
  _stillDataToFetch = ...;

  // Properly finish the batch fetch
  [context completeBatchFetching:YES];
}

获取新数据后直接插入到列表中,而不是刷新整个列表,好比:

- (void)insertSections:(NSIndexSet *)sections withRowAnimation:(UITableViewRowAnimation)animation;

- (void)insertRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;

加载数据

细心的同窗可能发现了前面提到内容中的就有相关的方法:

- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context;

如今绝大多数APP加载更多数据的方法都是经过下拉到列表底部再去请求数据而后添加到列表中,可是 Texture 提供了另一种更“合理”的方式,原文是这样描述的:

By default, as a user is scrolling, when they approach the point in the table or collection where they are 2 “screens” away from the end of the current content, the table will try to fetch more data.

当列表滚到到距离底部还有两个屏幕高度请求新的数据,这个阈值是能够调整的。一旦距离底部达到两个屏幕的高度的时候,就会调用前面提到的方法。因此用起来大概是这样的:

- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context{
    [context beginBatchFetching];
    [listApi startWithBlockSuccess:^(HQHomeListApi *request) {
        @strongify(self);
        NSArray *array = [request responseJSONObject];
        [self.dataSourceArray addObjectsFromArray:array];
        [self.tableNode insertSections:[NSIndexSet indexSetWithIndexesInRange:rang] withRowAnimation:UITableViewRowAnimationNone];
        [self updateHavMore:array];
        [context completeBatchFetching:YES];
    } failure:NULL];
}

- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode{
    return self.haveMore;
}

shouldBatchFetchForTableNode 用来控制是否须要获取更多数据。这种方式优势在于在网络情况好的状况下用户都不会感觉到已经加载了其余数据并显示,缺点在于网络情况很差的状况下用于即便列表已经下拉到底部也没有任何提示。

相关文章
相关标签/搜索