Working with Top Layout Guide in UIScrollView through Auto Layout - ios

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.

Related

Xcode Scroll View Auto Layout Issue when Embedding with Navigation Controller?

I seem to be having issues when it comes to embedding a scrollview within a navigation controller. So here are the steps I take, and its really easy to replicate the weird error i seem to be getting.
1.) Drag and drop a UIViewController
2.) Followed by a UIScrollView
add constraints to UIScrollView, 0 on all sides, uncheck constrains to margins.
3.) Drag and drop another View into UIScrollView
add constraints to UIScrollView, 0 on all sides, uncheck constrains to margins.
Control drag from view into scroll view, make same width and height
After the following steps you will see in the following images that the view is not following the constraints I set, for some reason it thinks 0 is +64 from the top of scroll view? Not sure what I'm doing wrong here as it is very annoying visually.
In your view controller's attributes inspector, look for the Adjust Scroll View Insets and Extend Edges Under [Top|Bottom] Bar options.

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

UIImageView resizing issue in UIPageViewController

I'm building a new app and wish to have a "Welcome walkthrough" at the beginning wherein I have a storyboard with a series of images presented in a UIPageViewController. I have it loading the images and all of that just fine, however the images are resized whenever they go beyond being the "previous" or "next" ViewController. I am using Swift to develop.
Here is a video of the issue: http://youtu.be/dXcjjT-8Bk0
I have tried all of the different View Modes (Aspect fit, aspect fill, redraw etc.) and they all behave the same.
I am using Auto-Layout + Size Classes as I wish to simplify the development for different screen sizes. The current constraints I have that make the UIImage appear at the right size are:
Align Centre X to Superview
Top Space to Top Layout Guide
Bottom Space to Bottom Layout Guide + Equals: 50
I am currently using Aspect Fit which gives me the correct image (after they have done their 'resizing behaviour'.
Can anyone guide me further as to how to fix this?
From your video, I noticed that your UIImageView is always "resized" at the top, not at the bottom. This is most certainly because of your autolayout constraint you call "Top Space to Top Layout Guide". While your UIImageView's view controller is being transitioned through your scrolling page view controller, it doesn't know where the top layout guide is, so its topLayoutGuide.length is 0. Only after the animation completes does the view controller get a positive value for topLayoutGuide.length. Yes, the page view controller should be a bit smarter than this, but it's not.
You can either stop using the top layout guide and make an autolayout constraint relative to the top of its superview. Or you can continue to use the top layout guide but account for when it's length is 0. You can do this by making an outlet for your storyboard's NSLayoutConstraint and overriding viewWillLayoutSubviews() in your ViewController containing your UIImageViews:
#IBOutlet weak var topSpaceToTLG: NSLayoutConstraint!
var parentTLGlength: CGFloat = 20
override func viewWillLayoutSubviews() {
if self.topLayoutGuide.length == 0 {
// Lengthen the autolayout constraint to where we know the
// top layout guide will be when the transition completes
topSpaceToTLG.constant = parentTLGlength
} else {
topSpaceToTLG.constant = 0
}
}
This will always put the top of your UIImageView at the top layout guide, assuming that the status bar is always 20 points. Before laying out subviews, it will check to see if the top layout guide length is 0 or not and adjusts your autolayout constraint accordingly. After the transition animation completes, layout is triggered again, and the top layout guide length will be the expected value, so the constraint constant can go back to 0. Even better than hardcoding the value is to pass in the parent view controller's exact length during initialization, accounting for any possible changes to the top layout guide like adding a navigation bar.
From the video I think you could solve this by preventing the UIPageViewController from extending under the top bars.
In xcode you can do this using the attribute inspector for the page view controller by deselecting Extend Edges Under Top Bars.
This should prevent it paging in under the status bar I think helping to avoid the switch you see.
I figured out my problem was that when the view controller began animating the Top and Bottom Layout Guides had no height. The right and left margins didn't either. When the view finished animating they all received a height or width and my view resized itself.
I fixed this problem on my project by adding vertical constraints between my objects and their super view instead of to the Top/Bottom Layout Guide. I also had to change my horizontal constraints to ignore the side margins.
The last issue I came across is that I had to account for the status bar myself. It may or may not be there or it could be a double bar, like when you are using Maps.

Layout TableView with navigation bar and tool bar

I placed TableView on a middle of a layout with constraints to stay aligned with top/bottom guide bars and left/right sides.
When I insert prototype cell it doesn't go to the top and it's the same at run time. Seems like TableView holds space on top for something?
How do I make first cell to appear on top?
In your storyboard, adjust scroll view insets of your view controller is selected by default.
The best way to solve this problem is to add top and bottom constrains between the table view and its super view, instead of the layout guide, because the view controller will adjust the insects of the table view automatically.
By default, iOS adds a content inset on the UITableView. There are two solutions. You could either set the layout constraints to the top and bottom of the view instead of the top and bottom layout guides, or you could click on the view controller and in the attributes inspector, disable Extend Edges for Under Top Bars and Under Bottom Bars.

AutoLayout or not? How to position 3 subviews?

I can't figure out how to correctly position subviews in a Navigation Controller.
I am trying to position a view, table and another view.
If I turn off AutoLayout than top view and table are ok but my bottom view is pushed off the screen.
With autolayout I get both the table and bottom view in the wrong place:
I try to set frame in viewDidLoad as follows (calendarPicker is position at the top below navigation bar), I want the configPanel to be on the bottom, I hide bottom bar on Push.
self.eventsTable.frame = CGRectMake(0, CGRectGetMaxY(self.calendarPicker.frame),
self.eventsTable.bounds.size.width,
self.view.bounds.size.height - self.calendarPicker.bounds.size.height
);
self.configPanel.frame = CGRectMake(0, self.view.bounds.size.height,
self.configPanel.bounds.size.width,
self.view.bounds.size.height - self.configPanel.bounds.size.height
);
Should I rely on autolayout? How should I make my constraints?
I believe my problems arise due to autosizing of the table mostly, but given that I am setting its frame size why would it not change? I do not want to remove auto layout since it is used on other views designed in the storyboard and from what I understand it applies to all?
Is there something that I am missing that needs to be done on top of setting the frames of individual controls?
EDIT:
I think my biggest problem is autolayout and inability to size table appropriately. If I add constraint to the bottom view to be 0 from bottom of the view, it will originally appear correctly. However consequently when resizing table and top view, the table will push bottom view down sizing itself to occupy all available space.
I need to force UITableView to be no more than height between the bottom of the top view and lower view, but still not sure how to do this.
Somewhat closer
Removing code for frame change of the table fixes the issue of the bottom view being pushed off. However in this case top view overlapps table when it changes size at the same time not being drawn correctly:
Uncheck autolayout and then set frames of all three subviews.
Or
If you want constraints then you can use NSLayoutConstraint class to add constraint to your subviews.
I recommend against switching off autolayout, especially because you can't do it on a view by view basis. Autolayout is a must if you tend to do i18n or want to make sure your app does well in different screen resolutions/orientation.
Try This!
disable use Autolayout in storyboard. Place your three views on view controller(view1, view2, view3). Next go to size inspector and use autosizing masks for all three views. Check this, which will help you
http://www.raywenderlich.com/50317/beginning-auto-layout-tutorial-in-ios-7-part-1

Resources