I have 4 UIViews in my UIViewController with colours: Yellow, Green, Grey and Blue.
I also gave the following auto layout constraints:
Yellow view: Top-8-superview, Leading-8-superview, Trailing-8-superview, height = 120
Green View: Top-8-YellowView, Leading-8-superview, width=200, height=100
Grey View:Top-8-GreenView, Leading-8-superview, Trailing-8-superview, bottom-8-blueView;
BlueView: Bottom-60-superview, trailing-8-superview, width=260, heigth=30
After compile and run, it looks like this:
At this point, no problem, no constraint complaints, everything is fine.
However, I changed the 4 UIViews' parent view to UIScrollView, and then the UI display wrongly: only green view displays correctly, yellow view and grey view are missing, blue view shows a small part.
In the console I checked that the scrollView's contentSize.width is 16, which is incorrect, so I tried to correct it in viewDidLayoutSubviews:
self.scrollView.contentSize = self.view.bonds.size;
[self.scrollView setNeedsDisplay];
[self.scrollView layoutIfNeeded];
The contentSize becomes (414, 736), which is correct, but the display is still same as my second attached image. I listed out the view details:
YellowView: frame:(8, 28, 0,120)
GreenView: frame:(8, 156, 200,120)
GreyView: frame:(8, 264, 0, 274)
BlueView: frame:(-252, 646, 260,30)
What I observe is, if I give a width constant, the view is at least has width greater than 0, and for the Yellow and Grey view, since they are blank view with no width constrains, they will have problem to show.
What is the best solution to fix it other than give width constraints? I am also wondering why the width of yellow and grey is zero, since I also gave top, leading, trailing, height constraints?
Add contentView as subview of scrollView and then all your subviews as subviews of contentView. Constraints for contentView set as width = superview.width, height >= superview.height and pin 4 sides of contentView to scrollView (leading, top, trailing, bottom). More info: https://www.natashatherobot.com/ios-autolayout-scrollview/
Related
Black - screen frame/size
Red - default UIScrollView's frame/size
Yellow - my button.
I want to always keep that button at the bottom. So e.g. on 3.5in screen, scrolling is available and button is at the bottom, no problem. Now, when we move to 5.5in screen, it becomes like in this image, button is not at the bottom anymore. What I am trying to do is change UIScrollView's height, but it does not work:
if(self.view.frame.height > contentView.frame.height)
{
print("fixing scroll view")
contentView.frame = CGRectMake(0, 0, self.view.frame.width, self.view.frame.height * 2)
scrollView.contentSize = CGSizeMake(self.view.frame.width, self.view.frame.height * 2)
}
self.view.frame.height * 2 is just for testing. What could be wrong?
Late response, but for those who need a button to stay at the bottom of the screen in a scrollview, if the content view should be the same as the screen height, try this:
Set ScrollView top, left, right and bottom constraints to 0 from parent
Set ContentView to Equal Width and Equal Height of ScrollView
Set ContentView top, left, right and bottom constraints to 0 from ScrollView
Give each element inside the scroll view some constraints, linking everything from top to bottom
Select one constraint between the elements and remove it, or make it low priority. I'd say to make it low priority because if you have to delete the Equal Height, the layout won't break.
With auto layout all you need to do is add pin constraints to your scrollView to each corner of the superview.
Set your 4 constraints to 0 and you'll be all set.
I'm having this weird issue with my constraints which causes the UILabel (Caption Label) to be a fixed height instead of dynamically changing height depending on the text.
I have a view (Vertical View) with a top constraint on the label above it. The Vertical View contains a view (called View) which I'm using as a divider that is centered from top to bottom with a width of 1. On the left of the divider is a UIImageView (Left Image View) with constraints leading, top, bottom equal to superview and trailing equal to View. I want to do the exact same thing to the UIImageView on the right of the divider but here is where my issue comes up.
If I use a fixed height as seen below, the UILabel above Vertical View dynamically changes its height like I want but this is obviously not how I want the UIImageView on the right to appear. I want it to be similar to the UIImageView on the left of the divider with equal height and width.
If I set the top constraint of the UIImageView on the right to the superview Vertical View, similar to the UIImageView on the left of the divider, the UILabel above Vertical View doesn't dynamically change height anymore. The UILabel now has a fixed height which I believe comes from the fact that UILabel has a height of >= 14.
How can I properly set the constraints so that I can have both UIImageViews next to each other with equal and height contained within the Vertical View and still have the UILabel above Vertical View dynamically change height depending on the text that I set the UILabel to?
On the RightImageView, you first need to get rid of the "Height = 50" constraint. This is what is causing it to be small.
Next, if that alone doesn't fix you, can you try setting the following constraints instead of using the superview for the constrains (instead make it mirror the LeftImageView):
Left: Leading spacing to divider view
Top: Align top edges to LeftImageView
Right: Horizontal space to superview (your vertical container view)
Bottom: Align bottom edges to LeftImageView
This should allow the views to remain the same height and width (assuming your distances between left/right edge of vertical container view are the same, and the distances between divider are the same).
Now, ensure the size constraint for width of the divider is set to 1 and not >= 1. Also, ensure the vertical container view has a Compression lower than the Label.
One final note--your screenshot shows the result that IB is showing you (with the dotted yellow box) on the LeftImageView. One you update your constraints correctly, this yellow box should go away.
Regarding the UILabel - if you want this to grow dynamically, you need to do the following:
myUILabel.numberOfLines = 0;
myUILabel.text = #"Enter large amount of text here";
[myUILabel sizeToFit];
I have subclassed UITableViewCell (with a xib), and I have noticed that when I use autolayout/constraints, the labels that I place in Interface Builder do not show correctly at runtime.
First, I put three labels aligned left, center, and right with respect to the cell's content view.
When I run my app, The left-most label is on the left, the center label is on the right, and the right-most label is nowhere to be seen.
I logged the frame of the cell's content view at my -tableView:cellForRowAtIndexPath: (inside the UIViewController subclass that hosts the table view), and I get this value:
(0.0, 0.0, 600.0, 43.5)
The width is reported to be 600, but I assume this is the generic, 'any device' storyboard size (even though, weirdly, the cell comes from a separated xib with only the cell view, not a storyboard or the xib of a view controller), and it will be sized accordingly by the table view after being passed to it (AS a side note, notice the height being 43.5, not 44!).
I have tried several things, and found out the following:
If I set constraints on my label against the leading (left), top and bottom edges of the cell, it displays appropriately.
If I place the label at the right-most edge of the cell in Interface Builder, without any constraints, it shows where I placed it (but of course, on rotation to landscape, it will stay put a fixed distance from the left edge instead of sticking to the right edge).
If I place the label anywhere, and add a constraint to the trailing (right) edge of the container, it is displayed or not depending on how much distance the constraint specifies. That is, the constraint seems to be stuck to the cell's original right edge of +600. If I place it near the left-most edge, but drag the constrain towards the trailing edge, it will appear somewhere on the right.
I tried logging the label's frame from within the cell's code, like this:
override func layoutSubviews()
{
super.layoutSubviews()
// (...label should be at the right position now?)
println("Label frame: \(rightLabel.frame)")
}
On Interface Builder, the label is 41 x 21 and is positioned at (271, 11).
The log above is called twice for one row, and gives:
Label frame: (271.0, 11.0, 41.0, 21.0)
Label frame: (544.0, 11.0, 41.0, 21.0)
If, instead, I remove the trailing constraint and replace it by a leading constraint to the left edge of the content view, and leave the label on the same position in Interface Builder, the above logs give:
Label frame: (271.0, 11.0, 41.0, 21.0)
Label frame: (278.0, 11.0, 41.0, 21.0)
So, What is going on with trailing constraints on table view cells? How do I align a label to the right edge??
EDIT: I added the following line to the cell's layoutSubviews() method:
println("Own frame: \(self.frame)")
And I get this output:
Label frame: (271.0, 11.0, 41.0, 21.0)
Own frame: (0.0, 0.0, 600.0, 44.0)
Label frame: (544.0, 11.0, 41.0, 21.0)
Own frame: (0.0, 0.0, 600.0, 44.0)
So, the cell stays at 600 points wide, even after being put into the table view!
EDIT 2:
So, I found the problem and obviously it was somewhere else: The table itself is stuck at 600 points wide, because I did not constrain it to the view controller's view edges in Interface Builder.
Silly me!
Once I pinned the table view's four edges to those of its parent view, the problem corrected itself.
First time using table views in Interface Builder...
First of all remove all constraint from that three label then follow this step and add constraint one by one as mentioned below.
For Left Label select your label go to pin menu the add this 5 constraint for it.:
For center label add this two constraint so it will always appear at center:
For right label add this 5 constraint :
And the result will be for all screen:
HERE is the sample project.
Apply this constraint to your label
Constraint for left label
1. Leading to superview
2. Top to superview
3. Fix hight and width
Constraint for center label
1. horizontal to superview
2. Top to superview
3. Fix hight and width
Constraint for right label
1. Trailing to superview
2. Top to superview
3. Fix hight and width
I have a Controller with 4UILabels that are constant height throught all the iPhones, below the lastUILabel there is aUIView with a page controller(with aTableView inside that page controller).
What I want is that theUIView take all the height that he cant :
example: Screen of 600 height
4UILABELS = 200 height
TableView = it should get 400 height
Screen of 800 height
4UILABELS = 200 height
TableView = it should get 600 height
I need 1 constraint more to set the height of the view,What I have defined is :
EqualWidth constraints to superView
LeadingSpace to SuperView
Top Space to super VIew
The following constraints should align everything for you so that the UILabels stack vertically, each have height 50, and stretch to the edges. The UIView will take up the remaining space regardless of the screen size.
All UILabels
Pin leading edge to superview
Pin trailing edge to superview
Add height constraint set to 50.0
UILabel 1:
Pin top edge to superview top
UILabel 2:
Pin top edge to UILabel 1 bottom
UILabel 3:
Pin top edge to UILabel 2 bottom
UILabel 4:
Pin top edge to UILabel 3 bottom
UIView
Pin leading edge to superview
Pin trailing edge to superview
Pin top edge to UILabel 4 bottom
Pin bottom edge to superview
UITableView
Pin leading, trailing, top and bottom to its container view so it fills the area.
I assume that you are creating this UIViewController in a Storyboard.
You should be able to put all the labels in a container view which you give the constant height of 200 points, as you specified. Make a vertical constraint from "Top layout Guide" to said container view, maybe with the value 0. Drag an UITableView and place it below the container view. Dont give it any constraint regarding height. Make a vertical spacing between the (bottom of) container view and the (top of the) UITableView with value 0 and a vertical spacing between the (bottom of the) UITableView and the Bottom Layout Guide with value 0.
So it will be (V:0 means vertical spacing, constant 0):
Top Layout Guide
V:0
Container View - Height: 200
V:0
TableView (dynamic height)
V:0
Bottom Layout Guide
This should work.
(And then of course you need to create constraints regarding width and also internal constraints for each UILabel inside the container view. Let me know if you need help with that.)
What does this expression "minimum x-value of the superview’s bounds" mean in the Apple Documentation regarding Autolayout in a UIScrollView?
...some notes regarding Auto Layout support for UIScrollView:
In general, Auto Layout considers the top, left, bottom, and right edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of its bounds. To make this work with Auto Layout, the top, left,
bottom, and right edges within a scroll view now mean the edges of its
content view.
1) As I know by default bounds of a view is (0, 0, width, height). So x is 0.
2) How could a view have more x values to take minimum on it?
The Autolayout constraints for any view is generally in relation with the visible bounds of the view but when it comes to a UIScrollView, the Autolayout constraints are in relation with the scrollView's contentView.
Obviously, because a scrollView is, well... supposed to scroll.
Suppose a scrollView has a contentSize of something like:
myScrollView.frame = CGRectMake(0, 0, 100, 100);
myScrollView.contentSize = CGSizeMake(1000, 1000);
now...
Say, in this scrollView you have a subView, something like:
UILabel *lblTest = [UILabel alloc] initWithFrame:CGRectMake(0, 0, 50, 50)];
[lblTest setText:#"Where am I?"];
[myScrollView addSubView:lblTest];
Before Scrolling:
myScrollView.contentOffset.y will be 0
myScrollView.bounds.y will be 0
myScrollView.frame.origin.y will be 0
lblTest.frame.origin.y will be 0 (and will be visible)
After Scrolling (to bottom):
myScrollView.contentOffset.y will change to 900
myScrollView.bounds.y will also be 900
myScrollView.frame.origin.y will still be 0
lblTest.frame.origin.y will be -900 (and no longer visible)
This is what the Apple Documentation means by:
The UIScrollView class scrolls its content by changing the origin of
its bounds.
Now...
If Autolayout was in relation with the visible bounds of UIScrollView then no matter how much you scrolled, the UILabel would not scroll up.
but...
Since Autolayout in a UIScrollView goes in relation with the scrollView's contentSize, the Autolayout contraints work within it with relation to the scrolling functionality.
So...
When you use Autolayout for subViews in a scrollView, it will be relational to the scrollView's actual size and not the visible size.
apple docs says ask: The UIScrollView class scrolls its content by changing the origin of its bounds.
1) so scrolling can change bounds.x, and than x will be not 0
bounds property is usually (0, 0, width, height), but not always
2) the minimum means: the top, left, bottom, and right edges within a scroll view now mean the edges of its content view.