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】:删除collectionView
或tableView
顶部的多余空间
if #available(iOS 11.0, *)
collectionView.contentInsetAdjustmentBehavior = .never
//tableView.contentInsetAdjustmentBehavior = .never
else
automaticallyAdjustsScrollViewInsets = false
以上代码collectionView
或tableView
位于导航栏下方。
下面的代码阻止集合视图进入导航
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
。
tableView.insetsContentViewsToSafeArea = true
) - 这可能不是必需的,但我就是这样做的。
将内容插入调整行为设置为“可滚动轴”(或 tableView.contentInsetAdjustmentBehavior = .scrollableAxes
) - .always
也可能有效,但我没有测试。
如果一切都失败了,可以尝试另一件事:
重写 viewSafeAreaInsetsDidChange
UIViewController
方法以获取表格视图以强制将滚动视图插图设置为安全区域插图。这与 Maggy 回答中的“从不”设置相结合。
- (void)viewSafeAreaInsetsDidChange
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = self.view.safeAreaInsets;
注意:self.tableView
和 self.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 行为