首页 文章

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

提问于
浏览
107

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

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

当我用tableview推动我的视图控制器时,我得到一个额外的动画,其中单元格“向上滚动”(或者可能整个桌面框架被更改)并沿着推/弹导航动画向下移动 . 请看gif:

wavy tableview

我在 loadView 方法中手动创建 tableview 并将自动布局约束设置为等于tableview的superview的leading,trailing,top,bottom . 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 创建具有给定大小和颜色的方形图像 .

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

我很感激有关这件事的任何想法 .

10 回答

  • 3

    这是由于 UIScrollView's (UITableView is a subclass of UIScrollview) new contentInsetAdjustmentBehavior 属性,默认情况下设置为 .automatic .

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

    tableView.contentInsetAdjustmentBehavior = .never
    

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

  • 0

    除了maggy的答案

    OBJECTIVE-C

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

    此问题是由iOS 11中的一个错误造成的,其中导航过渡期间视图控制器视图的safeAreaInsets设置不正确,应在iOS 11.2中修复 . 将contentInsetAdjustmentBehavior设置为.never不是一个很好的解决方法,因为它可能会有其他不良副作用 . 如果你确实使用了一种解决方法,你应该确保删除它的iOS版本> = 11.2-由smileyborg提到(Apple的软件工程师)

  • 2

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

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

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

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

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

    XIB中的 New simple solution: :只需在主视图顶部添加新的UIView,顶部,前导和尾随superview,高度设置为0.您不必将其连接到其他子视图或任何其他内容 .

    Old solution:

    注意:如果您在横向模式下使用UIScrollView,它仍然没有正确设置水平插入(另一个错误?),因此您必须将滚动视图的前导/尾随固定到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
        }
    }
    
  • 1

    以下是我设法解决此问题的方法 while still allowing iOS 11 to set insets automatically . 我正在使用 UITableViewController .

    • 在故事板(或programmatically)的视图控制器中选择"Extend edges under top bars"和"Extend edges under opaque bars" . 安全区域插入将阻止您的视图进入顶部栏 .

    • 检查故事板中表格视图上的"Insets to Safe Area"按钮 . (或 tableView.insetsContentViewsToSafeArea = true ) - 这可能没有必要,但这就是我所做的 .

    • 将内容插入调整行为设置为"Scrollable Axes"(或 tableView.contentInsetAdjustmentBehavior = .scrollableAxes ) - .always 也可能有效,但我没有测试 .


    如果其他所有方法都失败了,还有另外一件事

    覆盖 viewSafeAreaInsetsDidChange UIViewController 方法以获取表视图以强制将滚动视图插入设置为安全区域insets . 这与Maggy的答案中的'Never'设置一起使用 .

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

    注意: self.tableViewself.view 应与 UITableViewController 相同

  • 20

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

  • 135

    请确保以上代码,添加如下附加代码 . 它解决了这个问题

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

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

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

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

    self.navigationController.navigationBar.translucent = YES;
    
  • 1

    删除此代码工作对我来说

    self.edgesForExtendedLayout = UIRectEdgeNone
    

相关问题