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
Related
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/
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];
really new to the interface builder. I have 5 buttons evenly spaced. Setup in the iPhone5 screen. I want the spacing between the buttons and the boundaries to remain the same but the buttons to grow in size with the screen (keeping the same aspect ratio).
How would I setup the constraints for this ? Do I need to put invisible spacers between the buttons or something to do this?
Do like this:
Put all buttons inside UIView
Set constraint to UIView as leading, top ,Trailing, height and width.
Set constraints to all the buttons inside UIView as top,leading,width and height.
Then select first button and UIView together, and make them equal widths and set the multiplier of this constraint as per your number of buttons need, at this point set priority of UIView's width constraint to 900 (this is important). Same with button's width constraint.
Now select all buttons and set them as equal width and set the multiplier of this constraint as per your button's width.
Delete width constraint of all the buttons except first one.
View heirarchy:
Final Output:
Solutions:
I have started implementing constraints from the button in the center i.e. Button4
1) Constraints on Holder View
HolderView.centerY = superview.centery
HolderView trailing/leading constraints to superview
2) Constraints on Button4
Button4.width = Button4.height
Button4.width = HolderView.superview.width * (1/7) - 10
(yes it is not a typo Button4.width is relative to screen width not its holder view's width)
Button4.trailing to Spacer, Button4.leading to Spacer
Button4 bottom/top constraints to superview
Make all other buttons equal to Button4 (button in the center)
3) If you need to have the same arrangement for any number of button you just have to play with width constraint of the button in the center
Sample Project :
Link to Sample Project
Note: This solutions will work on only odd number of buttons
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.)
In portrait mode, I have a tableview on the left-side, and a webview on the right-side. They both take half of the screen. I have autolayout enabled.
But when I rotate the screen to landscape mode, the tableview's width takes up less than half the screen's size, and the webview ends up taking more, instead of them being an even split.
Why does this happen? How can I have the views take up only half the screen in regard to their widths, regardless of screen orientation?
The first half of the answer address the case in which we want to split the view evenly between view A (blue) and view B (red). The second half will address the case in which we want to have view A take up half the screen, but view B does not exist.
Step 1:
Set up blue's auto-layout constraints as pictured. Top, left, bottom of 0 to the superview. Right of 0 to the red view.
Step 2:
Set up the same, but mirrored, constraints for the red view.
If you've completed the first two steps correctly, you should have some auto-layout errors and warnings:
We need one more constraint to fix these errors/warnings and get what we need.
Step 3:
Hold control, click and drag from one view to the other and select "equal widths". Now our views will always maintain the same width. All of our auto layout warnings and errors disappear, and our views will always be half the screen no matter the orientation or device.
To add these constraints in code using VFL, we need the following constraints:
#"H:|[blueView(==redView)][redView]|"
#"V:|[blueView]|"
#"V:|[redView]|"
Now, suppose the case where we want a single view to take up half the screen, but we don't have a view for the other half. We can still do this with auto layout, but it's a slightly different set up. In this example, our view is blue, and its parent view is green.
Step 1:
This is similar to step 1 above, except we don't add a right side constraint (this will obviously vary if we want our view to take up a different half).
Step 2:
Like before, we want to assign an "equal widths" constraint. In this case, from our view to the parent view. Again, hold control and click drag from one to the other.
At this point, we have an auto layout warning. Auto layout wants our frame to match its parent's width. Clicking the warning and choosing "Update constraints" will put a hardcoded value in. We don't want this.
Step 3:
Select the view and go to its size inspector. Here, we'll be able to edit the constraints.
Click "Edit" next to the "Equal Width to:" constraint. We need to change the multiplier value.
We need to change the multiplier value to 2.
The constraint now changes to a "Proportional Width to:", and all of our auto layout warnings and errors disappear. Now our view will always take up exactly half of the super view.
To add these constraints in code, we can add some using VFL:
#"H:|[blueView]"
#"V:|[blueView]|"
But the proportional width constraint can't be added with VFL. We must add it as such:
NSLayoutConstraint *constraint =
[NSLayoutConstraint constraintWithItem:blueView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:superView
attribute:NSLayoutAttributeWidth
multiplier:2.0
constant:0.0];
Create a UIView in XIB or Storyboard
Set top and left constraints
Set a height constraint
Set width constraint to equal width of the super view *Important
Edit the width of the constraint by converting it to decimal
Set the decimal to the width you'd like (In my case 0.5 or half)
Bind both of them to the superview edges (table view to the leading edge, web view to the trailing edge) and set constraint for them to have same width.
Programatically in Swift 4:
import UIKit
class ViewController: UIViewController {
let halfScreenViewLeft: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
let halfScreenViewRight: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(halfScreenViewLeft)
view.addSubview(halfScreenViewRight)
//Enable Autolayout
halfScreenViewLeft.translatesAutoresizingMaskIntoConstraints = false
//Place the top side of the view to the top of the screen
halfScreenViewLeft.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
//Place the left side of the view to the left of the screen.
halfScreenViewLeft.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
//Set the width of the view. The multiplier indicates that it should be half of the screen.
halfScreenViewLeft.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5).isActive = true
//Set the same height as the view´s height
halfScreenViewLeft.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
//We do the same for the right view
//Enable Autolayout
halfScreenViewRight.translatesAutoresizingMaskIntoConstraints = false
//Place the top side of the view to the top of the screen
halfScreenViewRight.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
//The left position of the right view depends on the position of the right side of the left view
halfScreenViewRight.leadingAnchor.constraint(equalTo: halfScreenViewLeft.trailingAnchor).isActive = true
//Place the right side of the view to the right of the screen.
halfScreenViewRight.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
//Set the same height as the view´s height
halfScreenViewRight.heightAnchor.constraint(equalTo: view.heightAnchor).isActive = true
}
}
The constraint system can't read your mind. You must say what you want. Half is half. If you want half, say half. To make a view half the width of its superview, give it a width constraint that has a multiplier of .5 in relation to its superview's width.