iOS程序猿iOS高级开发技术iOS开发记录

runtime入侵实现tableview自动显示隐藏无数据占位图

2018-10-25  本文已影响80人  _maomao

在我们开发中,tableview和collectionview经常被用来展示数据列表。一般来说,tableView获取到的数据源为空时,直接展示一个空的tableView显得比较突兀,所以设计师往往会针对这种情况给出相应的UI,用来替代空tableView的展示。无数据占位图,就是当后台返回的数据源为空时需要展示的页面
如果只是想实现这个效果,有非常多的方法。

下面介绍一种利用runtime methodchange来实现自动控制的方法,使用非常简单便捷,只需要import文件,进行属性控制就可以了。避免繁琐的手动控制占位图显示隐藏

使用示例:一般app的占位视图都是一致的 不需要每个tableview都去重复创建
建议占位视图根据项目情况单独封装一个uiview
或者把默认的view写到分类代码里边儿,就不用调用下面的set方法了

    //自动控制占位图
    self.tableView.autoShowNoData = YES;
    
    //设置占位图
    //一般app的占位视图都是一致的 不需要每个tableview都去重复创建
    //建议占位视图根据项目情况单独封装一个uiview
    //或者把默认的view写到分类代码里边儿,就不用调用下面的方法了
    self.tableView.nodataView = ({
        UILabel *label = [[UILabel alloc] init];
        label.text = @"暂无内容";
        label.textAlignment = NSTextAlignmentCenter;
        label.textColor = [UIColor blackColor];
        label.backgroundColor = [UIColor groupTableViewBackgroundColor];
        label;
    });
实现思路

1.占位图设置并保存
2.提供展示和隐藏方法
3.拦截数据变化
4.判断是否有数据自动调用展示隐藏方法

1.占位图设置并保存

初始化一个视图来做为占位,这个很简单,根据项目UI设计的来做就行了。那怎么保存呢,用tableview自身去保存占位图对象,为什么呢?因为如果这一步使用了其他的对象去保存占位图,会导致代码耦合性增大。要做好一个封装,尽量降低耦合,所有这里不引入其他对象了。

tableview没有占位视图这个属性,那么我们就给它动态增加一个nodataView

创建一个分类 增加属性

@interface UITableView (NoData)
//可定制的无数据视图 默认为显示暂无数据label
//居中显示
@property(nonatomic,strong)UIView       *nodataView;
//自动显示隐藏无数据视图 根据协议返回的section个数以及cell个数联合判断 (默认为NO,需要手动调用showNoDataView显示)
@property(nonatomic,assign)BOOL     autoShowNoData;
@end

.m文件实现setter gtter

- (UIView *)nodataView
{
    return objc_getAssociatedObject(self, @selector(nodataView));
}
- (void)setNodataView:(UIView *)nodataView{
    
    objc_setAssociatedObject(self, @selector(nodataView), nodataView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (void)setAutoShowNoData:(BOOL)autoShowNoData{
    objc_setAssociatedObject(self, @selector(autoShowNoData), @(autoShowNoData), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [self reloadData];
}

- (BOOL)autoShowNoData{
    return [objc_getAssociatedObject(self, @selector(autoShowNoData)) boolValue];
}
2.提供展示和隐藏方法

提供方法并配置默认占位视图 默认占位图根据项目情况设置,这个很重要,可以减少业务层代码

- (void)showNoDataView
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self removeNoDataView];
        if (!self.nodataView) {
            UILabel *label = [[UILabel alloc] init];
            label.text = @"暂无内容";
            label.textAlignment = NSTextAlignmentCenter;
            label.textColor = [UIColor hexColor:@"363636"];
            self.nodataView = label;
        }
        [self addSubview:self.nodataView];
        [self.nodataView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.centerX.equalTo(@0);
            make.centerY.equalTo(@0);
        }];
    });
}

- (void)removeNoDataView
{
    [self.nodataView removeFromSuperview];
}
3.拦截数据变化事件

如何拦截到每一次数据更新?由于tableview的数据显示变化都是由reloadData方法导致的,所以我们想办法监听这个方法的执行。可以用切面编程的思想,Aspects这个框架可以做到,非常好用。mjrefresh的mj_reloadBlock也可以做到,但是我们要尽量减少依赖,这里我们可以使用 <objc/runtime.h>的method_exchangeImplementations函数进行方法替换

method_exchangeImplementations 拦截reloadData

+ (void)load{
    Method reloadData    = class_getInstanceMethod(self, @selector(reloadData));
    Method wy_reloadData = class_getInstanceMethod(self, @selector(wy_reloadData));
    method_exchangeImplementations(reloadData, wy_reloadData);
}
                                                   
- (void)wy_reloadData{
    [self wy_reloadData];
    [self checkData];
}
4.判断是否有数据自动调用展示隐藏方法

通过上面的步骤我们成功的拦截到了每一次数据变化,接下来就是判断是否有数据了。

- (void)checkData{
    if (self.autoShowNoData) {
        BOOL haveData = YES;
        if (!self.dataSource) {
            haveData = NO;
        }else{
            if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]&&[self.dataSource numberOfSectionsInCollectionView:self]<=0) {
                haveData = NO;
            }else{
                NSInteger section =[self.dataSource respondsToSelector:@selector(numberOfSectionsInCollectionView:)]? [self.dataSource numberOfSectionsInCollectionView:self]:1;
                haveData = NO;
                for (int i = 0; i<section; i++) {
                    if ([self.dataSource collectionView:self numberOfItemsInSection:i]>0) {
                        haveData = YES;
                        break;
                    }
                }
            }
        }
        if (haveData) {
            [self removeNoDataView];
        }else{
            [self showNoDataView];
        }
    }
}

这样就完成了tableview的占位控制分类封装。
使用起来是不是很方便啊

上一篇 下一篇

猜你喜欢

热点阅读