iOS页面卡顿优化

面临的问题

最近看到一个页面卡顿优化的框架,叫AsyncDisplayKit,最开始是 Facebook 公司的 Paper 应用程序。它是为了解决 Paper 团队面临的核心问题之一:尽可能缓解主线程的压力。它能在异步线程绘制修改UI,然后统一添加进内存渲染出来。而之前所有针对UI渲染的操作就是在主线程进行的。主要解决CPU或GPU消耗过大,导致在一次同步信号之间没有准备完成,没有内容提交,导致掉帧的问题

优化原理

1,系统对象创建与销毁:

    UIKit组件封装了CALayer图层的对象,在创建、调整、销毁的时候,都会在主线程消耗CPU的资源,AsyncDisplayKit框架自己设计了一套Node机制,也能够调用。

2,布局:

    iOS自带的Autolayout在布局性能上存在瓶颈,并且只能在主线程进行计算。参考AutoLayout Performance on iOS,因此AsyncDisplayKit弃用了Autolayout,自己设计一了套布局方式,叫ComponenKit。

3,渲染:

    对于大量文本、图片渲染,UIKit组件只能在主线程并且可能会造成GPU绘制的资源紧张。AsyncDisplayKit使用了一些方法,图层预混合,并且异步的在后台绘制图层,不阻塞主线程的运行。

核心组件

AsyncDisplayKit的核心组件包括:

ASDK Node UIKit Equivalent
ASDisplayNode 代替UIKit的 UIView ,其他的node都继承于它
ASCellNode 代替UIKit的 UITableViewCell 和 UICollectionViewCell,用在 ASTableNode , ASCollectionNode 和 ASPagerNode
ASScrollNode 代替UIKit的 UIScrollView,这个 node 用在自定义滚动区域上非常有用
ASEditableTextNode 代替UIKit的 UITextView
ASTextNode 代替UIKit的 UILabel
ASImageNode 代替UIKit的 UIImage
ASNetworkImageNode
ASMultiplexImageNode
ASVideoNode 代替UIKit的 AVPlayerLayer
ASVideoPlayerNode 代替UIKit的 UIMoviePlayer
SControlNode 代替UIKit的 UIControl
ASButtonNode 代替UIKit的 UIButton
ASMapNode

代替UIKit的 MKMapView

ASViewController 代替UIKit的UIViewController

你可以用这些相关的类来替代相应的UIKit中对应的类,这样会带来性能上的提高。(这是使用AsyncDisplayKit的原因,实际上是提供了十来种种高效的类供我们使用)

安装

CocoaPods安装

pod 'AsyncDisplayKit'

Carthage安装

AsyncDisplayKit可以使用Carthage安装,将下面的代码添加进入Cartfile

github "facebook/AsyncDisplayKit"

在终端执行carthage update来构建AsyncDisplayKit库,会自动在项目根目录下生成Carthage名字的文件夹,里面有个build文件夹,可以用来framework到你打算使用的项目中

使用方式

主要介绍常用控件ASTableNode/ASCollectionNode的使用,代码放在GitHub上的ASDK_Demo

UIImageView

_imageNode = [[ASImageNode alloc] init];
_imageNode.backgroundColor = [UIColor lightGrayColor];
_imageNode.image = [UIImage imageNamed:@"hello"];
_imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
[self.view addSubview:_imageNode.view];

UIButton

// create the node
_shuffleNode = [[ASTextNode alloc] init];
_shuffleNode.attributedString = string;

// configure the button
_shuffleNode.userInteractionEnabled = YES; // opt into touch handling
[_shuffleNode addTarget:self
action:@selector(buttonTapped:)
forControlEvents:ASControlNodeEventTouchUpInside];

UITableView

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "PhotoModel.h"
#import "TableCellNode.h"
@interface ASDKTableViewController () <ASTableDelegate, ASTableDataSource>
@property (nonatomic, strong) ASTableNode *tableNode;
@end
@implementation ASDKTableViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"ASTableNode";
    [self initViews];
}

- (void)initViews {
    self.tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
    _tableNode.dataSource = self;
    _tableNode.delegate = self;
    _tableNode.frame = self.view.bounds;
    [self.view addSubnode:_tableNode];  //等效:[self.view addSubview:_tableNode.view];
    //ASTableNode 不会暴露所有UITableView的的属性,所以你必须通过 tableNode 底层的 UITableView 实例去设置 UITableView 的特殊属性。
    _tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
    //无限滚动需要
    //将 leadingScreensForBatching 设置为 1.0 表示当用户滚动还剩 1 个全屏就到达页尾时,开始抓取新的一批数据。
    self.tableNode.view.leadingScreensForBatching = 1.0;  // default of 2.0
}

#pragma mark - TableNode Delegate
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section {
    return _dataArray.count;
}
/**
 *  不支持复用
 *  该方法优先于 tableNode:nodeForRowAtIndexPath:
 */
- (ASCellNodeBlock)tableNode:(ASTableNode *)tableNode nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    PhotoModel *model = [[PhotoModel alloc] init];
    model.text = [NSString stringWithFormat:@"Row : %ld", indexPath.row];
    model.imgUrl = _dataArray[indexPath.row];
    // this may be executed on a background thread - it is important to make sure it is thread safe
    ASCellNode *(^cellBlock)() = ^ASCellNode *() {
        TableCellNode *cellNode = [[TableCellNode alloc] initWithData:model];    
        return cellNode;
    };
    return cellBlock;
}
//添加约束
- (ASSizeRange)tableNode:(ASTableNode *)tableNode constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    CGSize min = CGSizeMake(width, 380);
    CGSize max = CGSizeMake(width, CGFLOAT_MAX);
    return ASSizeRangeMake(min, max);
}
//点击事件
- (void)tableNode:(ASTableNode *)tableNode didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableNode deselectRowAtIndexPath:indexPath animated:YES];
}

UIColletionView 

#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import "CollectionCellNode.h"
#import "PhotoModel.h"
@interface ASDKCollectionViewController () <ASCollectionDelegate, ASCollectionDataSource, UICollectionViewDelegateFlowLayout>
@property (nonatomic, strong) ASCollectionNode *collectionNode;
@end

@implementation ASDKCollectionViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    self.title = @"ASDKCollectionView";
    [self initViews];
}

- (void)initViews {
    UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.minimumInteritemSpacing = 7;
    flowLayout.minimumLineSpacing = 8;
    self.collectionNode = [[ASCollectionNode alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
    _collectionNode.backgroundColor = [UIColor whiteColor];
    _collectionNode.dataSource = self;
    _collectionNode.delegate = self;
    //不支持复用
//    [_collectionNode.view registerClass:[CollectionCellNode class] forCellWithReuseIdentifier:@"collectID"];
    [self.view addSubnode:_collectionNode];
    YYFPSLabel *fpsLabel = [YYFPSLabel new];
    fpsLabel.frame = CGRectMake(200, 200, 50, 30);
    [self.view addSubview:fpsLabel];
}
#pragma mark - ASCollectionNode Delegate
- (NSInteger)collectionNode:(ASCollectionNode *)collectionNode numberOfItemsInSection:(NSInteger)section {
    return _dataArray.count;
}
- (ASCellNodeBlock)collectionNode:(ASCollectionNode *)collectionNode nodeBlockForItemAtIndexPath:(NSIndexPath *)indexPath {
    PhotoModel *model = [[PhotoModel alloc] init];
    model.text = [NSString stringWithFormat:@"Row : %ld", indexPath.row];
    model.imgUrl = _dataArray[indexPath.row];
    return ^() {
        CollectionCellNode *cellNode = [[CollectionCellNode alloc] initWithData:model];
        return cellNode;
    };
}
//添加约束
- (ASSizeRange)collectionNode:(ASCollectionNode *)collectionNode constrainedSizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    CGFloat width = ([UIScreen mainScreen].bounds.size.width - 32) / 3;
    CGSize min = CGSizeMake(width, width);
    CGSize max = CGSizeMake(width, width);
    return ASSizeRangeMake(min, max);
}
//设置Cell间距
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
    return UIEdgeInsetsMake(8, 8, 8, 8);
}
@end

猜你喜欢

转载自blog.csdn.net/humiaor/article/details/129274789