iOS Combine View and TableView <Bug01>

我在开发我的第一个项目SportJoin的过程中遇到过这样一个问题, 需要写出一个同时有UIViewUITablView的界面, 这个为什么很困难呢.

  1. 需要写出易于维护的结构.
  2. 需要在tableView滚动的时候滚动.
  3. 需要在headerView到达顶部的时候segmentControl停在视图的顶部.

我在第一次遇到这种问题的时候, 想到的第一种解决办法就是在第一个UITableViewCell里面加入headerView, 但是这样却导致了另一个问题, 所有在dataSource方法和一些delegate方法中的一些count都需要+1, 我认为这一点相当的不优雅, 而随着需求的更改, 这种结构也被抛弃了, 所以我开始选择另一种方式解决这个问题. 经过了无数的实验与摸索, 选择了下图的这种方式达到我想要的效果.

但是...经过我们团队成员多次测试, 竟然发现了这样的bug... tableViewheaderView之间有20 offset. 而且当下拉刷新动画被打断的时候, 比如点击其他的tabBarItem. offset会越来越大.

设计师第一次发现这个bug告诉我之后, 我立刻感觉到这个高度20应该跟status bar有关, 找了所有与status bar以及这个视图有关的代码, 并且上google搜索了无数次, 都没有发现这是因为什么.

后来由于团队缺少运营, 参加完比赛, 项目不了了之, 这个bug我也没有再去改了. 不过这个bug一直我都记得非常的清楚, 实在是让我头痛不已.

现在先解释一下这个界面是如何完成的, 简单说一下这个界面的根控制器是一个UIViewController, 它的view上面只有一个子视图tableView, 注意只有一个子视图, 而这个子视图包含了我们在这个界面中看到的headerView, 也就是顶部的个人信息.

界面的层级如下:

    -----------------
    |      view     |
    -----------------
            ^
            |
            |
    -----------------
    |   tableView   |
    -----------------
            ^
            |
            |
    -----------------
    |   headerView  |
    -----------------

headerViewtableView的子视图, 但是为什么会这么显示呢, 有这么几个原因.

  1. 在视图加载时, 我通过调整tableViewcontentInset使tableView在初始化时向下偏移headerView的高度, 目的呢, 就是将cell开始显示的位置设置到目标的区域.

    tableView.contentInset = UIEdgeInsetsMake(HEADER_HEIGHT, 0, 0, 0);

  2. 然后呢, 将headerView初始化, 并添加到tableView上面, 这里呢, 有一点需要注意的就是headerView.frame.origin.y是小于0的. 这是为什么呢, 是因为我们上一步将tableView内容开是显示的height设置为295, 那么我们需要将headerView初始化到tableView的"外面"

    // 使用CGRect的情况下, 使用AutoLayout可以类比一下 headerView.frame = CGRectMake (0, -HEADER_HEIGHT, WIDTH_SCREEN, HEADER_HEIGHT);

  3. 这样我们就完成了这个界面的显示, 但是, 当我们向这个headerView中添加按钮的时候, 却发现, 这个按钮的action没有触发, 因为headerViewtableViewsubview并且在它的外部, 所以, 我们需要设置clipsToBound属性, 防止tableView拦截这个实践.

    tableView.clipsToBound = NO;

  4. 接下来我们要实现让界面的一部分停在屏幕上, 只需要调用UIScrollViewdelegate方法, 注意设置适当的条件就可以.

    ```

    • (void)scrollViewDidScroll:(UIScrollView *)scrollView { static CGFloat headerHeight = 60; if (scrollView.contentOffset.y >= -60) [self.headerView setFrame:CGRectMake(0, scrollView.contentOffset.y - (HEADERHEIGHT - headerHeight), WIDTHSCREEN, HEADERHEIGHT)]; if (scrollView.contentOffset.y <= -HEADERHEIGHT) [self.headerView setFrame:CGRectMake(0, scrollView.contentOffset.y, WIDTHSCREEN, HEADERHEIGHT)]; } ```

卧槽, 终于搞定了, 我特么太爽了, 然而.........就是上面的bug让我头疼了好久...

直到今天, 我在新项目中又遇到了这个问题, 无论我怎么更改布局, 使用CGRect, AutoLayout都没能解决, 因为代码量较小, 经过多次调试, 认为这次绝对另有原因, 我机智的上Google搜索了如下关键字contentInset 20 iOS, 结果, 这回果然发现了问题所在, 在stackoverflow上面找到了这个问题的答案, 原来automaticallyAdjustsScrollViewInsets才是导致问题的罪魁祸首, 而之前我完全没有接触过这个属性. 在苹果官方文档中是这样描述的.

If you don’t want a scroll view’s content insets to be automatically adjusted, set automaticallyAdjustsScrollViewInsets to NO. (The default value of automaticallyAdjustsScrollViewInsets is YES.)

在设置了tableViewController之后, 仍然没有起到任何作用.

self.automaticallyAdjustsScrollViewInsets = NO;  

经过仔细寻找其他答案后, 找到了这样一条答案

Your view controller must be directly on a UINavigaitonController's stack for automaticallyAdjustsScrollViewInsets to work (i.e. not a child view controller)

If it is a child view controller of another view controller which is on the navigation stack, you can instead set automaticallyAdjustsScrollViewInsets = NO on the parent. Alternatively you can do this:

   self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;

意思就是, 如果你的viewController直接在UINavigationController那么, 直接设置这个viewControllerautomaticallyAdjustsScrollViewInsets属性, 如果viewController是另一个viewController比如说tabBarViewController, 那么需要设置这个viewControllerparentViewController的属性.

最后, 我在3个月后的今天, fix了这个bug, 实在是太难忘了.

5 . self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;


最后总结一下:

  1. tableView.contentInset = UIEdgeInsetsMake(HEADER_HEIGHT, 0, 0, 0);

  2. headerView.frame = CGRectMake (0, -HEADER_HEIGHT, WIDTH_SCREEN, HEADER_HEIGHT);

  3. tableView.clipsToBound = NO;

  4. scrollViewDidScroll

  5. self.parentViewController.automaticallyAdjustsScrollViewInsets = NO;


后来的后来...我发现了tableView的的另一个属性tableHeaderView, 只需要设置这个属性, 就可以把视图放在tableView的前面...真是蛋都碎了...

self.tableView.tableHeaderView = headerView;  

......

Draveness

继续阅读此作者的更多文章

comments powered by Disqus