IOS9: UIScrollView offset in UINavigationController - ios

This is beyond frustrating. I'm having an issue with the UINavigationController (and thus UINavigationBar) pushing my UIScrollView's content down at the top, similar to the issues found here:
iOS 7 -- navigationController is setting the contentInset and ContentOffset of my UIScrollView
IOS7 : UIScrollView offset in UINavigationController
I tried both the recommended solutions of either setting self.automaticallyAdjustsScrollViewInsets = true in my view controller's viewDidLoad() method and deselecting Adjust Scroll View Insets in the Interface Builder. Both solutions remove the margin at the top, but as a few others have noted, I can't scroll anymore.
The UIScrollView and ContentView fill the entire view controller. My content view (inside the UIScrollView) contains labels, segments, and a UITextView at the bottom. Does it have something to do with the content in Content View?
I can attach pictures if needed. Using XCode 7, Swift 2, and iOS 9.

I solved my issue. In auto layout, I wasn't setting my content view's height constraint.
Previously, I had pinned the bottom of the content view to the view controller that contained the UIScrollView.
I removed that pin and explicitly set the height. It works now.

I just ran into the same issue. I had an "About" view with a bunch of labels containing static text. I had a UIScrollView inside of a view controller and it was being pushed down. I had a label pinned to the bottom of the screen that was being drawn off page when it was not necessary to do that.
I had the following view hierarchy:
View Controller
View
Scroll View
Content View (UIView)
Label
Label
Content View was pinned to the bounds of Scroll View, but the width and height had equal constraints to View.
I resolved this by adding another UIView as a child of View. My hierarchy looks like this now:
View Controller
View
Inner View (UIView)
Scroll View
Content View (UIView)
Label
Label
I set the height constraint on Content View to be equal height to Inner View. This resolved the issue for me and everything started displaying correctly. Inner View is pinned to the borders of View.
NOTE: In the hierarchies, the Labels should be children of Content View. In the preview, it's showing that the Markdown lists can't go that deep.

May or may not apply to you, but if you have a Nav controller and you don't need it to be translucent you can do something along the lines self.navigationController.navigationBar.translucent = NO;
This may help with your odd offsets.

Related

Swift Storyboard List view Not Working?

Been following tutorials online to create a scrollable list view in the storyboard.
I have done the following
View Controller --> Scroll View --> Content View
The scroll view is constrained to the View controller.
The content view width is constrained to the View controller.
The contentSize is set to 1000 height for the content view.
The story board is set to 1000 height.
The scroll view is set to 1000 height.
It appears to only scroll down half way
Any ideas on what i have done wrong here
Thank you
try to remove the height constraint for the scrollview, and fix it to the bottom of your parent view.

Gap between main view and scroll view using Auto Layout in navigation controller

I'm experiencing some trouble managing a scroll view embed inside a navigation view controller. I'm using Auto Layout and I'll try to explain the problem the best I can.
I embed a scroll view in the controller's main view and pinned the top, left, bottom and right borders to main view's borders.
I embed a standard UIView inside the scroll view and gave it a fixed height of 800 points. This should act as a container for all my controls (let's name it content view). Then again I pinned its top, left, bottom and right borders to the scroll view's borders. Because the bottom space constraint of scroll view's descendant view was negative, I adjusted it bringing it back to 0.
For the width, I CTRL-dragged from the content view to the main view and added a Equal widths constraint.
Finally, I added an image view and placed it at the top center of my content view adding some further trivial constraint.
The storyboard for the situation I've just depicted is shown above (in the document outline you should see all the constraints I've defined).
My scroll view works, it scrolls fine and the image view is well-centered where it's supposed to be. However, there's a gap between the end of the navigation bar and the start of the scroll view and I can't figure out why. It seems to be as high as the navigation bar, but I have no idea about how to fix it. The image shown below should make you understand what I'm talking about (the content view is highlighted in grey for clarity).
I didn't write a single line of code to achieve this result. I would really appreciate any kind of help and I'm ready to give you all the information you need to help me address the problem.
I think you should uncheck Adjust Scrollview Insets property of your viewcontroller to get rid of this....
you have to uncheck Adjust Scrollview Insets

IOS : Make entire view scrollable, not only subview

I have a viewController in which I have a scrollView in which I have 3 views. This is a scheme :
ScrollView (UIScrollView)
Header (UIView)
TabBar (UIView)
Container (UIView in which I load a ViewController)
The main problem is that, in my container (in which there is a view controller), I have a collectionView (which can scroll) but I want my entire scrollView to scroll (not only my container).
So this is what I have :
And this is what I want :
Anyone can help me with this ?
I just solved this problem for my own project. Assuming you are using storyboards, I made the UIView a child of UITableView and made the UITableView extend the full viewport of the device.
Since UITableView implements UIScrollView you get full screen scrolling of your content.
General Rule your parent view has to implement UIScrollView and extend the full screen to get viewport vertical scrolling.
To do this, if you are not using auto layout or if you are adding views to container programmatically, you must manually set collection view frame to match its content size after you load some data on it. If you are using auto layout, you should create height constraint outlet and set its constant value based on collection view content size, again after loading data on it
You should set the frame of the container view to match the height of the view controller that it is loaded in it and set the contentsize of the scroolview based on the container height.
A scrollview will scroll only if its contents are bigger than its frame. This applies to the parent scrollview as well as the child scrollview.
Here in your case, for the parent scrollview to scroll its contents (Header, Tabbar & container) together must have greater height than its parent. The child scrollview (container) is already scrolling because its contents (a view controller) has greater height than its parent.
I have made both scroll views to scroll:
(1) The parent scrollview - by increasing the height of the child scrollview so that it extends below the main view controller's frame. The child scrollview is one of the contents of the main scrollview.
(2) The child scrollview - Setting a large content inside it so that it is bigger than its parent. I have used a long UIImage as its content.
Also, I have used autolayout and pinned the contents to the scrollviews' sides by adding constraints.
Hope this helps.
This is how it scrolls: Scrolling of UIScrollView inside another

Working with Top Layout Guide in UIScrollView through Auto Layout

I want to use the Top Layout Guide in the UIScrollView through Auto Layout. Without the UIScrollView Auto Layout works well with Top Layout Guide.
But when I embed the UIButton in UIScrollView, it doesn't.
I know that is because UIScrollView is not the same hierarchy level with Top Layout Guide. But I think there may be a good solution to resolve this issue.
You are right to be confused. It is a bit counterintuitive but the top and bottom layout guides are irrelevant to configuring a UIScrollView so that its scrollable content will underlap the translucent navigation bar, which is the effect you are trying to achieve.
what to do
Given the view hierarchy you've shown in the second picture, this is what you need to do on iOS8:
Configure the view controller so that "Extend Edges Under Top Bar" is checked (in code, use edgesForExtendedLayout). This will ensure that the view controller's lays out its root view so that it underlaps the nav bar.
Configure the scroll view constraints so that the scroll view's top edge has a zero offset from the top edge of its superview, not zero space from the top layout guide. This will ensure that the collection view fills the root view and thus also underlaps the nav bar, which is necessary for the scroll view's content to be able to scroll under the nav bar. (IB might fight you on this. See the footnote below.)
So now how do you make sure that the scroll view has any idea where the nav bar is, so that (for instance) it doesnt't always position its content under the nav bar? The answer has nothing to do with layout guides. In the view controller, check the box "Adjusts Scroll View Insets" (or in code, automaticallyAdjustsScrollViewInsets). This will cause the view controller to automatically adjust the scroll view's contentInset property so that the scroll view positions its content appropriately.
This will work.
what's going on
So why is this the answer? And why is it so confusing?
Frankly, it's easy to get confused because the top and bottom layout guides are prominently presented to us as elements that convey layout information about translucent overlaid elements. However, they are not the only "translucency-aware" layout mechanism. They are directly relevant only for positioning of "normal" subviews, i.e., not the view controller's root view, and not content within a UIScrollView.
Content within a scroll view (or a subclass like UICollectionView and UITableView) will always be positioned in a more complicated way involving the scroll the view itself, affected by properties like contentInset, contentOffset, etc.. (Really, if scroll view layout were a straightforward thing, why would Apple have dedicated WWDC sessions to scroll view layout for the last four years running?!)
To summarize, as the steps above indicate, the three distinct translucency-aware mechanisms for managing layout are as follows:
Extends Edges determines if the view controller positions its root view so that it underlaps the nav bar.
Layout Guides provide a metric that tells where the "main" content area is, taking translucent bars into account. You can use these with Auto Layout to position normal views so they don't underlap. Or you can access the numerical values in code.
Scroll View Insets are the right way to ensure that a scroll view's content can underlap but doesn't always underlap. The automaticallyAdjustsScrollViewInsets property on the view controller can do this for you automatically in simple cases. (Presumably, this property just causes the view controller to update the scroll view's contentInset based on the same values it exposes via the layout guides. So if you needed to manage the insets yourself, that's how you would do it.)
fighting IB's layout guide mania
A footnote on "Fighting with IB":
Unfortunately, Interface Builder might fight you when you try to constrain the scroll view edge to its superview's edge. If you do a ctrl-drag from the scroll view to the superview, when it pops up the menu of possible constraints to add between those views, it might try to get you to constrain the scroll view against the view controller's layout guides. This is because IB mindlessly prefers layout guides to superview edges, when the superview is the root view. But when you're working with a scrollview, this is the wrong thing.
Why? For instance, suppose you accept a constraint against the layout guide. Then you will end up with a top constraint on your scrollview that constrains it to topLayoutGuide-64.0. That -64.0 is a hard-coded value compensating for the exact height of a nav bar. So what happens when one fine day the nav bar does not equal 64pt? Or when you simply turn off the nav bar entirely? Or want to re-use this scene in a context without a nav bar? Answer: then you get a broken layout.
So how do you force IB to add a constraint from the scroll view to its superview's edge, as opposed to the layout guide? As far as I can tell, the answer is that you can't add that constraint correctly in IB by doing a ctrl-drag between views.
Instead, you need to select the view, and then use the "Pin" control at the bottom of the canvas. This is the one that looks like a capital H with a box in its middle. In the top section of the Pin popup dialog, the section with the little diagram showing superview space constraints, you can use the dropdown controls next to the text fields to configure if the space constraint binds a layout guide or a superview. This is shown below:
Github link to demo projects: https://github.com/algal/ScrollViewUnderlapDemo
While algal's answer seems to have worked prior to iOS 9.0 it is unnecessarily complicated and broken beyond iOS 9.0. The easier way that also works beyond iOS 9.0 and requires no interaction with auto layout is to simply do the following:
Ensure Adjust Scroll View Insets is checked for the ViewController in Interface Builder (or set automaticallyAdjustsScrollViewInsets to true programmatically).
Set the class of the ViewController's root View to UIScrollView in Interface Builder (or replace it (self.view) manually with an UIScrollView in code).
While adding spacing constraints, Xcode does not show items which have negative distance with your view. It seems you added a vertical space constraint between UIScrollView and UIView. Delete that constraint, move your scroll view to below Top Layout Guide and add a new vertical space constraint between UIScrollView and Top Layout Guide.

Adding UIViewController with ScrollView and AutoLayout to another UIViewController

I have a UIViewController (added to a UINavigationController) that adds another UIViewController's view using standard code:
[self addChildViewController:toVC];
[self.view addSubview:toVC.view];
[toVC didMoveToParentViewController:self];
That's working fine.
The childViewController's (toVC) view is constructed using Interface Builder and Auto Layout, and it contains a UIScrollView. If I load toVC into my app directly into a UINavigationController (instead of adding it to another view controller) scrolling works perfectly.
However, when adding toVC to my mainVC using the above code, toVC's scrollView doesn't scroll at all and I'm at a loss as to what I need to do.
You should set the frame and the autoresizing mask / auto layout constraints of the child view controller's view. Even though the child controller's view is set up with auto layout, it still needs to be positioned in the parent view controller's view with whatever system the parent controller is using.
EDIT: Another potential issue, since you mentioned that the scroll view doesn't scroll when presented:
Did you set top, left, right, and bottom constraints for the scroll view's subviews? Scroll views treat "space to superview" constraints differently than normal views do; rather than defining where its subview is positioned, these constraints define the content size. Failing to set constraints on both top and bottom, or both left and right, may leave the scroll view with a content size of {0, 0}, in which case the scroll view would not scroll.

Resources