Can you set the scroll view insets for container controllers? - ios

I have a container controller that displays a bar of data right below the lop layout guide.
I want the child controller of the this container controller to be able to scroll behind this bar as well as any navigation bar and status bar. However it seems that when UIKit automatically adjusts the scroll view insets of a controller it only takes into account the length of the top layout guide.
Is there any way to be say that the scroll view insets should be the top layout guide + the height of my bar? I know people are going to suggest I set automaticallyAdjustsScrollViewInsets = NO but I have already tried this. The problem is that I cannot seem to replicate exactly when UIKit sets the content insets and so I get all sorts of weird edge cases that break the insets. The most prominent example being UITableViewController where, because I set the content insets manually, the refresh control is sometimes not in the correct position and when I end refreshing the insets of the scroll view get set to { 0, 0 } so content is hidden behind my bar and the navigation bar.
UPDATE: All the magic seems to be inside the methods _setNavigationControllerContentInsetAdjustment on UIViewController and _computeAndApplyScrollContentInsetDeltaForViewController on UINavigationController.
It appears that only the navigation controller attempts to adjust the scroll view insets and it calculates the insets based on the top and bottom layout guides. It passes these insets onto the view controller which then adds/subtracts the insets from the scroll view and takes into consideration what navigation content insets have been set previously. This ensures that if anyone else modified the content insets between now and when the navigation controller last modified the insets they will not override the intermediary changes.

My solution was to set automaticallyAdjustsScrollViewInsets = NO and handle the setting of the content insets myself.
I created a category on UIViewController that tracked any existing insets that were set so whenever I adjusted the insets to have the scroll view appear below my custom UIView I would not be losing any information, such as the content inset for the UITableViewController with a active refresh control inside it.
After that the last challenge was figuring out where to set the custom insets and after much experimenting I found that - (void)viewDidLayoutSubviews was the best place to do it.
FDScrollingTabBarController is the container controller that I implemented this solution in.

Related

UICollectionView under Navigation Bar, but not under Tab Bar

I have a UICollectionView that I want to go under the navigation bar. Basically I want it to ignore the entire top safe area, yet still want it to respect the bottom safe area, as there's a tabbar there. This is how it currently looks:
But I want the first cell to start directly the very top of the screen, under the (translucent) navigation bar and the status bar.
If I set collectionView.contentInsetAdjustmentBehavior = .never, then the top part works great, but then the bottom part of the collection view content is hidden by the tabbar - you can't scroll all the way to the bottom so to speak. So I have to manually add a bottom inset again? How do I get the height of the tabbar, including any bottom safe area on devices that have the home bar? Or is there a better way to tell the collectionview to ignore only the top area for its content inset adjustment?
You just need to set the bottom content inset of the collection view manually, after setting the adjustment behavior to .never.
The correct inset (including the tab bar and any home bar) can be found in safeAreaInsets.
collectionView.contentInsetAdjustmentBehavior = .never
collectionView.contentInset.bottom = collectionView.safeAreaInsets.bottom
You'll need to do this at a point when the safeAreaInsets have been set, such as viewDidLayoutSubviews.
As far as I remember, it used to be possible by simply adjusting the edgesForExtendedLayout property of the containing view controller, but that was phased out when safe areas were introduced in iOS 11 I believe.

UISearchBar on UITableView strange offset issue

I have a UITableView which has a UISearchBar subview. This is all on the view of a UIViewController along with a few other subviews (labels, text fields and such).
The search bar and content offset of the table are acting quite strangely, but it seems dependent on the order in which these views are added to the main view in the xib. I created a sample project with just my table/search and a label in order to test, and the result is the same. When the table is added after the label, everything works fine:
Setup:
Correct and Expected Result:
However, if I simply change the order in which my 2 subviews sit on the main view (aka table added before the label) then weird things start happening.
Apparently bad setup:
Weird offset of Search Bar:
I'm not changing anything else whatsoever, so why does Xcode seem to care which order these subviews are added to the main view?? If I scroll up on the "bad" table setup, the search bar disappears immediately at its top edge, but the table's content will continue to scroll up until it reaches the top of the frame that was set in the xib. Scroll back down and the search bar doesn't reappear until the strange lowered location. This is in Xcode 5.1.1, not the new beta. The result is the same with or without Autolayout turned on.
Any idea why this is happening? Is this a bug, or am I missing something? (I didn't post any code because all I'm doing is setting the number of sections, rows, and setting the text on the cell. Not messing with content insets, offset, anything. I load the view from the app delegate as the root of a nav controller)
This happens because a UIViewController's property called automaticallyAdjustsScrollViewInsets
With iOS 7, UIViewControllers have a property called
automaticallyAdjustsScrollViewInsets, and it defaults to YES. If you
have a scroll view that is either the root view of your view
controller (such as with a UITableViewController) or the subview at
index 0, then that property will adjust both the contentInset and the
scrollIndicatorInsets. This will allow your scroll view to start its
content and scroll indicators below the navigation bar (if your view
controller is in a navigation controller).
From Big Nerd Ranch
If you are using storyboards, you can change it by selecting the view controller and in the attributes inspector deselect Adjust scroll view insets.
Here is its description from apple documentation:
Default value is YES, which allows the view controller to adjust its
scroll view insets in response to the screen areas consumed by the
status bar, navigation bar, and toolbar or tab bar. Set to NO if you
want to manage scroll view inset adjustments yourself, such as when
there is more than one scroll view in the view hierarchy.
I have same problem before about position of tableview and searchbar. i tried the following and it works for me.
If you do not write code for that and if it is only problem of xib or storyboard then try all outlet's autosizing and origin setting to fix its position and see the difference. it may be work for you.
Update : automaticallyAdjustsScrollViewInsets has been deprecated in ios 11 and a new field contentInsetAdjustmentBehavior has been introduced.
if #available(iOS 11.0, *) {
tableview.contentInsetAdjustmentBehavior = .never
} else {
automaticallyAdjustsScrollViewInsets = false
}
UITableView header can contains only one UIView, so if you need UISearchBar plus UILabel, you need to wrap they into UIView and add this view as UITableView header.

UITableView Content inset unexplainable behaviour

I have a View Controller with UITableView declared in storyboard. The View Controllers has "Under top bars" option selected (Edge Rect). Normally the view looks like the below image.
There is no gap between the cells of the tableview and navigation bar.
The filter in the right bar button item presents filter view controller modally and
when returning back (dismiss), there is content inset (additional) added (by I don't know who). The final looks like this.
I have tried to set content inset in viewWillAppear, viewDidLayoutSUbviews etc. But nothing seems to work. Has anyone ever come across similar kind of problems? How can I remove this contentInset.

Why is a UIToolBar's height not being taken into account by bottomLayoutGuide and automaticallyAdjustsScrollViewInsets in iOS7 (with autolayout)

I haven't been to find anything about this, and it may be that there is no easy solution.
I have a scroll view (in this instance a UITableView) which is the first subview of my UIViewController's view (which is not itself a scroll view). This view controller is in a UINavigationController, and I have also added a UIToolBar as second subview of the view controller's view. Both the table view and the toolbar are positioned and sized in the view controller's view using autolayout (with the table view filling the view, and the toolbar being pinned to the bottomLayoutGuide of the view controller).
As I understood it, navigation bars and toolbars (which are pinned to the bottom of views) should influence the topLayoutGuide and bottomLayoutGuide of the view controller, and by this influence the contentInset of contained scroll views.
The navigation bar is being taken into account (by the topLayoutGuide and automaticallyAdjustsScrollViewInsets) so that my table view content scrolls underneath it, but is visible below it, but my UIToolBar is not - either by bottomLayoutGuide or automaticallyAdjustsScrollViewInsets. This behaviour is the same even if I position my toolbar with a frame (not using constraints).
Am I right in thinking that a UIToolBar pinned to the bottom of the view controller's view should be taken into account by the layout guides? If so, does anyone have any ideas as to why it is not?
If not, is there anywhere (amy method) where I can manually add the tollbar frame to the bottomLayoutGuide so that it is automatically propagated by automaticallyAdjustsScrollViewInsets? And if not, in what method is it best to manually set the table view's content insets?
If you added one yourself it would just be a subview and would not affect the layout guides. Use the built in UIToolbar by setting the UINavigationBar .toobarHidden = false property. Then set the UIViewController.toolbarItems = ...
This toolbar will be your bottom layout guide

Autolayout adding extra padding

I have a UITableViewController in a Storyboard that I'm adding as a subview to a lone UIView in a UIViewController, my end goal is to get the UITableView flush up against the status bar so that scrolling goes underneath the status bar (not through it with clashes).
I have configured the lone UIView to have constraints of 0 on both verticals and horizontals but as demonstrated in the image I believe autolayout is then adding the 20px y offset that I am including in the layout. If I remove the 20px y offset (and size the UIView to the whole layout) I end up with the clashing.
I suggest you to set up your view controller as follows. Create a UIViewController in IB and add a simple UITableView as a subview of its main view. I almost never use the UITableViewController because it has almost no added value but it restricts you in adding subviews to your table view. Now, you position your table view's origin to (0, 20) and set up the top layout constraint of the table view to the top layout guide instead of the superview. Maybe you should open the drop down menu close to the constraint constant value in IB:
This way your table view will start right under the status bar.
Note however that iOS 7 design guidelines suggest that you would in fact extend the content under the top bars (nav bar and status bar). You should create a 20 points high semi-transparent background png, position it under the status bar, and leave the table view to scroll under the status bar. In this case you should also not forget to check in the "Adjust scroll views inset" option of your view controller.

Resources