How to make UIScrollView height dynamic from storyboard with the new Safe Layout feature? - ios

I used to make my scroll views have dynamic height using auto layout constraints (adding a content view to the scroll view to hold all the views and pinning them etc.) But now as with the new Safe Layout feature, I cannot achieve the same dynamic height scroll view behavior despite all my trials.
All I want is a scrollable screen with an image at the top, with title and text labels below whose content is not known beforehand.
I hope I could explain my problem well enough. Thank you all!
Cheers,
Murat

Related

Objective-c: Dynamic size of UIStackView, depending of it's content

I have an UIStackView which is inside a scrollview. the content of the stackView is dynamic, depending of how much views created and added with the methode "addArrangedSubview". if I have a few subviews, there is so much spacing between them, and if I have too much views, they become compressed.
I have:
_viewController
|__ view
|____scrollView
|______stackView (dynamic content)
I set the stackview to:
Alignement: fill
Distribution: equal spacing
Spacing: 5
and of course the constrains top/bottom/leading/trailing
I want to increase the size of the UIStackview every time a view is added, and keep the size of my added subviews.
Maybe something is missing or I have a bad understanding.. someone can explain to me how to do it ?
I'm working with objective-c
I've a detailed Medium post on this topic. You can take a look there for a step-by-step guide. But I'm also adding a brief explanation here as well:
You should have all of the necessary constraints set-up for the scroll view to it's super view. Then comes your stack view that is the sub-view of this scroll view. You might have pinned all the four edges of this stack view to the scroll view as well. But here comes the actual concern.
UIScrollView doesn't work as like other views. It has a contentView. This content view is responsible for scrolling behavior. If there are more content that don't fit in the frame of the scroll view than the scroll is enabled.
So for setting up the content view correctly, the scroll view must know the size of the content view so it knows when to stop scrolling. Here size means the actual width and height. But this size can't be determined from the constraint's setup because they are calculated dynamically by the auto layout engine.
In your case, the stack view acts as the content view of the scroll view. You might have pinned all the edges of the stack view to it's superview - UIScrollView. But that isn't enough for the scroll view to calculate the content size. You must also provide the:
width & height - if your scroll view is scrollable on both axes
width - if you want to scroll vertically and restrict scrolling horizontally
height - if you want to scroll horizontally and restrict scrolling vertically
As you need horizontal scrolling, you must restrict the vertical scrolling by providing the height of the stack view equal to the scroll view (it doesn't always need to be the same height as the scroll view, but should cover the whole height of the scroll view by other means). And you will also need a placeholder x-axis constraint to make the Interface Builder happy. The actual width of the content view will be covered by the sub views that will be added to the stack view.
Important: You should add a Horizontally in Container constraint to the stack view and make this a place holder that will be removed at build time. You can do this by selecting the constraint in the document outline and opening size inspector where you will get a Remove at build time check box. You check that box, you are ready to go.

UISlider causing horizontal scrolling

I have a simple screen, with a slider and a label positioned next to each other horizontally. I have embedded these inside a UIScrollView (I set this to fill the screen and used 'Add missing constraints'), because I will need vertical scrolling later down the line. I don't however, want horizontal scrolling. I have seen numerous posts on here and other sources about people wanting to disable horizontal scrolling, however I'm not sure that's what I want to do, I think I need to restrict the UISlider from causing the horizontal scrolling; I think it is trying to take up more width than the screen. I have added what I think are the necessary horizontal constraints:
Leading space to container for the UISlider
Horizontal spacing to the UILabel, and
Trailing space to container for the UILabel
But this still causes horizontal scrolling, and the UISlider's are the cause, they are taking up more room than I want, as seen below:
I have tried disabling horizontal scrolling in the code using a few techniques, one being:
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView.contentOffset.x>0 {
scrollView.contentOffset.x = 0
}
}
but this does not seem to stop the horizontal scrolling.
Can anyone offer any suggestions?
Thanks in advance.
My suggestion is to never use Add missing constraints. It never does what you really want.
Here's the problem. You are laying out your UI on a ViewController in the Storyboard that is square. Apple did this to remind you that you need to be flexible in your design, but it doesn't match the size of any device. When you Add missing constraints, it uses the absolute dimensions of that square to create the constraints which are certainly wrong.
In your specific case, it is giving the slider a width that is too wide, which is why the slider goes off the right side of your screen.
Here's the trick about scroll views. If the contents inside of a scroll view are wider than the scroll view itself, then that content will scroll. The same applies vertically: if the contents inside of a scroll view are taller than the scroll view, then the contents will scroll.
In order to design this to work on all phones, you need to make sure that the contents of the scroll view are laid out correctly for each phone size. Which certainly means you don't want to use specific widths for both the label and the slider because you'll end up with the wrong width for some device, if not all of them.
The best way to do this is to:
Drag out the scroll view and add it to your ViewController. Add constraints to make sure it is properly sized on all phones, such as attaching it on all sides to its superview with a fixed distance.
Drag out a new UIView and drop it on the scroll view. Drag its edges until it exactly matches the size of the scroll view. This will be your content view. Pin all four edges of this content view to the scroll view with offsets of 0.
Here's a tricky bit. Even though you've pinned the content view to the scroll view, its size of free to grow because that is what allows it to be bigger than the scroll view itself and allow there to be content to scroll over. To keep your scroll view from scrolling horizontally, you need to make sure the content view has the same width as the scroll view on all devices. To do that, find the scroll view and the content view in the Document Outline to the left of the Storyboard. Control-drag from the content view to the scroll view and select Equal Widths from the pop-up.
You still haven't told your content view how tall it should be. For now, give it an explicit height constraint of 1000. That will be enough to scroll.
Now, add your label and slider to the content view. In addition to constraining them to each other and to the edges of the content view, you will need to give your label a width constraint. Then Auto Layout will have all of the information it needs to compute the width of your slider. Auto Layout knows how wide the content view is (which will be different on different devices), it knows how wide your label is, and how far everything is from everything else, so it will just stretch the slider to fill in the rest.
If you do all of this, you will have a UI that is properly sized for all devices in all orientations that scrolls vertically.
Just embed all view in your UIScrollView in a UIView, give it the required constraints then the slider and label will stay.
That worked for me just now.
UIScrollView is special when you want use AutoLayout with it, subviews can not be added directly, it needs a container view to constraint the contentSize of UIScrollView, Auto Layout Guide:Working with Scroll Views explains the detail reason, and you can find many solutions to solve UIScrollView's auto layout on Google, Such as this answer.
To be honest, it's confused and complicated to understand UIScrollView's auto layout, but if you overcome this, others auto layout question is easy to resolve.

How To make Scrollable our Freedom View controller using constraints?

Actual image is.....
->View controller
->view
->ScrollView[[top,bottom,leading,trailing]to its super view]
->contentView[[top,bottom,leading,trailing,vertical,horizontal]to its scrollview].
that actual image is for iphone6 ->iphone4s,Any one help me how to do this.....
When it comes to set constraint with scrollview then little bit extraa effort is required for handling scrolling so that Scrollview + autolayout works.
First of All What exactly take place when autolayout is used :
When you use the auto layout, your views will resized automatically based on the constraint properties.
But, when you use a UIScrollView with auto layout like a container, the auto layout not works like expected.
so Here is Very good tutorial g8production which clears this confusion. Big Thanks goes to g8Production for providing this tutorial.
I think you are setting the constraints for content view in wrong way. You are trying to set the constraints on contentView that are related to its container(scrollview) which don't know content size of its own. It determines its content size from what it contains (in your case contentView,). Both are dependent to each other for their size. So both are confused.
To solve this you should first set the fixed height and width of contentView and then make outlets of these constraints and update these constraints with the height and width of superview when your view loads.
add top, bottom , leading and trailing space to your scroll view to view. It should be set accordingly, how you you want it to be seen. Then set same for content view (image). just addheight and width constraint for your image.
Misha :)

iOS: animate UICollectionView vertical expansion with constraints?

I've a view controller with a UICollectionView on top (using the default grid layout) followed by other controls below it. As I add / remove cells to / from the collection view, I want it to expand / contract in the vertical direction (so it has enough rows to show all of the cells and no more), and all controls below it to move down / up the screen accordingly. If you imagine how email app UIs work when you add / remove addresses, that's the effect i'm trying to implement. Achieving this effect using constraints, though, is eluding me. Any help would greatly, greatly appreciated!
What I did:
removed all constraints in IB
added a height constraint on the collection view and connected it to an IBOutlet
had IB add missing constraints
add an observer for the collection view's contentSize property
Now, when I add a cell to the collection view and tell it to reload, i'm notified when the contentSize changes and can set the constant for the height constraint to the contentSize height. Result: a collection view that resizes to fill it's content, with the other controls moving up / down as needed :-)

XCode: laying out scrolling content in IB

TL;DR: What's the best way to layout over-sized scroll-view content in Interface Builder?
I am trying to figure out the least painful way to layout offscreen / scrolling content in Interface Builder. My current approach has been to tweak scrollview content width and height constraints to see all content in IB, before reverting to proper contraints for building, but this gets to be a hassle. That said, I would prefer to do more more graphically, and less programmatically for general ease of editing.
The general approaches that I can think of are:
use embedded segues to build up views in a size-accurate fashion (the most intuitive, flow-charty approach)
tweak constraints for editing, reset for building
use placeholder values / constraints in IB (haven't tried this yet)
layout over-sized content programmatically
In more detail, I am trying to build a vertically scrolling view comprised of sub views. Each sub view is self contained and can potentially appear in more than one context and I would like to keep them isolated (in fact, I am embedding them as well, but that does not affect the question). What I would like to do, is use IB to layout a tall composite content view. I would then like to create the outer scroll view with a single content view again of a container view, embedding the composite content view. Ie. the grey content view on the left embeds the taller red / blue content on the right, and I am wondering if I can deduce the height, so it could be device agnostic (the views are square, so they will take their width from the device)
So summarized, is there anyway that I could derive the hosting scrollview's contentView height from the actual height of the content of the embedded view, or should I instead use approaches 2, 3, or other?
To be clear, Auto Layout requires that scroll view content views define constraints for the edges, and width / height dimensions. In my case, I would like to be able to preset the width to the device width, but leave the height to be derived from the red/blue content view. IB won't let me drag constraints between the embedding and embedded scenes in Storyboard, and when I leave the field missing, it uses the prototype values for the content height.
Is it recommended to use placeholder sizes (3) in IB to make the content visible for editing, knowing that run-time constraints will render it properly (ie over-size scroll views in Storyboard so all scrolling content is visible)? Conversely, is it foolish to even try - should I do all scroll-view content composition programmatically (4)?
Thanks in advance.
Edit:
Placeholder constraints allow you to get by, but force you to change the priority of the run-time constraints, and trigger IB / layout errors and warnings for conflicting constraints, despite selecting the placeholder checkbox on conflicting design-time-only constraints.
Use a UITableviewController with static cells. The cells will be your content views.
You can scroll the tableview down in IB to see the cells at the bottom and you get a nice extra behaviour like scrolling the views to the correct position when the keyboard is opened so it doesn't hide the textfield you clicked on.
And you don't have to worry about the scroll view at all.
If you want to reuse your content views you can make the table dynamic of course and provide the content views by code.

Resources