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

Posted

技术标签:

【中文标题】iOS11 中奇怪的 uitableview 行为。单元格通过导航推送动画向上滚动【英文标题】:Weird uitableview behaviour in iOS11. Cells scroll up with navigation push animation 【发布时间】:2018-01-16 08:22:16 【问题描述】:

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

我现在从 UITableView 中得到一个非常令人困惑的行为。 tableview 本身并没有那么花哨。我有自定义单元格,但大部分只是为了它们的高度。

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

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

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

如果您对此事有任何想法,我将不胜感激。

【问题讨论】:

导航栏的自定义可能不是问题。我遇到了同样的问题(接受的答案解决了这个问题),没有任何定制。我认为这可能是 iOS 在手动创建表视图作为子视图而不是使用 UITableViewController 时处理表视图的方式的问题。 只有当我将navigationBar.isTranslucent 设置为false 时才会看到这种行为,否则它可以正常工作。 这似乎是 iOS11 GM 中的一个错误,请复制该错误报告,以便该问题得到 Apple 的关注:openradar.appspot.com/34465226 这个问题似乎在 iOS 11.2 beta 中得到修复。我不会将 contentInsetAdjustmentBehavior 设置为 never,因为它通过不在屏幕底部提供填充来破坏 iPhone X 滚动视图。内容视图的底部位于 iPhone X 的主页“按钮”下方。 【参考方案1】:
  if #available(iOS 11, *) 
        self.edgesForExtendedLayout = UIRectEdge.bottom
  

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

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

【讨论】:

【参考方案2】:

删除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)

【讨论】:

【参考方案3】:

除了maggy的回答

目标-C

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

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

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

【讨论】:

【参考方案4】:

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

【讨论】:

是的,在 iOS 11.2 上已修复【参考方案5】:

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

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

需要做两件事: 1. 防止 scrollView 在 push/pop 时向上动画 2. 保留 .automatic 行为,因为 iPhone X 需要它。没有这个,例如在纵向中,内容将位于底部滚动条下方。

新的简单解决方案: 在 XIB 中:只需在主视图顶部添加新 UIView,顶部、前导和尾随到超级视图,高度设置为 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
    

【讨论】:

什么是父视图? 你的视图控制器的主视图,我已经更新了答案。谢谢。 当我使用 UITableViewController 时,tableView 是视图控制器的视图。此外,当设备旋转时,插图应该不同。我要调整。我只是不喜欢 tableView 首次出现时的动画。 在你的情况下,由于 UITableView 是控制器的视图,它应该有 safeAreaInsets.bottom = 34(纵向),所以你可以设置 tableView.contentInset = tableView.safeAreaInsets。 这对我不起作用。在 viewDidLoad 中,所有插图都为零。如果我尝试在 viewWillLayoutSubviews 中使用该代码,滚动指示器确实会插入,但 tableView 本身变得能够水平滚动。如果我只查看 viewWillLayoutSubviews 中的插图而不禁用调整,则底部的adjustedContentInset 变为21,这似乎就是所有变化。【参考方案6】:

在我的情况下,这有效(将其放入 viewDidLoad):

self.navigationController.navigationBar.translucent = YES;

【讨论】:

【参考方案7】:

删除此代码对我有用

self.edgesForExtendedLayout = UIRectEdgeNone

【讨论】:

【参考方案8】:

这是由于UIScrollView's (UITableView 是UIScrollview 的子类) 新的contentInsetAdjustmentBehavior 属性,该属性设置为.automatic strong> 默认情况下。

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

    tableView.contentInsetAdjustmentBehavior = .never

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

【讨论】:

这条关于 UIScrollViewContentInsetAdjustmentBehavior.automatic 定义的评论说:“...为了向后兼容,当滚动视图由视图控制器拥有时,它还将调整顶部和底部 contentInset,其中自动调整滚动视图Insets = YES导航控制器,无论滚动视图是否可滚动”。我的理论是导航栏的 contentInset 受设置背景图片的影响,然后动态调整。 您也可以使用情节提要来做到这一点。大小检查器 -> 内容插图 -> 设置“从不”。 如果您的内容延伸到标签栏后面,禁用tableView.contentInsetAdjustmentBehavior 会破坏插图。 另外,简单地禁用它会将滚动指示器放置在 iPhone X (顶部小工具)的横向后面。这种行为的重点是调整滚动视图的内容区域,以便它们在非矩形屏幕上可见。我想我们目前只能在 iPhone X Sim 上看到这个。 这个问题是由 iOS 11 中的一个 bug 引起的,在导航转换期间视图控制器的 view 的 safeAreaInsets 设置不正确,应该在 iOS 11.2 中修复。将contentInsetAdjustmentBehavior 设置为.never 并不是一个很好的解决方法,因为它可能会产生其他不良副作用。如果您确实使用了解决方法,则应确保为 iOS 版本 >= 11.2 删除它。【参考方案9】:

以下是我设法解决此问题的方法同时仍允许 iOS 11 自动设置插图。我正在使用UITableViewController

在情节提要(或programmatically)的视图控制器中选择“在顶部条下延伸边缘”和“在不透明条下延伸边缘”。安全区域插图将阻止您的视图进入顶部栏下方。 检查情节提要中表格视图上的“插入到安全区域”按钮。 (或tableView.insetsContentViewsToSafeArea = true) - 这可能不是必需的,但我就是这样做的。 将内容插入调整行为设置为“可滚动轴”(或 tableView.contentInsetAdjustmentBehavior = .scrollableAxes) - .always 也可能有效,但我没有测试。

如果一切都失败了,可以尝试另一件事:

重写 viewSafeAreaInsetsDidChange UIViewController 方法以获取表格视图以强制将滚动视图插图设置为安全区域插图。这与 Maggy 回答中的“从不”设置相结合。

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

注意:self.tableViewself.view 对于UITableViewController 应该是同一个东西

【讨论】:

在这个线程上尝试了几乎所有的东西,这对我有用。 TableViewVC 嵌入在 TabBarVC 中。谢谢!【参考方案10】:

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

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

【讨论】:

这会破坏系统的控制器,比如 UIImagePickerController【参考方案11】:

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

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

【讨论】:

【参考方案12】:

请确保与上述代码一起,添加如下附加代码。解决了问题

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

【讨论】:

仅此一项就解决了我的问题!!谢谢你。在这个问题上浪费了太多时间!

以上是关于iOS11 中奇怪的 uitableview 行为。单元格通过导航推送动画向上滚动的主要内容,如果未能解决你的问题,请参考以下文章

iphone中奇怪的UISearchBar取消按钮行为(在模拟器中工作正常)

iPhone 4 中 IOS 7 中奇怪的 UIScrollview 行为

Swift 中奇怪的 UInt64 行为

CodeIgniter 2.0.3 中 Active Record 中奇怪的反引号行为

类中奇怪的异步/等待行为

zsh 中奇怪的“工作”行为