Design UIView so that it can dynamically change its height based on the content height - ios

In a XIB file I have a CustomView related to a class with the same name.
The view content is quite simple.
There are a vertical stackview and inside the stackview there are two labels. The stackview is vertically aligned to the view that contains it:
The first label (title) contains a static text (always a few characters).
The second label (subtitle) can have variable length text.
I add this CustomView with other similar views as a subview of a Content View, as it was a “row”.
for i in 0...aViews.count {
var v = CustomView(frame: CGRect(x: 0, y: _y, width: 320, height: 100))
v.labelText = "my long label text ..."
contentView.addSubview(v)
// ...
}
As you can imagine, the title label should have a fixed position (top and left), that is, there cannot be a row with the title starting at 10 points and another at 14.
I must admit I naively thought the position of the label would have been automatically managed by the same fact that I aligned vertically the stackview. I was wrong and I noticed no problem at all, until they told me the second label, the subtitle, could contain more than one line.
I added lines to the label directly in the storyboard, and found out that:
the container of the stackview doesn’t change its height based on the height of its content;
the position of the “fixed” elements is maybe vertically centered but not fixed;
What I need is the following:
the labels should be “grouped” and aligned vertically: there should be the same amount of space from the top of the first label to the upper border of the container and from the bottom of the second label to the bottom border;
when the second label has to display more than one line, the container view should change its height accordingly;
Is this possible? How should I design my view?

OK - looking at your project, you're not far off... just a couple key items.
In your CustomView.xib, you had no constraint between Fixed Label and Date Label. The result is that the Date Label grows up from the bottom of the view, with nothing to stop it.
Also in your CustomView.xib, you had the Fixed Label constrained to the trailing edge... I assume you want it left-aligned with Date Label
When creating your CustomViews, you were setting a height constraint of 100 -- which defeats the purpose of allowing the content to determine the size.
In CustomView class, you had contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight] commented out. That line should be there.
In your view controller, you added a height constraint to contentView with a low priority. Best option is to make that a Placeholder constraint (double-click the constraint, and select the Placeholder checkbox). That way the constraint is removed at run-time, but is there during design (and Storyboard doesn't complain).
You were doing a few other un-necessary things - likely you were trying different stuff to get it to work.
The view setup (adding your views) is best done in viewDidLoad() -- definitely not in viewDidLayoutSubviews()
I'm guessing that, in your CustomView, instead of an explicit width of 200 you probably want to constrain the Date Label leading and trailing, allowing it to horizontally stretch based on the device width... but I left it at 200.
If you can follow that information, you should be able to fix the issue(s). But, I put the project up as a GitHub repo for you, so you can get it in a "working" state, and so you can see the changes.
Here's the link to the repo: https://github.com/DonMag/ATester2
Here is the result:
And scrolled up a bit:

I think you are mixing absolute positioning of outer views (i.e. manually setting frames) and using autolayout constraints for their inner components, and wrongly expect the dynamic autolayout part to somehow "reach all the way up" to the outer views. For autolayout to do that, you'll need to use autolayout all the way up, including the way CustomViews are positioned in your contentView.
If for whatever reason you do not want to use a TableView or CollectionView for this you could also, for example, try adding your CustomViews as arranged subviews to a vertical stackview that has top/bottom/leading/trailing constraints to contentView, then replace the "Align Center Y" constraint of your CustomView with "Align Top" and "Align Bottom" constraints to actually allow your labels to "push" from the inside if they need more vertical space.
Edit here's a quick sketch to illustrate that setup:
Edit 2 here are a couple of screenshots to clarify further. With a basic layout like this:
the result will look like this at runtime:
Note that the UIStackViews use the "Equal Spacing" Distribution in this example. If you want to create and add your CustomViews programmatically, use the StackView's func addArrangedSubview(_ view: UIView).
Good luck!

Related

How to set stack view height according to its subviews height?

I have a requirement to scroll six different type of view, Each view has different size, One of the views is: About Me. In About Me, I have to show the title (i.e. ABOUT ME) and text below it and label height should be according to text height.
Till Now I have designed this requirement as:
Root View
- Stack View
* Item 1 with fix height
* Item 2 with fix height
* UIView as About Me:
- Label as title
- Label as text with max lines set to 0 and width equals to container width.
The problem is: I can not set fix height of about me UIView and it does not resize according to label text.
Please suggest the appropriate solutions.
Hi you can get the intrinsic height of the stackView like this:
let size = stackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
I know Im kinda late to reply to this message. But I had the same issue and I found this piece of code. Thought anyone who looking for this might be useful :)
I don't think that you declare your problem clearly. The first thing I want to point out is that UIStackView can infer its size by the size of its arranged subview. But you must first confirm that the constraints added to the subviews are proper. So if you don't get the resizing effect you want, you must be missing something. Maybe you're not making the reasonable constraints.
To clarify your situation, you'd better add some screenshot of your constraints added to your 'About Me' view. By the way, what's the axis of your UIStackView?
I don't know either of these. So I have to make some assumption for me to explain more about your problem.
The axis of your UIStackView is UILayoutConstraintAxisVertical.
And your UIStackView(or all of your content) has a fixed width.
With the above conditions, Defining a view that its height is decided by its text will be simple. Because UILabel doesn't have to have a fixed size. You only need to specify its width(or specify its leading and trailing to its superview), and its height will be automatically computed based on its font and content.
Your About Me view has two label, so you should
Make sure of these things:
Title label: The top, leading, trailing constraints to the super view. and the bottom gap to the subtitle label(whatever you call).
Subtitle label: The leading, trailing, bottom, constraints to the super view. and the top gap to the title label.

How to use auto layout to resize views in a table view cell?

I have a cell in which I place four buttons and four labels. Each button gets assigned a picture with width 50 and height 50. Furthermore, all buttons have a corresponding label describing what they're intended for.
My objective is to have the buttons and labels resize to keep the buttons' and labels' aspect ration intact while the screen dimension changes on different devices. I have been playing with auto layout changing the hugging and compression to achieve this but haven't been successful yet. Any help would be much appreciated...
I think you should take a look at a UIStackView, because this seems exactly as a use case for stack. Just put each pair button/label in a stack, and then all four pairs into a horizontal stack, which you constraint to the cell itself. You should be able to handle all you need just by configuring the stack’s properties (axis, distribution, alignment, spacing).
Embed your button and label into a view. Set the width of this view equal widths to content view and change the multiplier value to 1:4. This will adjust the widths of the views according to superview. Also, set the top and bottom constraint to 0 for this view.
Provide center align y-axis constraint to button after setting the width and height constraint to 50. Set its top constraint to a value you deem fit.
Set labels's leading and trailing constraint to a value like 8. Choose center alignment for text. Also, provide top constraint to buttona nd bottom to its superview.
Copy the view and paste to create the three views and provide them equal widths constraint to the first view. Also, provide their leading, trailing, top and bottom constraints.
Here are a fast tutorial in how to achieve that:
1-
2- completion of the first Gif:
Note you can achieve the same output using a UIStackView

UIScrollView with multiple multi-line labels and AutoLayout?

Is there a way to achieve this? I have tried literally everything and nothing has worked for me yet.
So basically what i want to do is the following: I have a scroll view with some labels in it. All the labels get their text from a server and I have set their number of lines to 0 so that they change their height according to the amount of text. However, this does not affect the scrollview content size(even though my labels have constraints set up to the bottom,top,leading and trailing of the scrollview) and the labels go off screen and I am unable to scroll down. Can someone point me in the right direction to how I would set up my constraints, my view hierarchy and etc?
Any help is much appreciated! :)
Late, but this solved it for me:
Set leading (I have a 32pt inset), trailing and top constraints. The trailing will not actually seemingly do anything..
Make the trailing Greater Than or Equal to avoid localization alert.
Finally, add a new Equal Width constraint to the label matching the scrollview. Use the constant to subtract the required padding (I used 64 due to mirror my leading inset).
And voilà! The Label will align correctly both in IB and in-app.
In Scrollview the last view's bottom constraint is so important. You should set its priority to 250 and put it to Greater than or equal.
Remember you should only change the bottom constraint of the last view, which in my case it's the continue button.
I would consider using UITableView instead, it has several benefits:
It allows for reuse of cells, if all the cells look the same
It manages recycling of cells when the number of values you're getting from the server increases (decreases memory pressure when number of cells becomes substantial)
It allows for more flexibility with the content (it's quite often for design to change last second or to evolve over the course of the project)
Most importantly, UITableView support auto sizing cells (as of iOS8), you need to specify the constraints between the label and the borders of the cell
There are several resources to start with:
http://www.raywenderlich.com/73602/dynamic-table-view-cell-height-auto-layout
https://www.captechconsulting.com/blogs/ios-8-tutorial-series-auto-sizing-table-cells
http://www.appcoda.com/self-sizing-cells/
Use a container view in a scrollView
Add constraints to superview (leading, trailing, top, bottom, height,width)
Make IBOutlet of constraints that you are going to update.
Add you all labels inside that view.
Update constrains/frame of your label so that it fits the text.
How much you increase the label height you should increase the container height too.
If the label count is not fixed use custom label class to add subview.
Perhaps you should need to understand how ScrollView works in Storyboard with autolayout.

Center multiple UILabels on a line

I want to horizontally center multiple UILabels - as a group - on a line in Interface Builder. One the straight view controller i could not figure out how to do this. I read comments about place the UILabels in a View and then centering the view in the view controller. When I tried this, overtime I said to update frames in the interface builder, the View would be resized down to nothing. (i.e. its height and width would be set to 0 by IB). I need to know how to get this to work in interface builder.
An example of a line containing multiple labels I want horizontally centered is below. The braces simply indicate the start and end of each label and are not art of the text. The <- 6 -> is meant to indicate the trailing space from label 1 to label 2 is 6
Label 1 Label 2
[Rating: 0.0]<- 6 ->[Distance: 125.34 Kilometers]
Any suggestions would be greatly appreciated.
Here is an EXACT example of what I have done and it does not work:
Create a new view controller in IB
Place a page label at the top of the page centered horizontally and aligned to the top of the layout guide.
Add a view with the following constraints on the page:
Now add two labels to the view with the following constraints:
Once this is done I get the following error and updating the frames will cause it to be give a height and width of 0
Here is what the page looks like in IB:
I need the height and width of the view to size automatically so that I have put a multiline label in the view and have all the contents of the view treated as a group and centered on the page.
Any suggestions would be greatly appreciated.
Here is a snapshot of a working set of all the constraints on an abbreviated layout that produces an always-centered view that automatically resizes with the child views (i.e., as the content of any label changes, the view grows or shrinks around it).
Important to your solution, the view has neither a height nor width constraint; its size is constrained entirely by its descendant constraints.
The key is that every component has a direct or indirect constraint from which its size and position are specified or can be inferred. For example, in order for the view to infer its width, the child labels must have a leading space constraint on the first label, a trailing space constraint on the last label, and a horizontal space constraint between interior labels. Those constraints plus the contents of the labels allow the width of the view to be inferred ... and force the view to dynamically conform to that width.
The same applies for the view's height. For example, you can specify the top and bottom space of just one label, or all of them. If just one, the remaining labels can be vertically aligned with it (see "Align Center Y to: Label1" in the screenshot).

Swift - UIScrollView Scrolls Partially

My problem is slightly different from other's 'Swift UIScrollView' problems when using auto layout:
Problem:
Unlike others, when I run my app, it scrolls. My problem is that the scroll cuts off the bottom 20-30% of the content. I can pull to see the buttons did build and are down there, but when I let it go the scroll snaps back to a false bottom which cuts off my content!!! I've been trying, for days, to fix it to scroll the entire height but it continues to cut off!!
Description of app:
I used auto layout to layout 6 buttons and labels. Each button a rectangular image, with a label directly beneath it. (sorry, the site won't let me post pictures!)
I have my views arranged like this:
MainView > ScrollView > ContentView > Buttons & Labels
I have my contentView pinned to my ScrollView and my ScrollView pinned to my MainView. My buttons and labels all have constraints that are building correctly, to create a list that looks like:
Rectangular button
Label beneath it
Spacing
Rectangular button
Label beneath it
Spacing
Etc.
Can anyone tell me why I can't scroll the full length of the view?
Your description of how your items are constrained is vague, so I'm going to list all of the constraints you need to make this work. Then you can compare what you have to what you need and adjust accordingly.
Your ScrollView should be pinned on all 4 sides to the MainView. (This isn't absolutely necessary. You can constrain your ScrollView however you want, but make sure it can grow as the device and/or orientation changes).
Your ContentView should be pinned on all 4 sides to the ScrollView with offsets of 0.
Since you want your ScrollView to scroll vertically only, constrain the width of the ContentView to the width of the ScrollView using an Equal Widths constraint. To do this, in the Document Outline view, Control-drag from your ContentView to your ScrollView and select Equal Widths from the pop up.
The height of ContentView will be set by the sum total height of everything in it. In order for this to work, your topmost button needs to be pinned to the top of the ContentView, all of your buttons and labels should be pinned to their nearest neighbors, and the bottommost label should be pinned to the bottom of the ContentView. In addition, all of your buttons and labels should have constraints for their widths and heights. I would suggest setting an explicit width constraint and explicit height constraint for your buttons and centering them horizontally in the ContentView. For your labels, set an explicit height constraint and pin the left and right edges to the ContentView.
If you have these constraints and no other ones, your ContentView will be properly sized.
Using contentView, like you said, usually fixes the issue. So chances are you need to take a second look at your contraints. Maybe try this solution in a clean/new project to see that it works. (it does work). My guess would be that some of your constraints conflict each other.
Otherwise I think it would be a good idea to setContentSize of your scrollView in your viewDidLoad.
Another hack would be to place 2 UI objects with their alphas set to zero on the right top corner and left bottom corner. This would hint scrollView to set its contentSize.

Resources