Why content insets of a UITableView inside a UIPageViewController get messy right after an interaction? - ios

I've created a Page-Based Application and hacked it a bit for some experiments; my simple aim is to have a UIPageViewController whose pages will contain a UIViewController holding a UITableView (after further inspection, the outcome of my experiment is the same if I use a UITableViewController instead).
To do this I've simply edited the project template, and added the UIPageViewController as an embedded view of the RootViewController using Storyboard's ContainerView object, as you can see in this screenshot:
Every controller is configured via storyboard to automatically adjust scroll view's content inset, and if I start the project with this configuration everything looks fine, and the DataViewController's tableview has its content insets properly adjusted right under the navigation bar as expected; however, as the user makes an interaction with the tableview, the content insets break and the tableview underlaps the navigation bar:
I have been able to fix this by manually setting the content insets in DataViewController's viewDidLayoutSubview method, and by disabling Adjusts Scroll View Insets on any controller, so I don't need this as an answer to solve my problem.
My question is why the content insets are properly set right after the first controller gets loaded from the storyboard the first time, and why they break after any kind of user interaction.
If you need to test it directly, here's a link to the project

I'm not 100% sure it's what you're running into, but here's some explanation regarding your setup:
By default, UIViewControllers tend to be configured to extend their content behind non-opaque bars, and to adjust scroll view insets. In your setup above this means that the container VC:
Extends its content so as to appear behind the navigation bar
Sets its scrollview's .contentInset and .scrollIndicatorInsets to be the same height as the navigation bar
"But, ah!" I hear you say, "that container view controller doesn't have a scroll view". Well this is the tricky bit:
It seems UIViewController's definition of "my scroll view" is rather simplistically, "the first scroll view I come to when searching down through my view hierarchy".
UIPageViewController happens to be implemented using a scroll view. So the container VC finds that scroll view, and adjusts its top insets. This is not really what you want; it confuses the page controller, and tends to upset layout at some point.
The simple solution there is simply to turn off .automaticallyAdjustsScrollViewInsets for the container view controller.
You then need to solve the next problem of your table view needing some kind of top insets to account for the navigation bar. So far it seems to me the best way to do this is manually either in the storyboard, or code.

Found an elegant solution:
From your storyboard, select your UIPageViewController
In the Attributes inspector, uncheck the option ViewController => Extend Edges => Under Top Bars
That's all, no weird UI hacks

Have you tried fixing the Auto Layout constraint in the container view of the Root view controller in the Storyboard (that is, the top space constraint should be set related to the Top Layout Guide and not the Superview, with constant = 0)?
You will only lose the effect of the Table View scrolling under the Navigation Bar

Related

iOS 11 navigationbar scroll

so, the setup looks like this:
I want to have one navigationbar and three collectionViews. I want to be able to horizontally scroll through the collectionviews and on vertical scroll the navigationBar large title animation should trigger as well.
What I have tried:
My first try was to create one horizontally.scrolling collectionView with cells having the size of the whole view and in each cell there would be another collectionView that would scroll vertically. Now, that worked great for my purpose but the large title stayed the same size - which, as I understand, is because it does not receive any vertical scroll events since my collectionViews for vertical scrolling are embedded in main collectionView.
It looked like this: https://giphy.com/gifs/50hYRx71XSdYA/html5
Then I tried a different approach. I created pageViewController and two viewControllers both with one vertically-scrolling collectionView. And ... it did not work either. This time if there was pageCurl animation, the navigation title DID animate - but I do not want a pageCurl animation, you can see that here: https://giphy.com/gifs/jFmOpEtUOyxtC/html5
Changing the animation to .scroll just disabled the title animation: https://giphy.com/gifs/l0IsGVhKKb0lzomqc/html5
I can not find a way to do this, do any of you have any idea?
Thanks!
Unfortunately the new self-collapsing navigation bar is using some private Apple-magic in the background to hook into the top-level scroll view of the view controller, therefore you cannot use this mechanism the way you've described.

Scrollview won't scroll all the way down

I have a scroll view which works fine. Almost. I'm building in Any/Any. The problem is that the scroll view won't scroll past the view controller. I have a switch that is mostly in the view controller window, but the rest is off the box (not really sure how to describe it; it's in the view in the scroll view, but the view is longer than the view controller so part of it is hidden).
The scroll view will scroll down until it hits the part where the view controller would end if you were looking at it in Xcode. There is some more stuff under the switch (labels and another switch). To view these you have to forcefully scroll down. Xcode shows no constraint errors (little red circle with white arrow).
Hopefully this makes sense
A ScrollView needs to know the height and width of the content it is holding in order to know how much to scroll and which direction. Here is a quick read on how ScrollViews work in iOS: https://www.objc.io/issues/3-views/scroll-view/
You can set this programmatically using the contentSize property, but this requires you to know and or calculate the contentSize, which is pretty tedious in most cases.
The correct way of defining the contentSize in iOS is to define AutoLayout constraints in your View. Here is an excellent tutorial on doing just that:
https://www.natashatherobot.com/ios-autolayout-scrollview/

Width and height not adjusting with Xcode Auto Layout

I have created an interface for my app in Storyboard under the Any x Any layout setup, when I run my app the layout is still sized for the square Any x Any and not the phone screen. I want the app to adjust to all sizes and not use a set layout for set screen sizes. How can I do this?
Here are pictures to show my design, and the outcome of running the app.
Storyboard View
Simulator View
The table view is also cut off here
If your ViewController is subclass of UIViewController then add leading, trailing ,top and bottom constraints to UITableView, and contains to your custom navigation bar. or If its subclass of UITableViewController. No need to add any constraints. Just embed your TableViewController into NavigationController.
Hope this will help you
what exactly are you trying to achieve? Your tableview to be entirely on the screen, so not scrollable?
If so, then you should override the layout of your tableview and its corresponding cells. As those cells (and the number of them) define the height of your tableview.
From what I can see it appears that you have manually added a UINavigationBar. I personally find it much easier to embed your view controller into a navigation controller. This will provide you with a navigation bar that will automatically resize depending on the hardware, or size class, of the device. To embed it go to: Editor -> Embed in -> Navigation Controller.
Your TableView seems to be fully in the screen as well? If you have layout issues with your cells not adjusting then I will be more than happy to help you once you clarify what is not quite right.
Hope this helps.
Yep. Been there, done that. Just because something is centered in the Any x Any view in interface builder does not mean it will also be centered e.g. in an iPhone simulator. You always need to make your layout choices explicit with constraints, i.e. in the this case add a "center horizontally in superview" constraint or align the right and left edge with the superview (I really would need to know more about your view controller in interface builder to tell you exactly).
Are you familiar with constraints or should I add some more detail on how to solve this?

Adding a fixed-position view on top of a view

I have a UITableViewController inside a UINavigationController. I'm adding a "modal" subview to the tableView, which is a custom UIView when one of the rows is selected.
(It’s modal in spirit, not in the UIKit sense, since Apple doesn’t support modal views on iPhone, I’m adding it with a [self.view addSubview:customView] in my table view’s controller.)
I would like it to appear at the bottom of the screen and stay put there. I can get it to draw at the bottom, but once I scroll the table view, the view moves with it. Here are some illustrations:
Initial position (good):
Position after scrolling (bad):
I'm getting the bottom position by subtracting the height of all the chrome (navigation bar and status bar) as well as the height of the custom view from [UIScreen mainScreen].bounds.
How can I get the custom view to stay put? Should I constantly be adjusting its frame when the table view is being scrolled?
Your best and most flexible option is to switch to using a view controller with the table view as a subview so that you can change its frame and add sibling views. Then, when you want to add a modal you can run an animation to move the table view out of the way and slide the modal view in.
UITableViewController.view is an instance of UITableView. Means you added your custom view into a scroll view and that is why it scrolls. You can try to put your custom view into the tableFooterView property of the UITableView, which is the old school solution.
Personally I would create a container UIViewController and have UITableViewController be a sub viewController of it. Another sub viewController UIViewController or just a simple UIView could represent the footer.

automaticallyAdjustsScrollViewInsets doesn't work in embedded UITableViewController with UITableView

When you have a UITableView inside a UIViewController, you have to turn off the automaticallyAdjustsScrollViewInsets flag (either in code or storyboard editor) to get the top space to not avoid the top bar. I now have a child UIViewController using an "embed" seque inside of a child view which contains a UITableView. I am seeing the "space" (the yellow area) yet I cleared all the flags in the embedded UIViewController and even cleared the flag manually in prepareForSeque, yet the space remains. Is there something else one has to do, or is this a bug?
Had this issue this morning.
Turns out, you need to uncheck automaticallyAdjustsScrollViewInsets property from parentViewController instead of child when you are using embedded view controller mode like you did.
Here is my UI structure:
ParentViewController has a container view which point to ChildViewController (subclass of UITableViewController).
Uncheck automaticallyAdjustsScrollViewInsets in ChildViewController: doesn't work
Uncheck automaticallyAdjustsScrollViewInsets in ParentViewController: works!
I have been struggling with this for hours until I realized that the answer by #user3188985 was what was happening, though the solution did not work for me.
This is because I have two contained view controllers that each contain scrollViews, and I want one to adjust, and one not, what to do?
Well if you turn off auto adjustment in the parent controller, then you must manually adjust the one scrollView that you need, e.g. setting contentInset on appropriate cases.
If you leave on auto adjustment in the parent, I have found that a dummy scrollView, with a zero frame, hidden, all scroll properties set to NO, and if it is before your visible/enabled scrollView in the child view controller view hierarchy, then it will receive the auto adjustment, and leave your visible scrollView view alone.
Alternatively, you can just have your contained view controller's top most view be a scrollView with scrolling disabled, and have it contain another scrollView with scrolling enabled, this will achieve the same thing as the "dummy" approach.
What you can see here, is that the auto adjustment flag of only the parent view controller is taken into account, but it's own view controller hierarchy is traversed to find scrollViews to adjust in each contained controller. This is simply unwanted UIKit behavior. It would be nice each child controller's auto adjust setting was checked before deciding to mess with it :/

Resources