TableVIew可能是我们日常开发中最经常用到的控件,随着一遍一遍重复无水准的写TableView,反正我是感到很无趣,所以自己写了一个BaseTableView,包含最基本的一些方法,如果还有更多的需求,再行扩充吧。进入正题
本文Demo地址 :github.com/LYFsy/BaseTableView
首先看一下目录:
有关tableView的封装,都集中在了这三个目录,通过这三个功能,我们可以很清晰的看出每个部分封装的功能。
EmptyTableView(显示占位信息):
EmptyTableView中主要是赋予TableView显示占位信息的功能。这个功能集成了第三方DZNEmptyDataSet,在使用这个第三方的时候,我是取消了下拉刷新的功能,而是采用DZNEmptyDataSet提供的点击占位文本或者点击按钮进行刷新,(个人习惯,感觉显示暂位信息的时候有下拉刷新很突兀。)这个第三方文档介绍的还是蛮清楚的。这里就不多说明用法了。在BaseEmptyTableView的.h文件中,已经提供了一些关于占位信息的属性和默认信息,如果每个TableView的占位信息不同的话,可以在各自的Controller中重新复值。.h文件中有一个isLoading属性,用来判断是否正在加载数据,同时我也使用了KVO来监听了这个属性,当isLoading == YES时,执行DZEmptyDataSet中的reloadEmptyDataSet方法。这个方法只有在datasource是空时,才会刷新,和TableView或者CollectionView的reloadData是不相同的。
以上这段代码说明了刷新的必要条件:
1:遵守了协议<DZNEmptyDataSetSource>。[self dzn_canDisplay]
2:允许显示占位信息,[self dzn_shouldDisplay],当我们遵守<DZNEmptyDataSetDelegate>协议时,有一个代理方法:
- (BOOL)emptyDataSetShouldDisplay:(UIScrollView *)scrollView;
用于询问是否允许开启占位信息的功能,在BaseEmptyTableView中我是用了isAllowDisplayPlaceholder属性来表示的,默认是YES。
3:数据源的数据为0,[self dzn_itemsCount] == 0。在dzn_itemsCount这个方法中,只有当前控件是UITableView和UICollectionView的时候才会继续执行。并且会根据section的数量 以及每个section中的row或者item。计算出总共的数量items,只有items == 0时,才会继续执行。
至于为什么刷新TableView的时候,会显示/隐藏 占位信息,是因为DZNEmptyDataSet中有一个方法拦截了tableview的reloadData。在我们设置DZNEmptyDataSet的DataSource的时,
当执行self.emptyDataSetSource的时候,会执行一下方法:
这两个方法其实是使用了runtime,偷换了reloadData和endUpdates这两个方法的指针。当我们执行reloadData/endUpdates方法的时候,其实我们是执行了以下方法:
至于是如何实现的,这里不做深究,有兴趣的可以自己看一下源码。通过这个方法我们可以看出,在执行reloadData的时候,进过以上方法的拦截注入,会再次执行步骤1:[self dnz_reloadEmptyDataSet]方法,dnz_reloadEmptyDataSet方法里面会再次判断占位图的相关逻辑处理,包括显示/隐藏。至于步骤二,这个impPointer是IMP类型的,指向原方法(reloadData/endupdatas)的函数指针,强转为((void)(*)(id,SEL))类型执行,其实就是继续执行reloadData/endupdates 方法。(这一段纯个人理解,如有偏差,请见谅)。
BaseTableView:(TableView的数据源代理):
BaseTableView相对要简单很多,内部代码其实就是我们经常写的tableview的数据源方法和代理方法。
由于只是Demo,所以并没有写过多的代理方法,如TableView处于编辑模式时的逻辑处理,以及数据源是分组的情况。需要的时候再自行扩充吧。BaseTableView中主要的就是2个block和一个方法需要看一下:
//渲染cell
typedef UITableViewCell *(^CallbackCell)(UITableViewCell * cell,NSIndexPath * indexPath);
//点击cell
typedef void(^CallbackDidSelectedCell)(UITableViewCell * cell,NSIndexPath * indexPath);
- (instancetype)initWithFrame:(CGRect)frame callbackIdentifier:(NSString * (^)(void))callbackIdentifier;
BaseTableView在渲染Cell的时候,我是通过一个block,让持有TableView的Controller自己实现逻辑处理,赋值,并返回指定类型的Cell进行渲染。
点击Cell的代理方法也是通过一个block进行逻辑处理的。
这三个方法中唯一需要注意的就是- (instancetype)initWithFrame:(CGRect)frame callbackIdentifier:(NSString * (^)(void))callbackIdentifier; 这个方法要求我们在初始化tableView的时候一定要传入重用标识符,否则tableview的重用机制就失效了。
BaseRefreshTableView:(集成MJRefresh的刷新加载功能)
BaseRefreshTableView继承自BaseTableView,集成MJRefresh,实现刷新加载功能,整体都比较简单,唯一要注意的可能就是- (void)tableViewDidFinishRefreshByDragDown:(BOOL)isDragDown ,在这个方法里面,刷新或者加载数据的操作,是通过代理实现的,这个代理是在BaseEmptyTableView中声明的,之所以放在这个BaseEmptyTableView中声明,是为了让BaseEmptyTableView中的刷新数据的操作和BaseRefreshTableView刷新数据的操作是同一个方法。
至此,感觉需要讲的都讲了,说实话,对于我这个好几年没正儿八经的写文章的来说,真心有点困难,之前也没怎么写过,排版可能也很难难看,慢慢改进,大家多多包涵。如果还有什么问题可以随时联系我。(终于写完了,好累~~~~~)
最后再附上Demo地址:github.com/LYFsy/BaseTableView