[iOS开发]IGListKit框架初学

39 篇文章 0 订阅

大多数 iOS 应用中列表都是通过 UICollectionView 实现。虽然它性能强大,API 丰富易用,但依旧存在一些不足。常见的比如,更新页面时如果调用 reloadData 会造成屏幕闪烁。IGListKit主流的使用和例子都是Swift的,接下来,我就通过我的一个朋友圈仿写来为大家介绍一下其使用Objective-C来完成的使用过程。先看一下ViewController里面的使用方法:

- (IGListAdapter *)adapter {
    if (!_adapter) {
        /**
         默认初始化IGListAdapter
         参数1:IGListAdapterUpdater,是一个遵循了IGListUpdatingDelegate的对象,它处理每行更新。
         参数2:viewController,是包含IGListAdapter的UIViewController。 可以用来push到其他控制器
         参数3:workingRangeSize是工作范围的大小,它可以让你为刚好在可见范围之外的视图做一些准备工作,暂时没用到给0。
         */
        _adapter = [[IGListAdapter alloc] initWithUpdater:[[IGListAdapterUpdater alloc] init] viewController:self workingRangeSize:0];
    }
    return _adapter;
}

Model数据模型

[self.view addSubview:self.collectionView];
//给adapter赋值collectionView
self.adapter.collectionView = self.collectionView;
//给adapter赋值dataSource
self.adapter.dataSource = self;

其中里面IGListAdapterUpdater是数据控制协议,collectionView也是放在里面作为其属性。然后需要按照需要定义自己项目需要的数据模型。给出我的用户的模型例子:

@interface AuthorModel : NSObject
<IGListDiffable>

@property (nonatomic, strong) NSNumber *authorId;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *headImageUrl;
@property (nonatomic, strong) NSString *pos;

- (instancetype)initWithId:(NSNumber *)authorId name:(NSString *)name headImage:(NSString *)headImage;
@end

@implementation AuthorModel

- (instancetype)initWithId:(NSNumber *)authorId name:(NSString *)name headImage:(NSString *)headImage {
    if (self = [super init]) {
        self.authorId = authorId;
        self.name = name;
        self.headImageUrl = headImage;
        self.pos = [self uuid];
    }
    return self;
}

- (nonnull id<NSObject>)diffIdentifier {
    return self.pos;
}

- (NSString *)uuid {
    CFUUIDRef uuid_ref = CFUUIDCreate(NULL);
    CFStringRef uuid_string_ref = CFUUIDCreateString(NULL, uuid_ref);
    NSString *uuid = [NSString stringWithString:(__bridge NSString *)uuid_string_ref];
    CFRelease(uuid_ref);
    CFRelease(uuid_string_ref);
//    NSLog(@"%@", uuid);
    return [uuid lowercaseString];
}


- (BOOL)isEqualToDiffableObject:(AuthorModel *)object {
    if (self == object) {//指针地址比较
        return YES;
    }
    if (![object isKindOfClass:[self class]]) {
        return NO;
    }
    return [self.pos isEqualToString:object.pos];
}

@end

需要注意的是model类都实现了IGListDiffable协议,该协议的作用是使用额外一些内存空间,降低时间复杂度到O(n),并且能准确获取所有Insert/Delete/Move操作。内部用哈希的方式实现。协议中需要实现两个数据Model的对比方法以判断其异同。例如Model中有数组元素,可以先判断数据个数,再循环判断数组里面的对象。在初次尝试时,用户的diffIdentifier我使用的是他的authorId,但是这样会导致一个问题。我们的“朋友圈”每个用户只能出现一次,因为它会判定你这是一个人,拒绝重复出现。我投机取巧,使用UUID作为判定一个人的标志,因为我们实际上是可以让一个人发好几次朋友圈的。在实际的应用中,把每个朋友圈的信息和朋友圈的id绑定上,或许是一个很好的解决办法,为了美观,也可以将每个朋友圈的信息整合到一个section中,毕竟更加美观且符合人类逻辑。这里的例子仅供学习参考,十分简陋,没有考虑太多。

IGListAdapterDataSource

看过了数据模型的定义方法,接下来,我们看一下IGListAdapterDataSource的数据源方法。

// MARK: IGListAdapterDataSource
//对于不同listAdapter的数据
- (NSArray<id<IGListDiffable>> *)objectsForListAdapter:(IGListAdapter *)listAdapter {
    if ([self.selectedClass isEqualToString:self.segments[0][1]]) {
        return self.data;
    }
    
    NSMutableArray *arrM = [NSMutableArray array];
    Class class = NSClassFromString(self.selectedClass);
    for (id obj in self.data) {
        if ([obj isKindOfClass:class]) {
            [arrM addObject:obj];
        }
    }
    return arrM;
}
//对于不同的object返回不同的sectionController
- (IGListSectionController *)listAdapter:(IGListAdapter *)listAdapter sectionControllerForObject:(id)object {
    if ([object isKindOfClass:[ContentModel class]]) {
        return [ContentSectionController new];
    } else if ([object isKindOfClass:[ImageModel class]]) {
        return [ImageSectionController new];
    } else {
        return [AuthorSectionController new];
    }
}
//没有数据返回的视图
- (UIView *)emptyViewForListAdapter:(IGListAdapter *)listAdapter {
    return nil;
}

Model的不同状态来返回不同的控制类。以实现对不同布局逻辑的代码分离。每一个Section具体到不同的SectionController里面去。

SectionController

我在这里使用显示图片的Section演示如何使用SectionController

- (instancetype)init {
    self = [super init];
    if (self) {
        self.minimumInteritemSpacing = 0;
        self.minimumLineSpacing = 0;
        self.inset = UIEdgeInsetsMake(0, 55, 0, 55);
    }
    return self;
}

- (NSInteger)numberOfItems {
    //相当于UICollectionView一个indexPath.setion中有几个row
    return _model.imageUrls.count;
}

- (CGSize)sizeForItemAtIndex:(NSInteger)index {
    CGFloat width = [self.collectionContext containerSize].width - 110;
    CGFloat itemSize = floor(width / 3);
    //返回cell大小
    return CGSizeMake(itemSize, itemSize);
}

- (UICollectionViewCell *)cellForItemAtIndex:(NSInteger)index {
    //返回cell
    ImageCell *cell = [self.collectionContext dequeueReusableCellOfClass:[ImageCell class] forSectionController:self atIndex:index];
    [cell.myImageView sd_setImageWithURL:_model.imageUrls[index] placeholderImage:nil options:SDWebImageProgressiveLoad completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
        cell.myImageView.image = [self cropSquareImage:image];
    }];
    return cell;
}

- (void)didUpdateToObject:(id)object {
    //绑定model
    _model = object;
}

- (void)didSelectItemAtIndex:(NSInteger)index {
    //一个row的点击事件
    return;
}

在每个SectionController里面,控制具体的index要什么类型的cell,再创建cell放上去。这样逻辑一层层下放分离,十分厉害。

更新方法:

section中

可以在具体的sectionController里面实现更新具体indexcell的方法:

[self.collectionContext performBatchAnimated:YES updates:^(id<IGListBatchContext>  _Nonnull batchContext) {
    [batchContext reloadInSectionController:self atIndexes:[NSIndexSet indexSetWithIndex:0]];
} completion:nil];

ViewController中

[self.adapter performUpdatesAnimated:YES completion:nil];

效果图:

我这个是仿照的朋友圈,又按照别人的demo更改了一下下数据处理,我们一起来看一下效果。请添加图片描述
对于不同的cell我们可以展开收起,实现了基于计算高度的自适应行高。我们还可以使用上面的控制器选择器来筛选不同的数据类型:请添加图片描述

请添加图片描述请添加图片描述
以上就是我对IGListKit框架初次学习的总结啦!这个框架的动画我真的是爱了爱了😍😍😍

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值