UITableView空白占位处理(利用runtime)

  在UITableView开发中经常会遇到空白页展位图,最简单粗暴的方法可以判断数据源后盖上一个UIView在上面。这种方法没错,能够解决问题。但是缺点大家也能一目了然。

  那么还有一种方法就是通过自定义UITableView,封装好后使用。OK,看起来很不错。但是你有多少时间进行代码重构。有时候我们中途接手项目,这时候我们要尽可能的少去修改基类以免伤其筋骨(别问我为啥?我TMD的就遇到过一哥们没啥经验改了基类整个APP崩了)。

 通过给类扩展方法来实现需求,Category和runtime的结合可以很好的解决问题。基类还是不变(当然,你的基类设计的非常好),只会通过Category来增加你需要的逻辑。

上代码

创建UITableView+empty的扩展

static char kEmptyViewKey;
static char kTHeaderHeight;

@interface UITableView (empty)
@property (nonatomic, strong) UIView *showView;
@end
+ (void)initialize
{
    NSLog(@"initialize");
    Method reloadData = class_getInstanceMethod(self, @selector(reloadData));
    Method EM_reloadData = class_getInstanceMethod(self, @selector(NewreloadData));
//要交换reloadData方法,这样NewreloadData能捕捉到reloadData方法,在reloadData调用之前可进行操作
    method_exchangeImplementations(reloadData, EM_reloadData);
}

- (void)EM_reloadData{
  //检查数据
    [self checkData];
  //由于方法交换,此时调用NewreloadData则表示调用reloadData
    [self EM_reloadData];
}

注意一点,由于使用扩展,所以重写set,get方法需要引入runtime库

引入后实现set,get方法

- (void)setShowView:(UIView *)showView
{
    objc_setAssociatedObject(self, &kEmptyViewKey, showView, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


- (UIView *)showView{
    return  objc_getAssociatedObject(self, &kEmptyViewKey);
}

占位如实例:

image.png
showView其实就是我们展示的占位图。我个人倾向于将占位图进行单独封装。如图所示,存在基本的图片和标题,但是也存在一个可以交互的button。所以占位图存在每个页面所需要的交互。所以讲占位图进行单独封装作为属性进行传值。

所以重点就是占位图的展示逻辑    [self checkData]方法

实现[self checkData]方法

- (void)checkData
{
    NSInteger sections = 1;  //默认section为1,因为存在多个section情况
    if ([self.dataSource respondsToSelector:@selector(numberOfSectionsInTableView:)]) {
     
        sections = [self.dataSource numberOfSectionsInTableView:self];  //获取实际section的数目
    }
    
//当没有section的时候也展示占位图
    if (sections == 0){
        [self.showView removeFromSuperview];
        [self addSubview:self.showView];

    }
    else
    {
        if (sections == 1){
//section个数为1时,获取row的数目
            NSInteger rowNumber = [self.dataSource tableView:self numberOfRowsInSection:0];
            if (rowNumber == 0){
                [self.showView removeFromSuperview];
                [self addSubview:self.showView];
            }
            else
            {
                [self.showView removeFromSuperview];
            }
        }
        else
        {
            [self.showView removeFromSuperview];
        }
    }

}

至此占位图展示部分处理完毕。下面看一下VC中的使用方法

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.    
    self.mainTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
    [self.mainTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    [self.mainTableView setDataSource:self];
    [self.mainTableView setDelegate:self];
    [self.view addSubview:self.mainTableView];
//
    self.emptyView = [[UIView alloc] initWithFrame:CGRectMake(0, 30, 90, 90)];
    [self.emptyView setBackgroundColor:[UIColor redColor]];
    self.mainTableView.showView = self.emptyView;
    
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.array.count;
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellInden = @"cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellInden];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellInden];
    }
//    [cell setBackgroundColor:[UIColor redColor]];
    [cell.textLabel setText:self.array[indexPath.row]];
    return cell;
}

运行截图


image.png

self.emptyView则是我们的占位图,前面提到过将占位图单独封装。现在在VC的实现里我们可以随意改变占位图的位置。

可是还有一种情况,tableview有headerview。

我们先给table加上headerview

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    self.mainTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height) style:UITableViewStylePlain];
    [self.mainTableView setSeparatorStyle:UITableViewCellSeparatorStyleNone];
    [self.mainTableView setDataSource:self];
    [self.mainTableView setDelegate:self];
    [self.view addSubview:self.mainTableView];
//
    self.emptyView = [[UIView alloc] initWithFrame:CGRectMake(0, 30, 90, 90)];
    [self.emptyView setBackgroundColor:[UIColor redColor]];
    self.mainTableView.showView = self.emptyView;

    UIView *headView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, 100)];
    [headView setBackgroundColor:[UIColor orangeColor]];
    [self.mainTableView setTableHeaderView:headView];
    
}

再次运行


image.png

占位图位置不变,但是这种情况下我们不能再VC中去修改占位图位置。多数情况下,头部和列表的数据是2个接口进行请求,无法确定哪个接口会回来。而又头部和没头部的占位图显示位置是不一样的。所以需要在Category中获取头部的高度,Y坐标向下偏移头部的高度。同时对外声明一个额外的高度属性用来自定义偏移量

@property (nonatomic, assign) CGFloat   headerHeight;
- (void)setHeaderHeight:(CGFloat)headerHeight
{
    objc_setAssociatedObject(self, &kTHeaderHeight, @(headerHeight), OBJC_ASSOCIATION_ASSIGN);
}

- (CGFloat)headerHeight{
    NSNumber *height = objc_getAssociatedObject(self, &kTHeaderHeight);
    return height.floatValue;
}

获取头部高度,重新改变你占位图位置

- (void)checkHeader
{
    CGFloat headerHeight = self.tableHeaderView.bounds.size.height;
    NSLog(@"%f", headerHeight);
    [self.showView setFrame:CGRectMake(0, self.tableHeaderView.bounds.size.height+self.headerHeight, self.showView.bounds.size.width,  self.showView.bounds.size.height)];
}

至此占位图的核心已经完成,在VC中实现方法如下

    self.emptyView = [[UIView alloc] initWithFrame:CGRectMake(0, 30, 90, 90)];
    [self.emptyView setBackgroundColor:[UIColor redColor]];
    self.mainTableView.showView = self.emptyView;

emptyView则是我们需要的占位图

使用Category来给其增加属性将占位图作为其属性优点:

1、不去修改已经封装好的基类。
2、按需增加,不影响其他模块使用
3、Category中只负责判断页面显示逻辑,将UI处理可单独封装,代码结构清晰

你可能感兴趣的:(UITableView空白占位处理(利用runtime))