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


116

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

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

当我用tableview推送视图控制器时,我会得到一个附加的动画,其中单元会沿着push / pop导航动画“向上滚动”(或可能改变整个tableview框架)并向下滚动。请参阅gif:

波浪表

我手动创建tableviewloadView方法和设置自动布局约束等于前置,顶部的tableview的上海华盈的底部。超级视图是视图控制器的根视图。

View Controller推送代码非常标准: 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创建具有给定尺寸和颜色的正方形图像。

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

我对此事有任何想法。


定制导航栏可能不是问题。我遇到了同样的问题(接受的答案解决了这个问题),没有进行任何自定义。我认为,当手动将它作为子视图而不是使用UITableViewController创建时,iOS处理表视图的方式可能会出现问题。
Mark Leonard

2
我看到只有当我的这种行为navigationBar.isTranslucentfalse,否则它工作正常。
b_ray

5
这似乎是iOS11 GM中的错误,请重复该错误报告,以使该问题引起Apple的注意:openradar.appspot.com/34465226
b_ray

1
此问题似乎已在iOS 11.2 beta中修复。我不会将contentInsetAdjustmentBehavior设置为从不,因为它不会通过在屏幕底部不提供填充来破坏iPhone X的滚动视图。内容视图的底部位于iPhone X的主页“按钮”下方。
巴图

Answers:


150

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

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

    tableView.contentInsetAdjustmentBehavior = .never

https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustment行为


2
这个对UIScrollViewContentInsetAdjustmentBehavior.automatic的定义的注释是:“ ...为了向后兼容,当滚动视图由导航控制器内部具有autoAdjustsScrollViewInsets = YES的视图控制器拥有时,也将调整top&bottom contentInset,无论滚动是否滚动视图是可滚动的”。我的理论是,导航栏的contentInset受设置背景图像的影响,然后动态调整。
Maggy Hillen

8
您也可以使用情节提要进行此操作。大小检查器->内容插入->设置为“从不”。
Woongbi Kim

4
如果您的内容扩展到选项卡栏的后面,则禁用tableView.contentInsetAdjustmentBehavior会破坏插图。
kean 2017年

4
此外,只需禁用此功能,滚动指示器就会在横向放置在iPhone X上的(顶部Gizmo)后面。此行为的全部目的是调整滚动视图的内容区域,以使它们在非矩形的屏幕上可见。我认为我们目前只能在iPhone X Sim上看到它。
PhoneyDeveloper

8
此问题是由iOS 11中的一个错误引起的,该错误中在导航转换期间错误设置了视图控制器视图的safeAreaInsets,应在iOS 11.2中修复该错误。将contentInsetAdjustmentBehaviorto 设置.never为不是一个很好的解决方法,因为它可能还会产生其他不良影响。如果确实使用了解决方法,则应确保在iOS版本> = 11.2的情况下将其删除。
smileyborg

23

除了maggy的答案

目标C

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

此问题是由iOS 11中的一个错误引起的,该错误中safeAreaInsets在导航过渡期间错误设置了视图控制器的视图,应在iOS 11.2中修复该错误。将contentInsetAdjustmentBehaviorto 设置 .never为不是一个很好的解决方法,因为它可能还会产生其他不良影响。如果您确实使用了解决方法,则应确保在iOS版本大于等于11.2的情况下将其删除

(Apple的软件工程师)提到


6

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

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

1
这将破坏系统的控制器,例如UIImagePickerController

6

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

  • 在情节提要中的视图控制器中(或通过编程方式)选择“顶栏下方的延伸边缘”和“不透明栏下方的延伸边缘” 。安全区域插图将阻止您的视图进入顶部栏。
  • 在情节提要的表格视图中,选中“插入到安全区域”按钮。(或tableView.insetsContentViewsToSafeArea = true)-这可能不是必需的,但这就是我所做的。
  • 将内容插图调整行为设置为“ Scrollable Axes”(或tableView.contentInsetAdjustmentBehavior = .scrollableAxes)- .always也许也可以,但是我没有测试。

如果其他所有方法都失败,请尝试另一件事:

viewSafeAreaInsetsDidChange UIViewController获取表视图的覆盖方法,将滚动视图插图强制设置为安全区域插图。这与Maggy答案中的“从不”设置结合在一起。

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

注意:self.tableViewself.view应该是一样的UITableViewController


在该线程上尝试了几乎所有内容,这对我有用。TabViewVC中嵌入了TableViewVC。谢谢!
乔什·沃尔夫

3

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

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

有必要做两件事:
1.防止scrollView在push / pop上动画化;
2.保留.automatic行为,因为iPhone X需要它。如果没有此行为(例如,肖像),内容将位于底部滚动条下方。

新的简单解决方案:在XIB中:只需在主视图的顶部添加新的UIView,并将其顶部,顶部和底部拖到Superview,并将height设置为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
    }
}

什么是parentView?
PhoneyDeveloper

您的视图控制器的主视图,我已经更新了答案。谢谢。
El Horrible

当我使用UITableViewController时,tableView是视图控制器的视图。同样,旋转设备时的插入点也应该不同。我要调整。我只是不喜欢tableView第一次出现时的动画。
PhoneyDeveloper

在您的情况下,由于UITableView是控制器的视图,因此它应该具有safeAreaInsets.bottom = 34(纵向),因此您只需设置tableView.contentInset = tableView.safeAreaInsets。
El Horrible

那对我不起作用。在viewDidLoad中,所有插图均为零。如果我尝试在viewWillLayoutSubviews中使用该代码,则滚动指示器会插入,但tableView本身可以水平滚动。如果我只查看viewWillLayoutSubviews中的插图而不禁用调整,则底部AdjustedContentInset变为21,这一切似乎都会改变。
PhoneyDeveloper


2

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

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

这一个解决了我的问题!谢谢。在这个问题上浪费了太多时间!
Murat Yasar

2

同样,如果使用选项卡栏,则集合视图的底部内容插图将为零。为此,将以下代码放在viewDidAppear中:

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

2

就我而言,这可行(将其放入viewDidLoad中):

self.navigationController.navigationBar.translucent = YES;

1

删除collectionView或顶部的多余空间tableView

    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)
}

0

删除此代码对我有用

self.edgesForExtendedLayout = UIRectEdgeNone

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.