[关闭]
@CoDancer 2018-04-25T14:29:51.000000Z 字数 3286 阅读 1414

IOS开源库学习笔记 -- IGListKit

SDK 源码阅读


介绍:IGListKit是由Instagram在Instagram创建的数据驱动的UICollectionView框架。 通过这个框架,您可以提供一系列在UICollectionView中显示的对象。 对于每种类型的对象,适配器创建一个名为section controller的内容,该控件具有创建单元格的所有细节。

5320748-d619be8a6de01a05.png

下载源码

源码地址

可能出现的问题
Error: SwiftLint not installed!Download from https://github.com/realm/SwiftLint,or brew install swiftlint.

打开终端,输入brew install swiftlint即可

在手机上运行后,首页是这个样子的。

DemosViewController

A60FCAD15528CF8172220391DB1E6980.png

很明显这是一个只有一个section的视图,包含cell。

在DemosViewController中需要实现ListAdapterDataSource(数据源协议),
并且一个UIcollection对象和一个ListAdapter对象,并且把UIcollection对象作为ListAdapter对象的视图属性,并在该控制器中实现代理方法。

思考:原生的ios是在控制器中通过数据去初始化界面,而这个IGListKit是通过适配器对象,实现对象中的代理方法,并传入视图对象。在实现代理方法中需要传入特定的对象数组;在方法中返回ListSectionController的对象,而具体完成对视图Cell的数据配置,点击事件;和返回当视图是空的视图对象。

数据源对象

  1. let demos: [DemoItem]
  2. func objects(for listAdapter: ListAdapter) -> [ListDiffable] {
  3. return demos
  4. }
而IGListDiffable是协议,demos是DemoItem的对象数组,这时候需要
extension DemoItem: ListDiffable作为ListDiffable的扩展,
这儿的extension在swift的用法可以参考,同时需要实现协议中的方法。

extension的用法

ListSectionController类的继承

在代理方法中返回ListSectionController的对象,自定义的类继承ListSectionController并重写里面的方法。

  1. override func sizeForItem(at index: Int) -> CGSize
  2. override func cellForItem(at index: Int) -> UICollectionViewCell
  3. override func didUpdate(to object: Any)

主要是视图的构建和点击cell的响应。

LoadMoreViewController

这个是有tail loading的界面,在上面的基础之上,另外实现了scrollViewDelegate的代理方法,而在这个控制器中主要包含两种数据对象和对应下的ListSectionController,在我们上拉加载的时候会执行

  1. let distance = scrollView.contentSize.height - (targetContentOffset.pointee.y + scrollView.bounds.height)
  2. if !loading && distance < 200 {
  3. loading = true
  4. adapter.performUpdates(animated: true, completion: nil)
  5. DispatchQueue.global(qos: .default).async {
  6. // fake background loading task
  7. sleep(2)
  8. DispatchQueue.main.async {
  9. self.loading = false
  10. let itemCount = self.items.count
  11. self.items.append(contentsOf: Array(itemCount..<itemCount + 5))
  12. self.adapter.performUpdates(animated: true, completion: nil)
  13. }
  14. }
  15. }
而这个adapter.performUpdates(animated: true, completion: nil)
相关于reloadData。
  1. DispatchQueue.global(qos: .default).async {
  2. sleep(2)
  3. DispatchQueue.main.async {
  4. }
  5. }

在一个异步队列中读取数据, 然后再返回主线程更新 UI.

这里开启一个异步队列去处理后台的可能获取数据的网络耗时操作,这样防止主线程被堵塞,当获得数据后再获取主线程更新UI。

然后这里看到了关于block的一些知识,刚好之前在面试的时候被问到关于block的底层原理的知识,当时还真不知道,在这儿就记录一下block的原理。

  1. int main() {
  2. int count = 10;
  3. void (^ blk)() = ^(){
  4. NSLog(@"In Block:%d", count);
  5. };
  6. blk();
  7. }

当然这是一个很简单的block的方法,但是经过clang后,可以看到

  1. static void __main_block_func_0(
  2. struct __main_block_impl_0 *__cself) {
  3. int count = __cself->count; // bound by copy
  4. NSLog((NSString *)&__NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_d2f8d2_mi_0,
  5. count);
  6. }

主要是__main_block_impl_0这个block的方法,在来看这个方法的底部结构:

  1. struct __main_block_impl_0 {
  2. struct __block_impl impl;
  3. struct __main_block_desc_0* Desc; //描述Block大小、版本等信息
  4. int count;
  5. //构造函数函数
  6. __main_block_impl_0(void *fp,
  7. struct __main_block_desc_0 *desc,
  8. int _count,
  9. int flags=0) : count(_count) {
  10. impl.isa = &_NSConcreteStackBlock; //在函数栈上声明,则为_NSConcreteStackBlock
  11. impl.Flags = flags;
  12. impl.FuncPtr = fp;
  13. Desc = desc;
  14. }
  15. };

首先有一个impl的__block_impl的结构体,一个__main_block_desc_0描述Block大小、版本等信息。
block其实是一个对象,而这个对象的定义是这样的

  1. struct __block_impl {
  2. void *isa;//指明对象的Class
  3. int Flags;
  4. int Reserved;
  5. void *FuncPtr;
  6. };

观察上节代码中__main_block_impl_0结构体(main栈上Block的结构体)的构造函数可以看到,栈上的变量count以参数的形式传入到了这个构造函数中,此处即为变量的自动截获。
因此可以这样理解:__block_impl结构体已经可以代表Block类了,但在栈上又声明了__main_block_impl_0结构体,对__block_impl进行封装后才来表示栈上的Block类,就是为了获取Block中使用到的栈上声明的变量(栈上没在Block中使用的变量不会被捕获),变量被保存在Block的结构体实例中。
所以在blk()执行之前,栈上简单数据类型的count无论发生什么变化,都不会影响到Block以参数形式传入而捕获的值。但这个变量是指向对象的指针时,是可以修改这个对象的属性的,只是不能为变量重新赋值。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注