UIStackView losing constraints when using custom XIBs - ios

I am attempting to dynamically add views to a UIStackView.
I have a XIB with a full screen UIStackView within. I then have some views - labels and text field/ text views.
The idea here is that I have a class that manages these individual views and that class has it's own XIB file for the layout.
So the class that handles the outermost UIStackView will grab the layout from the individual classes and add it as a subview to the UIStackView.
When attempting to do this, the view appears squashed and not in keeping with the original constraints.
I could offer code but I will explain the constraints.
The outer UIStackView is just stretched as full screen with distribution fill and equal spacing.
An example of an 'inner' view would be a label. I have an individual XIB file that holds a UIView and then a Label within that. The label is pinned to the center of the UIView.
I believe the issue lies with adding custom XIBs to an existing UIStackView. I'm not sure how much more information I give/ is required but I suspect there is something I am missing that someone may spot straight away from the information given.
Any help is appreciated. Thanks.

The UIStackView does exactly what you are asking it to do. If the constraints are setup so the UIStackView fills in the entire screen, then you need to provide content, that when it is equally divided on the screen, has the expected layout, each of the view inserted will be resized to be equally sized.
If you do not know how many views are going to be used, but you need each of your views to take their respective aspect ratio on the screen, then you can use the "Fill Proportionally" option. The UIStackView will add the height of every view, then divide its own height and distribute it proportionally to each view. If you add too many views this too will start to squash your views.
To avoid these kinds of issues, you may wish to put your UIStackView inside of a UIScrollView. You then setup a constraint with very very low priority to set the UIStackView height to 0, as you insert your views the UIStackView will automatically be resized to fit in the different views.
Good luck!

Related

When should we use UIStackView?

I am designing iOS Application since 2 years. I have designed 8-10 applications with using UIScrollView, UICollectionView and many more native views. But I have never used UIStackView. I have gone through many documents of UIStackView, but I am not able to identify exact situation where i can use UIStackView.
Can anyone guide me for same?
Thank you in advance.
UIStackView is useful when you need to repeat same views multiple times like in Sing up view. We use many textfields and manually set constraints between each textfields. But if you put all textfields in stack view then you just need to set required constraints of stackview only and not textfields. Textfields inside stackview will be arranged automatically without Autolayout.
Sometimes we need to hide view and we want to remove its occupied space so at that time use of stackview is recommended because if you hide any view that resided in stackview will also remove its occupied space automatically.
I believe you mean UIStackView. You use a UIStackView to group UI elements together to simplify setting constraints. In other words once you've added objects to the stack view, you constrain the stackview and set attributes on it rather than on each individual element. This results in far fewer constraints.
In this example, I've added a label and a text field to a stack view. Then I added constraints to the stack view and set the spacing in Attributes Inspector to 20.
If I had to constrain the label and the text field I'd have many more constraints.
UIStackView helps you to set autolayout. It isn't neccessary!

UIStackView without distribution or filling

I have a view on my app where I need to be able to push a dynamic number of custom subviews (the number of views changes whilst its running).
My original idea was a tableview, but it seems a bit OTT setting up all the delegate methods. Then i found UIStackView, which is great as it handles redrawing and resizing when I add and remove the subviews.
However when I have a small number of views in the UIStackView. It will either make each view bigger vertically to fill the space, or distribute them across the stackview depending on the various settings.
What i'd like to have is each view I add just be appended under any existing views in the stack. I don't want distribution or filling of the blank space in the stack. Kind of like a UIStackView with each subview having a height constraint.
I assume this isn't possible with a stackview, so how else can this be achieved? Do I have to use the tableview after all (or fallback to laying it out in code?)
If your views have a defined height constraint, this should work with the Distribution being Equal Spacing.
I managed to do this by setting distribution to fill proportionally and then i pushed at the bottom of the stack a UILabel with some spaces as string. Pushing an empty view or empty UILabel didn't work. I had some constraints errors after this though and didn't investigate further.

What are the correct constraints on UIScrollView from Interface Builder? Pure Autolayout from IB

I have been trying to get ScrollView to work for 2 days now, and it doesn't work at all. Most of the suggestions here on SO and other websites say that you need to pin the ScrollView to the root view and then place a ContentView (UIView) inside ScrollView and then pin it to all sides of the ScrollView (so that the scroll size can determine the contentSize... However this does nothing). There's also conflicting information out there, one video says that there needs to be a constraint from the bottom of the ScrollView to the ContentView. Neither solution has worked for me. Here is what I've been doing in most of the combinations I've tried:
UIView -> UIScrollView
Pin all sides of the UIScrollView to the UIView
Create a UIView (name it content view) and place it inside UIScrollView
Pin all sides of the UIView to the UIScrollView
Problem at this point: UIScrollView needs constraints for X or width AND Y or width. The only thing that seems to solve the complaint is setting the UIView inside the scroll view centered horizontally and vertically, but this does nothing to make scrolling work. Another option is setting the UIView equal height and width to scroll view, but again, that does nothing other than remove the complaint.
I don't understand. Isn't pinning the sides, setting the constraints? IB seems to think that this is not the case.
What are the correct constraints needed? All I need is a simple view with stacked controls (to fill out a form) and the screen needs to be able to scroll if the form is longer than the screen.
I'm using iOS for the first time, and building purely from IB for now... minimal code solution would be best.
You are half way there. First you need to decide what you are going to display in the scrollview, you have placed a content view, that needs to have an intrinsic size. You can choose to put there static or dynamic views. Static views will have their size defined at design time, and that will resolve the UIScrollView AutoLayout constraints. If instead you are doing it at runtime with dynamic views you will need to choose a default size for your content view, create an IBOutlet for the width and/or height of your views and then resize them at runtime altering the outlet in viewDidLayoutSubviews. The video you linked explains that quite clearly.

How do I prepare a UIViewController's view for being added to a UIStackView?

This is what I'm trying to do...
I have one view controller that needs to dynamically display different subviews based on the presence of some data.
Here is a simple mockup. Each colored block represents a unique subview.
Sometimes the green block needs to be at the top, sometimes the green block won't display at all, sometimes the light blue block will be something different, etc.
Each subview has interactive elements, so I've been creating and adding them like so:
Defining a new view controller
Defining its view
Calling addChildViewController and didMoveToParentViewController
Calling addSubview on myNewViewController.view
Using SnapKit to make auto layout constraints to position the view
I want to transition to UIStackView because it seems a good support system for this view because all I need to do is stack its subviews. I'm seeing many conflicting constraint errors and unexpected view frames when trying to add subviews with their own inner auto layout constraints.
Question
Am I setting myself up for failure here by embedding the views of 4-6 view controllers in the view of one view controller?
Also, how do I give the added views properties like minimum heights or content sizes without seeing many breaking constraints with UIStackView? (So they can stack, but one of them is say, 400 tall, and the other is 200 tall)
Answer
You can absolutely do this using UIContainerViews combined with UIStackViews and UIScrollViews, it's a complicated setup so here's a starter project to get you started:
https://github.com/Rnorback/ScrollingStackView
Here's what that project looks like:
You want the containers to have different sizes. In order to do that, simply add height constraints to the containers with Autolayout. If you want to change the height of the scrolling. Then you'll need to change the height constraint of the UIStackView.
Brief Explanation
When creating this setup, you have to make sure the UIStackView distribution setting stays in fill. That way you can set height constraints on UIContainerViews.
When setting up anything in a UIScrollView you have to make sure that the object is pinned to the edges of the scroll view and has a defined width and height, otherwise the scrollview will throw a constriant error. I think of it like the scrollview tries to press in on all sides of your content view and if any side gives, the scrollview won't be able to present it properly.
Hope this helps.

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