ios iOS11 中奇怪的 uitableview 行为。单元格使用导航推送动画向上滚动
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45573829/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Weird uitableview behaviour in iOS11. Cells scroll up with navigation push animation
提问by iur
I have recently migrated some code to new iOS 11 beta 5 SDK.
我最近将一些代码迁移到新的 iOS 11 beta 5 SDK。
I now get a very confusing behaviour from UITableView. The tableview itself is not that fancy. I have custom cells but in most part it is just for their height.
我现在从 UITableView 得到一个非常令人困惑的行为。tableview 本身并不是那么花哨。我有自定义单元格,但在大多数情况下,它只是为了它们的高度。
When I push my view controller with tableview I get an additional animation where cells "scroll up" (or possibly the whole tableview frame is changed) and down along push/pop navigation animation. Please see gif:
当我用 tableview 推送我的视图控制器时,我会得到一个额外的动画,其中单元格“向上滚动”(或者可能整个 tableview 框架被更改)并沿着推送/弹出导航动画向下滚动。请看gif:
I manually create tableview
in loadView
method and setup auto layout constraints to be equal to leading, trailing, top, bottom of tableview's superview. The superview is root view of view controller.
我tableview
在loadView
方法中手动创建并将自动布局约束设置为等于 tableview 超级视图的前导、尾随、顶部、底部。超级视图是视图控制器的根视图。
View controller pushing code is very much standard: self.navigationController?.pushViewController(notifVC, animated: true)
视图控制器推送代码非常标准: self.navigationController?.pushViewController(notifVC, animated: true)
The same code provides normal behaviour on iOS 10.
相同的代码提供了 iOS 10 上的正常行为。
Could you please point me into direction of what is wrong?
你能指出我出了什么问题吗?
EDIT: I have made a very simple tableview controller and I can reproduce the same behavior there. Code:
编辑:我制作了一个非常简单的 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)
}
}
EDIT 2: I was able to narrow issue down to my customisation of UINavigationBar. I have a customisation like this:
编辑 2:我能够将问题缩小到我对 UINavigationBar 的自定义。我有这样的自定义:
rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)
rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)
where createFilledImage
creates square image with given size and color.
其中createFilledImage
创建具有给定大小和颜色的方形图像。
If I comment out this line I get back normal behaviour.
如果我注释掉这一行,我就会恢复正常行为。
I would appreciate any thoughts on this matter.
我将不胜感激对此问题的任何想法。
回答by Maggy Hillen
This is due to UIScrollView's
(UITableView is a subclass of UIScrollview)new contentInsetAdjustmentBehavior
property, which is set to .automatic
by default.
这是由于UIScrollView's
(UITableView 是 UIScrollview 的子类)新contentInsetAdjustmentBehavior
属性,.automatic
默认设置为。
You can override this behavior with the following snippet in the viewDidLoad of any affected controllers:
您可以在任何受影响控制器的 viewDidLoad 中使用以下代码段覆盖此行为:
tableView.contentInsetAdjustmentBehavior = .never
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
回答by Lal Krishna
In addition to maggy's answer
除了玛吉的回答
OBJECTIVE-C
目标-C
if (@available(iOS 11.0, *)) {
scrollViewForView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
This issue was caused by a bug in iOS 11 where the
safeAreaInsets
of the view controller's view were set incorrectly during the navigation transition, which should be fixed in iOS 11.2. Setting thecontentInsetAdjustmentBehavior
to.never
isn't a great workaround because it will likely have other undesirable side effects. If you do use a workaround you should make sure to remove it for iOS versions >= 11.2-mentioned by smileyborg (Software Engineer at Apple)
此问题是由 iOS 11 中的一个错误引起的
safeAreaInsets
,其中在导航转换期间视图控制器的视图设置不正确,应在 iOS 11.2 中修复。设置contentInsetAdjustmentBehavior
到.never
是不是一个伟大的解决方法,因为它很可能有其他不良的副作用。如果您确实使用了解决方法,则应确保为 iOS 版本 >= 11.2 删除它- 由 Smileyborg(Apple 软件工程师)提及
回答by BigDanceMouse
You can edit this behavior at once throughout the application by using NSProxy in for example didFinishLaunchingWithOptions:
您可以通过在例如 didFinishLaunchingWithOptions 中使用 NSProxy 在整个应用程序中立即编辑此行为:
if (@available(iOS 11.0, *)) {
[UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
回答by Santa Claus
Here's how I managed to fix this issue while still allowing iOS 11 to set insets automatically. I am using UITableViewController
.
这是我设法解决此问题的方法,同时仍允许 iOS 11 自动设置插图。我正在使用UITableViewController
.
- Select "Extend edges under top bars" and "Extend edges under opaque bars" in your view controller in storyboard (or programmatically). The safe area insets will prevent your view from going under the top bar.
- Check the "Insets to Safe Area" button on your table view in your storyboard. (or
tableView.insetsContentViewsToSafeArea = true
) - This might not be necessary but it's what I did. - Set the content inset adjustment behavior to "Scrollable Axes" (or
tableView.contentInsetAdjustmentBehavior = .scrollableAxes
) -.always
might also work but I did not test.
- 在故事板(或以编程方式)的视图控制器中选择“在顶部条下延伸边缘”和“在不透明条下延伸边缘” 。安全区域插图将阻止您的视图进入顶栏下方。
- 检查故事板中表格视图上的“Insets to Safe Area”按钮。(或
tableView.insetsContentViewsToSafeArea = true
) - 这可能不是必需的,但这就是我所做的。 - 将内容插入调整行为设置为“可滚动轴”(或
tableView.contentInsetAdjustmentBehavior = .scrollableAxes
) -.always
也可能有效,但我没有测试。
One other thing to try if all else fails:
如果所有其他方法都失败了,请尝试另一件事:
Override viewSafeAreaInsetsDidChange
UIViewController
method to get the table view to force set the scroll view insets to the safe area insets. This is in conjunction with the 'Never' setting in Maggy's answer.
覆盖viewSafeAreaInsetsDidChange
UIViewController
方法以获取表格视图以强制将滚动视图插入设置为安全区域插入。这与 Maggy 的答案中的“从不”设置相结合。
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = self.view.safeAreaInsets;
}
Note: self.tableView
and self.view
should be the same thing for UITableViewController
注意:self.tableView
和self.view
应该是一样的UITableViewController
回答by El Horrible
This seems more like a bug than intended behavior. It happens when navigation bar is not translucent or when background image is set.
这似乎更像是一个错误而不是预期的行为。当导航栏不透明或设置背景图像时会发生这种情况。
If you just set contentInsetAdjustmentBehavior to .never, content insets won't be set correctly on iPhone X, e.g. content would go into bottom area, under the scrollbars.
如果您只是将 contentInsetAdjustmentBehavior 设置为 .never,则 iPhone X 上的内容插入将无法正确设置,例如,内容将进入滚动条下方的底部区域。
It is necessary to do two things:
1. prevent scrollView animating up on push/pop
2. retain .automatic behaviour because it is needed for iPhone X. Without this e.g. in portrait, content will go below bottom scrollbar.
有必要做两件事:
1. 防止滚动视图在推送/弹出时向上动画
2. 保留 .automatic 行为,因为 iPhone X 需要它。如果没有这个,例如纵向,内容将低于底部滚动条。
New simple solution:in XIB: Just add new UIView on top of your main view with top, leading and trailing to superview and height set to 0. You don't have to connect it to other subviews or anything.
新的简单解决方案:在 XIB 中:只需在主视图顶部添加新的 UIView,顶部、前导和尾随超级视图和高度设置为 0。您不必将其连接到其他子视图或任何东西。
Old solution:
旧解决方案:
Note: If you are using UIScrollView in landscape mode, it still doesn't set horizontal insets correctly(another bug?), so you must pin scrollView's leading/trailing to safeAreaInsets in IB.
注意:如果您在横向模式下使用 UIScrollView,它仍然没有正确设置水平插入(另一个错误?),因此您必须将 scrollView 的前导/尾随固定到 IB 中的 safeAreaInsets。
Note 2: Solution below also has problem that if tableView is scrolled to the bottom, and you push controller and pop back, it will not be at the bottom anymore.
注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
}
}
回答by Linda
I can reproduce the bug for iOS 11.1 but it seems that the bug is fixed since iOS 11.2. See http://openradar.appspot.com/34465226
我可以为 iOS 11.1 重现该错误,但该错误似乎自 iOS 11.2 以来已修复。见http://openradar.appspot.com/34465226
回答by hasankose
Also if you use tab bar, bottom content inset of the collection view will be zero. For this, put below code in viewDidAppear:
此外,如果您使用标签栏,集合视图的底部内容插入将为零。为此,将以下代码放入 viewDidAppear:
if #available(iOS 11, *) {
tableView.contentInset = self.collectionView.safeAreaInsets
}
回答by Basavaraj Kalaghatagi
please make sure along with above code, add additional code as follows. It solved the problem
请确保与上述代码一起,添加如下附加代码。它解决了问题
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) // print(thisUISBTV.adjustedContentInset)
}
回答by nemissm
In my case this worked (put it in viewDidLoad):
在我的情况下,这有效(将其放在 viewDidLoad 中):
self.navigationController.navigationBar.translucent = YES;
回答by Nazmul Hasan
Removing extra space at top of collectionView
or tableView
删除顶部collectionView
或顶部的额外空间tableView
if #available(iOS 11.0, *) {
collectionView.contentInsetAdjustmentBehavior = .never
//tableView.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
Above code collectionView
or tableView
goes under navigation bar.
Below code prevent the collection view to go under the navigation
上面的代码collectionView
或tableView
导航栏下。
下面的代码阻止集合视图进入导航
self.edgesForExtendedLayout = UIRectEdge.bottom
but I love to use below logic and code for the UICollectionView
但我喜欢为UICollectionView使用以下逻辑和代码
Edge inset values are applied to a rectangle to shrink or expand the area represented by that rectangle. Typically, edge insets are used during view layout to modify the view's frame. Positive values cause the frame to be inset (or shrunk) by the specified amount. Negative values cause the frame to be outset (or expanded) by the specified amount.
边缘插入值应用于矩形以缩小或扩展由该矩形表示的区域。通常,在视图布局期间使用边缘插入来修改视图的框架。正值会导致帧插入(或收缩)指定的量。负值会导致框架开始(或扩展)指定的数量。
collectionView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
//tableView.contentInset = UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
The best way for UICollectionView
UICollectionView的最佳方式
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}