【iOS学习】UITableView占位图的低耦合性设计

大哥打扰一下,【iOS学习】UITableView占位图的低耦合性设计
最新回答
满栀

2026-06-25 19:51:52

UITableView占位图的低耦合性设计需围绕显示条件、首次加载干扰排除、网络失败处理及再次请求策略展开,以下是具体方案:

1. 需要显示占位图的情况
  • 常规情况:当tableView.rows == 0时显示占位图,适用于无数据或数据为空的场景。
  • 特殊情况:当列表数据绑定在sectionHeaderView上时,需满足以下条件:

    tableView.sections > 0且tableView.rows == 0;

    tableView.viewForHeaderInSection != nil(即存在自定义的sectionHeaderView)。

    需求场景:点击sectionHeaderView展开section并刷新数据,此时若仅判断rows == 0会导致占位图异常显示。

  • 无网络情况:直接加载占位图,无需等待数据请求结果。
2. 排除tableView初次调用reloadData的干扰
  • 问题背景:首次调用reloadData时可能因数据未加载完成而误触发占位图显示。
  • 优化方案:

    属性标记法:在UITableView分类中添加BOOL属性isNotFirstReload,默认值为NO。

    在reloadData方法内部判断:

    if (self.isNotFirstReload) { [self checkEmpty]; // 检查是否显示占位图} self.isNotFirstReload = YES;

    优势:无需外部传入参数,避免遍历子视图带来的性能损耗。

    避免的方案:

    遍历子视图:通过递归遍历控制器子视图查找UITableView并设置属性,但性能开销较大,尤其在视图启动时。

3. 网络请求失败时的占位图处理
  • 问题背景:网络请求失败时,若未手动调用reloadData,占位图可能无法正确显示。
  • 解决方案:

    统一处理法:通过window的rootViewController获取当前控制器,遍历其子视图找到UITableView并调用reloadData。

    代码示例:

    // 获取根控制器UIViewController *rootVC = [[[UIApplication sharedApplication] delegate] window].rootViewController;// 处理导航控制器情况if ([rootVC isKindOfClass:[UINavigationController class]]) { UIViewController *visibleVC = [(UINavigationController *)rootVC visibleViewController]; [self findTargetViewWithController:visibleVC];} else { [self findTargetViewWithController:rootVC];}// 遍历子视图- (void)findTargetViewWithController:(UIViewController *)viewController { NSArray *subViews = viewController.view.subviews; [subViews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { if ([view isKindOfClass:[UITableView class]]) { UITableView *tableView = (UITableView *)view; [tableView reloadData]; } }];}

    局限性:

    性能损耗:遍历子视图需一定时间,但因仅在请求失败时触发,影响较小。

    多请求干扰:若界面存在多个请求,任一请求失败可能误触发其他tableView的reloadData。

4. 占位图触发再次网络请求的策略
  • 目标:通过占位图点击事件触发数据重新加载,实现低耦合设计。
  • 实现方案:

    基类统一处理:

    懒加载tableView:在基类中初始化tableView并设置代理(如reRequestDelegate)。

    代理回调:定义协议方法(如reRequesData),在回调中调用统一的网络请求方法(如loadData)。

    子类重写:子类仅需实现loadData方法完成具体网络请求逻辑。

    代码示例:

    // 基类中- (UITableView *)tableView { if (!_tableView) { _tableView = [[UITableView alloc] initWithFrame:CGRectZero]; _tableView.delegate = self; _tableView.reRequestDelegate = self; // 设置代理 } return _tableView;}- (void)reRequesData { [self loadData]; // 调用统一方法}// 子类中- (void)loadData { // 实现具体网络请求}

    优势:

    代码复用:子类无需重复实现点击事件处理逻辑。

    低耦合:占位图触发逻辑与业务逻辑分离,便于维护。

总结
  • 显示条件:综合判断rows、sections及sectionHeaderView是否存在,覆盖常规与特殊场景。
  • 首次加载:通过BOOL属性标记避免遍历子视图,优化性能。
  • 网络失败:统一处理reloadData调用,需权衡性能与多请求干扰问题。
  • 再次请求:利用基类代理模式实现低耦合设计,子类仅需关注数据加载逻辑。