新大陸: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 用來控制是否須要獲取更多數據。這種方式優勢在於在網絡情況好的狀況下用戶都不會感覺到已經加載了其餘數據並顯示,缺點在於網絡情況很差的狀況下用於即便列表已經下拉到底部也沒有任何提示。

相關文章
相關標籤/搜索