iOS11中奇怪的uitableview行为。单元格向上滚动导航推送动画

时间:2017-08-08 16:44:45

标签: ios uitableview animation uinavigationcontroller

我最近将一些代码迁移到了新的iOS 11 beta 5 SDK。

我现在从UITableView获得了一个非常令人困惑的行为。 tableview本身并不那么花哨。我有自定义单元格,但大多数情况下它只是为了它们的高度。

当我用tableview推动我的视图控制器时,我得到一个额外的动画,其中单元格"向上滚动" (或者可能整个tableview框架已更改)并沿着推/弹导航动画向下。请参阅gif:

wavy tableview

我在tableview方法中手动创建loadView,并将自动布局约束设置为等于tableview的超级视图的前导,尾随,顶部和底部。 superview是视图控制器的根视图。

查看控制器推送代码非常标准:self.navigationController?.pushViewController(notifVC, animated: true)

相同的代码在iOS 10上提供了正常的行为。

请你指出我的错误方向?

编辑:我制作了一个非常简单的tableview控制器,我可以在那里重现相同的行为。代码:

class VerySimpleTableViewController : UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 4
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = String(indexPath.row)
        cell.accessoryType = .disclosureIndicator

        return cell
    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        tableView.deselectRow(at: indexPath, animated: true)

        let vc = VerySimpleTableViewController.init(style: .grouped)

        self.navigationController?.pushViewController(vc, animated: true)
    }
}

编辑2:我能够将问题缩小到我对UINavigationBar的定制。我有这样的定制:

rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)

其中createFilledImage创建具有给定大小和颜色的方形图像。

如果我注释掉这一行,我会恢复正常行为。

我很感激对此事的任何想法。

12 个答案:

答案 0 :(得分:143)

这是由于 flag group url 0 0 a abc 1 0 a de 2 1 a fghi 3 1 b jkl 4 0 b m (UITableView是UIScrollview的子类) UIScrollView's 属性,设置为 { {1}} 默认情况下。

您可以在任何受影响的控制器的viewDidLoad中使用以下代码段覆盖此行为:

contentInsetAdjustmentBehavior

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior

答案 1 :(得分:21)

除了maggy的回答

<强>目的-C

if (@available(iOS 11.0, *)) {
    scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
  

此问题是由iOS 11中safeAreaInsets的错误引起的   导航期间视图控制器的视图设置不正确   转换,应在iOS 11.2中修复。设置   contentInsetAdjustmentBehavior.never不是一个很好的解决方法   因为它可能会产生其他不良副作用。如果你这样做   使用变通方法,你应该确保删除iOS版本&gt; =   11.2

     

- 由smileyborg(Apple的软件工程师)提及

答案 2 :(得分:7)

您可以在整个应用程序中使用NSProxy编辑此行为,例如didFinishLaunchingWithOptions:

if (@available(iOS 11.0, *)) {
      [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} 

答案 3 :(得分:4)

以下是我如何设法修复此问题同时仍允许iOS 11自动设置插入。我正在使用UITableViewController

  • 选择&#34;在顶栏下方延伸边缘&#34;和&#34;在不透明的条形下延伸边缘&#34;在故事板中的视图控制器中(或programmatically)。安全区域插入将阻止您的视图进入顶部栏。
  • 检查&#34; Inset to Safe Area&#34;故事板中桌面视图上的按钮。 (或tableView.insetsContentViewsToSafeArea = true) - 这可能没有必要,但这就是我所做的。
  • 将内容插入调整行为设置为&#34; Scrollable Axes&#34; (或tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .always也可能有用,但我没有测试。

如果其他所有方法都失败,还可以尝试另外一件事:

覆盖viewSafeAreaInsetsDidChange UIViewController方法以获取表视图以强制将滚动视图插入设置为安全区域insets。这与“永远不会”相关联。设置Maggy的答案。

- (void)viewSafeAreaInsetsDidChange {
    [super viewSafeAreaInsetsDidChange];
    self.tableView.contentInset = self.view.safeAreaInsets;
}

注意:self.tableViewself.view应与UITableViewController

相同

答案 4 :(得分:3)

这似乎更像是一个错误,而不是预期的行为。当导航栏不是半透明或设置背景图像时会发生这种情况。

如果您只是将contentInsetAdjustmentBehavior设置为.never,则在iPhone X上无法正确设置内容插入内容,例如内容将进入滚动条下方的底部区域。

有必要做两件事:
 1.阻止scrollView在推/弹簧上动画  2.保留.automatic行为,因为iPhone X需要它。在纵向中,内容将低于底部滚动条。

XIB中的新简单解决方案:只需在主视图顶部添加新的UIView,顶部,前导和尾部到superview,高度设置为0.您不必连接它到其他子视图或任何东西。

旧解决方案:

注意:如果你在横向模式下使用UIScrollView,它仍然没有正确设置水平插入(另一个错误?),所以你必须将scrollView的前导/尾随引脚到IB中的safeAreaInsets。

注意2:下面的解决方案还有一个问题,即如果tableView滚动到底部,并且你按下控制器并弹回,它将不再位于底部。

override func viewDidLoad()
{
    super.viewDidLoad()

    // This parts gets rid of animation when pushing
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidDisappear(_ animated: Bool)
{
    super.viewDidDisappear(animated)
    // This parts gets rid of animation when popping
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .never
    }
}

override func viewDidAppear(_ animated: Bool)
{
    super.viewDidAppear(animated)
    // This parts sets correct behaviour(insets are correct on iPhone X)
    if #available(iOS 11, *)
    {
        self.tableView.contentInsetAdjustmentBehavior = .automatic
    }
}

答案 5 :(得分:2)

请确保以上代码,添加其他代码,如下所示。它解决了这个问题

override func viewDidLayoutSubviews() { 
     super.viewDidLayoutSubviews() 
     tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset) 
}

答案 6 :(得分:2)

此外,如果使用标签栏,则集合视图的底部内容插入将为零。为此,请将以下代码放在viewDidAppear:

if #available(iOS 11, *) {
    tableView.contentInset = self.collectionView.safeAreaInsets
}

答案 7 :(得分:2)

我可以重现iOS 11.1的错误,但似乎自iOS 11.2以来该错误已得到修复。 见http://openradar.appspot.com/34465226

答案 8 :(得分:1)

在我的情况下,这工作(把它放在viewDidLoad中):

self.navigationController.navigationBar.translucent = YES;

答案 9 :(得分:0)

为我删除此代码工作

self.edgesForExtendedLayout = UIRectEdgeNone

答案 10 :(得分:0)

删除collectionViewtableView顶部的多余空间

    if #available(iOS 11.0, *) {
        collectionView.contentInsetAdjustmentBehavior  = .never
        //tableView.contentInsetAdjustmentBehavior  = .never
    } else {
        automaticallyAdjustsScrollViewInsets = false
    }

代码collectionViewtableView上方位于导航栏下方。
下面的代码阻止集合视图进入导航

    self.edgesForExtendedLayout = UIRectEdge.bottom

但是我喜欢为 UICollectionView

使用以下逻辑和代码
  

边缘插入值应用于矩形以缩小或扩展   该矩形代表的区域。通常,使用边缘插图   在视图布局期间修改视图的框架。正值导致   要插入(或缩小)指定数量的帧。负   值导致框架以指定的位置开始(或扩展)   数量。

collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)

UICollectionView

的最佳方法
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
        return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}

答案 11 :(得分:0)

  if #available(iOS 11, *) {
        self.edgesForExtendedLayout = UIRectEdge.bottom
  }

我正在将UISearchController与具有表视图的自定义resultsControllers一起使用。将新控制器推入结果控制器会导致表格视图进入搜索状态。

上面列出的代码完全解决了问题