UITableView基本上是使用最多的一个控件,今天介绍封装后的 WMZTableView 省去写代理的时间
先直接上代码
GroupTableView() .wAutoCell(YES) .wMasonryConfig(self.view, ^(MASConstraintMaker *make) { make.edges.mas_equalTo(0); }, self.modelArr) .wStart();
不需要写cell,是不是简化了不少,比起写那么多的代理方法,最精简的可以只传frame和data即可。
效果图是我随便写的几个自定义的cell,别介意。
用法
可以看demo了解基本都很简单清晰
1 如果要在UITableViewCell里自己写样式 需调用 wDealCell ,大概如下
.wDealCell(^UITableViewCell *(NSIndexPath *indexPath, UITableView *tableView,id model) { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([UITableViewCell class])]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:NSStringFromClass([UITableViewCell class])]; } cell.textLabel.text = model; return cell; })
2 如果要用特殊式写法开启,这种写法不需要手动创建cell,只需要传model过去即可
.wAutoCell(YES)
这种方法就是传进去的数据源带的model一定要是wBaseModel或者集成它的类
还有自定义cell要带wBaseModel属性
3 如果tableview是需要用section的 比如多少个section和每个section多少rows 需用 wSection ,默认是row模式
就是section只有一个,row的个数是传进来的数组的个数
.wSection(YES)
而且传进的modelArr的格式需要是多个子数组组成的数组
@[@[],@[],@[]]
3 开启头视图和尾视图高度和视图的定制
//cell高度自定义 默认是用masonry的自动布局 .wCellHeight(^NSInteger(NSIndexPath *indexPath, UITableView *tableView) { return indexPath.row == 0 ? 200:UITableViewAutomaticDimension; }) //cell尾部高度自定义 默认0.01 .wCellFootHeight(^NSInteger(NSInteger section, UITableView *tableView) { return 50; }) //cell尾部视图自定义 默认无 .wCellFootView(^UIView *(NSInteger section, UITableView *tableView) { UIView *view = [UIView new]; view.backgroundColor = [UIColor yellowColor]; return view; }) //cell头部高度自定义 默认0.01 .wCellHeadHeight(^NSInteger(NSInteger section, UITableView *tableView) { return 50; }) //cell头部视图自定义 默认无 .wCellHeadView(^UIView *(NSInteger section, UITableView *tableView) { UIView *view = [UIView new]; view.backgroundColor = [UIColor redColor]; return view; })
4 最重要的一点
wStart 属性 需放在最后才有效果,里面执行tableview的delegate和datasource方法
开始解释一下这个类的主要思路
1 我一开始的写法为什么看不到cell的定制的方法
数据源 modelArr 带的是自定义的 wBaseModel ,里面携带属性自定义cell类名
wBaseModel 可以继承这个类然后自己自定义
wBaseModel *model = [wBaseModel new]; //自定义cell的类名 model.cellName = @"NornalCellOne"; //带过去的参数 这些就不用说了 model.labelName = @"NornalCellOne的文本nNornalCellOne的文本"; //带过去的参数 这些就不用说了 model.imageName = [NSString stringWithFormat:@"%d.jpg",x3];
然后在tableViewCell的代理方法里
wBaseModel *model = (传进来的数据源对应的indexPath的位置) if (model) { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:model.cellName]; //kvo赋值 前提是这个自定义cell里要持有个wBaseModel属性且属性名字为model [cell setValue:model forKey:@"model"]; return cell; }
这种思路是通过model去控制UI的显示,拓展性和重用性比较高,效率快,可以随意修改cell的位置等,而且可以不用写一堆的import 自定义cell类。
2 链式写法 和 block
链式编程我就不说了
/* * cell的block */typedef UITableViewCell* (^WMZCellCallBlock)(NSIndexPath *indexPath,UITableView* tableView,id model); /* * cell点击的block */typedef void(^WMZSelectCallBlock)(NSIndexPath *indexPath,UITableView* tableView,id model); /* * cell高度的block */typedef NSInteger (^WMZCellHeight)(NSIndexPath *indexPath,UITableView* tableView);
enenen
这个就介绍到这里,觉得对你有用的不妨前往GitHub下载 给个✨ WMZTableView
拓展,思考
这个问题我想了一天都想不出来,处于我比较懒,想在
.wDidSelect(^(NSIndexPath *indexPath, UITableView *tableView,id model) { ViewController *VC = [ViewController new]; VC.type = 1; [weakSelf.navigationController pushViewController:VC animated:YES]; })
调用代码的点击方法的block里 不想用weakSelf想用self,也就是不想在代码块里弱引用,
这个block已经被tableview持有,作为copy属性,self又持有这个tableView,造成对block的间接使用,不用弱引用会造成循环无法释放
我看了一下masonry这个框架,它的block里面可以直接使用self而不会造成循环引用是因为它的block不是作为属性,而是用一个实例方法去接收,相当于block用完就销毁,self没有直接或者间接跟这个block产生关系,而且其操作是同步的,所以不会造成循环引用
而系统的动画里面也不需要用weakself,同理,原因也是self没有对block形成直接或者间接的引用,用完就销毁。
而我这个类操作是异步的
点击的时候,执行tableview的代理didSeleect方法,需要拿到传过去的block
这种时候就必须tableview持有这个block,才能在其他实例方法里拿到
所以在链式调用的时候,传进来的block我赋值给tableview的self.selectBlock属性(copy)
- (WMZTableView *(^)(WMZSelectCallBlock))wDidSelect{ return ^WMZTableView*(WMZSelectCallBlock wDidSelect){ if (wDidSelect) { self.selectBlock = wDidSelect; } return self; }; }
而要破除循环就要tableview不引用这个block,有点矛盾,怎么都无法做到让self不直接或者间接引用block
试了很多思路都不行,有点无解
有大神有其他方法的或者思路的话可以告知下,不胜感激。
2019/04/28 增加自动局部刷新
//要更新的数据代码 wBaseModel *model1 = [wBaseModel new]; model1.cellName = @"NornalCell"; model1.labelName = @"我是增加的文本1"; wBaseModel *model2 = [wBaseModel new]; model2.cellName = @"NornalCellOne"; model2.labelName = @"我是增加的文本和图片"; model2.imageName = @"8.jpg"; [self.modelArr addObject:model1]; [self.modelArr removeObjectAtIndex:0]; [self.modelArr insertObject:model1 atIndex:1]; wBaseModel *model = self.modelArr[0]; model.cellName = @"NornalCell"; model.labelName = @"我是改变的文本"; /* * 一句话调用 */ self.tableView.wUpdateData(self.modelArr);
只会刷新更改过的indexpath 没更改过的保持原来
我的算法如下 感觉性能有点不佳
首先先深拷贝一开始传进去的数据源
//创建一个完全新的数组,数组内的元素也要copy 保证指向的内存地址不同 self.dataArr = [[NSMutableArray alloc] initWithArray:data copyItems:YES]; //再创建一个数组 外面的数据源内元素发生改变 此数组内元素也会改变,增加和删除不会 self.simpleDataArr = [NSMutableArray arrayWithArray:data];
然后进行与更改后的数据的对比
for (int i = 0; i<self.simpleDataArr; i++) { id model = self.simpleDataArr[i]; if ([data indexOfObject:model] != NSNotFound) { //找出相等的 [sameArr addObject:@{ @"index":@(i), @"model":model }]; }else{ //找出self.simpleDataArr中有 data没有的 [differentArr addObject:@{ @"index":@(i), @"model":model }]; } } for (int i = 0; i<data.count; i++) { id model = data[i]; //找出data中有 self.simpleDataArr没有的 if ([self.simpleDataArr indexOfObject:model] == NSNotFound) { [addArr addObject:@{ @"index":@(i), @"model":model }]; } }
相同的数据对比model是否属性发生更改过,先更新数据
for (int i = 0; i<sameArr.count; i++) {
NSDictionary *dic = sameArr[i];
NSInteger index = [dic[@"index"] integerValue];
//改变前
id model = nornalData[index];
//改变后
id compareModel = dic[@"model"];
unsigned int modelCount, compareModelCount;
//runtime分别获取所有属性
objc_property_t * modelProperties = class_copyPropertyList([model class], &modelCount);
objc_property_t * compareModelProperties = class_copyPropertyList([compareModel class], &compareModelCount);
if (modelCount != compareModelCount) {
NSLog(@"不相等,两个元素发生了改变");
continue;
}else{
for (int j = 0; j < modelCount; j++) {
objc_property_t property1 =modelProperties[j];
objc_property_t property2 =compareModelProperties[j];
NSString *propertyName1 = [[NSString alloc] initWithCString:property_getName(property1) encoding:NSUTF8StringEncoding];
NSString *propertyName2 = [[NSString alloc] initWithCString:property_getName(property2) encoding:NSUTF8StringEncoding];
//对比属性的值
if ([model valueForKey:propertyName1]!=[compareModel valueForKey:propertyName2]) {
[changeArr addObject:@{
@"index":@(index),
@"model":model,
@"changeModel":compareModel
}];
break;
}
}
}
if (modelProperties)
free (modelProperties);
if (compareModelProperties)
free(compareModelProperties);
}
differentArr执行删除操作,addArr执行插入操作
//倒序删除 防止数组在删除的时候index发生改变 导致删除错误或崩溃 for (int i = (int)differentArr.count-1; i>= 0; i--) { NSDictionary *dic = differentArr[i]; NSInteger index = [dic[@"index"] integerValue]; [self.dataArr removeObjectAtIndex:index]; NSIndexPath *path = [NSIndexPath indexPathForRow:index inSection:section]; [deletePathArr addObject:path]; }
详情看demo 耗时只0.0几秒 ,有什么好的方法欢迎提出。