View inside UIStackView is not resizing with AutoLayout - ios

I have this following screen:
All of the views are inside an UIStackView. The yellow view would be circular on runtime. My issue when I run an a smaller iPhone (5S), the light blue view which contains the yellow view, will not get smaller, so the red and blue view from the bottom will shrink. I want these two and the green view to have fixed width and the light blue view to adapt height.
But for some reason it doesn't. I am sure is because of the constraints I set to the yellow view. But I can't figure out which one is the problem.
Here are the yellow view constraints:
The constraints for the other views are:
Green view: height = 64
Red and blue view: height = 50
Much appreciate if someone could take some time to look over this constraints and help me understand how to make the light blue (and yellow) to change height when screen changes.

Did you try lowering the Content Compression Resistance Priority (vertical) for the light blue and yellow views to be less than 750, and the Content Compression Resistance Priority (vertical) for the green, red and blue views to be higher than 750 (maybe even 1000 / required if that's the case?)
UPDATE
I took a look at your project. Actually, the issue doesn't seem to be with your stack views, constraints, or the DailyStatusViewController at all. Instead, the issue looks more related to the MainViewController which is embedding the Daily Status view inside a scroll view. The scroll view is both clipping to bounds, and is placed behind another container view. If you turn off clip to bounds on the scroll view, and move the container view below it to be behind it, you will see your stack views laid out correctly, but were just being clipped.
As to why they were clipped, keep in mind that your out stack view has a required height constraint of 300, while the scroll view it's being embedded in has a height constraint to 45% of the screen height. So on smaller screens, there is less than 300 points available to display the Daily Status view, and so it is being clipped. You probably want to remove the fixed height constraint from your outer stack view and instead constrain it to the edges of the root view.

Related

Autolayout with specific constraints

I have problem or maybe wrong understanding of constraints in general with view controller form photo below.
I already implemented scroll view and it is working properly. Photo below showing Content View with two subviews (with the same name: View). What I would like to achieve is to set first View (the blue one) to be exactly on edges of the screen and second one (green view) to be visible only when user scroll down on this page. Is it possible to achieve these just using autolayuots or I need to do some hard code work. What is the best approaches for solving this taking in account that I need this app to work with all king of iPhones.
At the bottom of the green view is located tab bar but it is not visible on this photo.
This is pretty straight-forward...
Pin your scroll view to Zero on all four sides.
set Blue Top, Leading and Trailing all to Zero to superview (the scroll view)
set Blue width equal to scroll view width
set Blue height equal to scroll view height
set Green width and height each equal to Blue width and height
set Green "center horizontally" to Blue
set Green Top to Zero from Blue bottom (vertical spacing)
set Green Bottom to Zero to superview (the scroll view)
That should do it :)
Edit: whoops, corrected step 4 (Blue height should be equal to scroll view height, not multiplied by 0.5)

Setting the content size of a view inside UIScrollView

I have to lay out a few views in a View Controller which should look like this when run.
Initially the white view should be partially covering the blue view at the bottom like shown in the image. The blue view should stay put while the white view at the bottom can be scrolled over the blue view.
I added the blue view to the main UIView of the view controller. Then added a UIScrollView on top of blue view and added the white view on to the scroll view.
Now I'm getting the dreaded ambigeous content size error. I have pinned the scrollview on all four sides. Then added leading, bottom and trailing constraints to the white view. Then I tried adding a top constraint to the white view but the error still persisted. I also adding a height constraint to the white view to no avail.
It might be dofficult to imagine my setup so I added a demo project here as well.
I set the constraints for you here.
Explanation:
You are getting ambigous content size because your scrollView does not know its content (white view's) width and height.
I added top,width and height constraint to white view. This way your scrollView knows its content width and will scroll only vertically. As for content height - u can change heightContstrait's constant in code, or completely remove height constraint if you use autolayout properly for white view's subviews.
Put the blue view under scrollview. And set scrollviews content inset, to make your white view appear not on top, but with some space. Now on start you get white view a bit over blue, and on scroll it covers blue view. About size errors, scrollview needs to know it’s content size initially. I assume your white view is table view, so set it’s bottom constraint to zero. If you want blue size to change it’s size on scrolling, like it’s avatar or something, it’s a bit different. You should take scrolling events on uiscrolldelegate, and change blue views height accordingly
Make sure you've read (and understand) Apple TN2154: "UIScrollView And Autolayout", which explains the steps required.
Looks like you're not specifying the content size properly.

Auto Layout for complex view in Table View Cell

I know this might not be the best question but I really need to a starting point:
If I wanted to create a layout as the one below (using auto layout):
What I tried was:
Put the Three zeros in a horizontal stack view
Then put the three labels (posts, followers, following) in a horizontal stack view.
Then put those two horizontal stack views above into a vertical stack view.
Then I added that gray button below the (posts, followers, following) labels into the vertical stack view created right above. So in the end I had one vertical stack view consisting of two horizontal stack views and the gray button.
Finally I added the image to the left in the vertical stack view created in the above step which made a final horizontal stack view. This step ruined my layout completely. Made the image like 500 width which made the stack view go off the screen. So I set layout constraints on the very last horizontal stack containing the image, gray button, and two horizontal stack view to Bottom Space: 140, Top space 10, Leading Space 20, Trailing space 20. (I didn't touch the stack views inside this final stack view. I only put layouts on the main horizontal stack view created in this step).
After 4bar suggestions:
Please note I have not touched the first name, facebook.com, or bio label at the bottom.
If you have not set the distribution mode for your outermost horizontal stack view, it will default to UIStackViewDistributionFill, in which case the arranged subviews will stretch to fill the whole width according to their hugging priority. Increase the content hugging priority on the image view (say, to 251) to prevent it from stretching. Also, if the image size is larger than the space you'd like to devote to it, you may also need to set additional width or height constraints on the image to override its intrinsic size.
The main thing to understand is that UIStackView's size is driven by the intrinsic sizes of its arranged subviews, plus spacing. In Fill mode, if too much space is available, content hugging priorities are used to determine which view to stretch; if too little space is available, compression resistance priorities are used to determine which view to compress.
As shown below, You need to set the width of the stackView containing the image.
If you want the image to look like a square, you have to pin the width and height of the image to the same value. Then set Mode to AspectFit so that image won't look stretched.
Pin the width of the labels like 'posts'. So that they will show the entire text.

Using AutoLayout and ensuring bounds of parent view to fit subviews when resized

I really wish I could get my head around auto layout. It seems that whenever I read an abstract description of how things are supposed to work it makes sense, but whenever I actually try and put it into practise it always causes massive headaches. So, apologies if there is already an answer out there for this but I couldn't find one.
The problem should be relatively simple. I have a container view, which contains two subviews, shown here in hideous colours for maximum readability :) :
The bottom (black) view, should remain at it's current size and maintain the spacing between it and the red view, and the spacing between itself and the bottom of yellow view.
The red view I want to be able to dynamically change its height, causing the black view to shift up/down accordingly whilst the yellow view resizes to fit both the red+black views.
For the black view, I've added constraints to:
Set the height to 94
Pin the leading and trailing space to superview
Set the top space to the red view at 51.
Set the bottom space to the yellow view at 20.
I am trying to understand what seemingly-mystical set of constraints I need to add in order that, when the red view is resized vertically, the black view stays its current distance from the red view and maintains its size, and the outer container view resizes accordingly so that it contains the red view + black view + vertical spacing between the views.
For the red view, I've added constraints to pin the top, left and right spacing to superview, but have had no luck working out the vertical constraints. Currently I've got a constraint pinning the height =114 with a priority of 999 and a constraint with height >=114 with a priority of 1000 thinking this would ensure the view is always at least 114 in height...
The fun starts when I try and manually set the height of the red view.... I've added a button on the view, and when the button is pressed, I manually set the bounds of the red view. (The red view's default height is 114):
CGRect bounds = self.redView.bounds;
bounds.size.height = 300;
self.redView.bounds = bounds;
When I run this and press the button, the view goes from this:
To this:
To me this makes no sense whatsoever. Why does this result in:
The Y origin of the red view changing? Particularly when there is a "required" constraint telling it to stay 20pts from the top of yellow view.
The spacing between the red and black views breaking down, even though the constraint on the spacing between them is "required"?
The vertical size of yellow view not changing. Again, despite the spacing between red+black, and me having tried just about every combination I can think of in terms of compression resistance and content hugging priority.....
I really want to understand this, so would be really grateful if someone can explain what additional constraints / changes to constants are required, but more importantly WHY they are required, because to me it doesn't seem clear at all how the layout system comes up with its answers....
Any clarification much appreciated.
Thanks in advance
(All code above is running on iOS 7 and built with Xcode 5.0.2).
You don't need any fancy constraints to do what you want here -- no inequalities or messing with the priorities. In addition to the constraints to the sides, the red view should have 20 to top, 51 to black view, and a height constraint of 114. The black view, has a 20 to the bottom and a height of 94. The superview (yellow) should have zero constraints to top, left and right -- no height. You should have an IBOutlet to the red view's height constraint. When you want to change its height, modify its constraint (don't set frames):
- (IBAction)resizeYellowView:(id)sender {
self.heightCon.constant = 300;
}
Everything is linked together from the top of the yellow view to the bottom with fixed values, so the only thing that can change when the height of the red view changes, is the height of the yellow view.

Views are Horizontally and Vertically Ambiguous with complex layout

I have a UIViewController on my storyboard that has 2 subviews side-to-side horizontally. I added constraints to fix the leading and trailing edges to a constant (20 pts), and another constraint to keep the widths equal. If I assume the following, it should be possible to calculate what the width of each subview will need to be:
the subviews do not overlap
there are no other views present (horizontally, at least)
the width of the screen (the superview) is known
However, XCode gives me a warning that my views are horizontally ambiguous. I'm guessing that means that XCode is not making one of these assumptions, but which one is it? And is there a way for me to instruct XCode to make that assumption?
EDIT: Okay, played with it a bit and got the warning to go away, but it looks like it's not making the first assumption - it's just setting each subview's width to superview.width - 40, and happily burying one view underneath the other. So the question is how to I stop them from overlapping?
EDIT 2: Okay, my actual screen is a lot more complicated than my simple example. Here's what I got:
So in this setup I have 4 views that are vertically and horizontally staggered.  I want the blue, red, and purple views to all be the same subview.frame.size.width = superview.width - 60. The blue and purple are lined up in the left column, and the red is alone in the right column, and all the gaps (between the two columns and between each column and it's nearest edge) are at a constant (20 pts). These 3 tables have a variable height, which I will be setting programmatically as described in James's answer here. At the bottom is a pink view that stretches the width of the screen (minus gaps), and sits at a constant 20 pts below either the purple or the red view, whichever is lower (which I'm attempting to do by giving it a spacing constraint of >= 20 to each view, and I hope that it will pick exactly 20 for one of them). Since all of the heights are dynamic and may not necessarily fit on the screen at the same time, I made their superview a UIScrollView instead of the normal UIView.
When all is said and done, I'm still getting a warning that all 4 of my views are horizontally ambiguous, and that the pink bar is vertically ambiguous. I think it's having trouble realizing what is supposed to go next to what, which is why it thinks it's horizontally ambiguous. And I think it's not picking to place the pink bar exactly 20 pts below either the purple or red views, which is why it thinks it's vertically ambiguous. Can anyone confirm or deny any of these suspicions? Or suggest a way around it? When I run it in the end, I just get this (I made the background of the scroll view yellow, which you can't tell in the storyboard screenshot):
Vertically Ambiguous
Okay, I think I've solved the vertical ambiguous part. I added two vertical constraints between the pink and purple views and two vertical constraints between the pink and red views. For each pair, the first constraint is that the spacing between them must be > 20 pts, and it has 1000 priority. The second constraint is that the spacing is = 20 pts, but it only has an 800 priority.
For example, if the bottom of the purple view ends up being lower than the bottom of the red view (as it is in my first screenshot), Xcode should try to set the vertical distance between the pink and red views = 20, but it will realize that that conflicts with condition that the space between the purple and pink being >= 20. Since the >= constraint has higher priority, the = constraint will be ignored. Now, when Xcode looks at the constraint that the spacing between the purple and pink views being = 20, it checks that against the constraint that the pink and red must be separated by at least 20. Since the bottom of the red view is higher than the bottom of the purple view, the >= 20 constraint between the red and the pink still passes.
So TL;DR, you can set up a view to have a spacing at a given value (x) from the most extreme of multiple views by giving it a >= x constraint with 1000 priority and giving it a = x constraint with <1000 priority for each view you are considering - and my vertical ambiguity problem has been solved. I do not yet have a solution for the horizontal ambiguity for all 4 of the views.
Horizontally Ambiguous
Okay, I got the horizontally ambiguous part fixed now as well. What it boils down to is that constraints in scroll views (and therefore table views) work differently than they do for any other kind of view. Here's what the step-by-step looks like.
Place the UIScrollView
Place a UIView into the UIScrollView to serve as a "contentView" for that scroll view
Add constraints to pin the contentView to all 4 corners of the scroll view AND pin it's width and height (so 6 constraints between the contentView and it's superview - 2 more than usual). Note that the width and the height can be pinned to something much larger than the normal screen size, which is probably why you are using a scroll view to begin with.
Add all of your other views you want in the UIScrollView (UIButtons, UILabels, etc. - I'm just going to assume UILabel from here on so I don't have to type as much, but any kind of UIView subclass will work) as subviews of the contentView, NOT directly as subviews of the UIScrollView
With this setup, the UILabels that are given constraints to their superview will constrain to the contentView, which has a defined size, so nothing is ambiguous.
Alternatively, if you want to fix the sizes of your UILabels (or dynamically calculate them, depending on the functionality of your app) and let the contentView expand to hold them:
Place the UIScrollView
Place a UIView into the UIScrollView to serve as a "contentView" for that scroll view
Add constraints to pin the contentView to all 4 corners of the scroll view AND pin it's width and height
create an outlet for the width constraints on the contentView (let's say we name it contentViewWidthConstraint)
place the UILabels
fix the sizes of the UILabels
create an outlet for the width constraints on the UILabels
Then in the code for viewWillLayoutSubviews
add up the widths of all of the UILabels and any gaps you want between them (as a CGFloat, which I'll call totalWidth)
set contentViewWidthConstraint.constant = totalWidth
And you're good to go! Note that I assumed you were setting the width in most of this example, but it should be just as applicable to height.
The problem is that many different widths of the two views will satisfy the constraints that you've set up. Here are two examples (I drew the shapes stacked vertically to make it easier to see the overlap example):
You can add a horizontal space constraint with a value of 0.

Resources