可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have recently migrated some code to new 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.
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:
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.
View controller pushing code is very much standard: self.navigationController?.pushViewController(notifVC, animated: true)
The same code provides normal behaviour on 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:
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:
rootNavController.navigationBar.setBackgroundImage(createFilledImage(withColor: .white, size: 1), for: .default)
where createFilledImage
creates square image with given size and color.
If I comment out this line I get back normal behaviour.
I would appreciate any thoughts on this matter.
回答1:
This is due to UIScrollView's
(UITableView is a subclass of UIScrollview) new contentInsetAdjustmentBehavior
property, which is set to .automatic
by default.
You can override this behavior with the following snippet in the viewDidLoad of any affected controllers:
tableView.contentInsetAdjustmentBehavior = .never
https://developer.apple.com/documentation/uikit/uiscrollview/2902261-contentinsetadjustmentbehavior
回答2:
In addition to maggy's answer
OBJECTIVE-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 the
contentInsetAdjustmentBehavior
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)
回答3:
You can edit this behavior at once throughout the application by using NSProxy in for example didFinishLaunchingWithOptions:
if (@available(iOS 11.0, *)) {
[UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
回答4:
Here's how I managed to fix this issue while still allowing iOS 11 to set insets automatically. I am using 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.
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.
- (void)viewSafeAreaInsetsDidChange {
[super viewSafeAreaInsetsDidChange];
self.tableView.contentInset = self.view.safeAreaInsets;
}
Note: self.tableView
and self.view
should be the same thing for UITableViewController
回答5:
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.
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.
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.
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.
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.
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
}
}
回答6:
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)
}
回答7:
Also if you use tab bar, bottom content inset of the collection view will be zero. For this, put below code in viewDidAppear:
if #available(iOS 11, *) {
tableView.contentInset = self.collectionView.safeAreaInsets
}
回答8:
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
回答9:
In my case this worked (put it in viewDidLoad):
self.navigationController.navigationBar.translucent = YES;
回答10:
Delete this code work for me
self.edgesForExtendedLayout = UIRectEdgeNone
回答11:
Removing extra space at top of collectionView
or 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
self.edgesForExtendedLayout = UIRectEdge.bottom
but I love to use below logic and code for the 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
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets {
return UIEdgeInsets(top: -30, left: 0, bottom: 0, right: 0)
}