I'm using a static Table View Controller and defining the UI in Interface Builder.
I want all of the UILabels to be the same width so that the the left side of the UITextFields line up.
If I force a width, then they often overdraw each other.
I was thinking of modifying the UILabel width programmatically, based on the width of "Safe Message" within the controller, but I couldn't get it to re-layout.
let width = labelSafeMessage.frame.size.width;
labelName.frame.size.width = labelSafeMessage.frame.size.width;
labelName.sizeToFit();
labelMessage.frame.size.width = labelSafeMessage.frame.size.width;
labelMessage.sizeToFit();
You can do this using only Interface Build and you do not need to force labels to have the same size.The only thing that you need to do is set leading spacing of UITextFields to superview and they will be aligned to left side.
you could avoid re-layout by calculating the width before the tableView displays data.
get the largest string in the set, and then use this
var str = "Hello, playground"
let constraintRect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: DEFINED_HEIGHT)
let boundingBox = (str as? NSString)?.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: FONT], context: nil)
boundingBox.width //width for the labels
Just replace the DEFINED_HEIGHT with the height for the label which would be the height of the row minus the top and bottom constraints that hold it.
and the FONT, for the font used in the label.
Instead of using a UITableView you could use a vertical UIStackView who will contains UIViews.
Set your UILabels constraints to Align Leading edges and Align Trailing edges and set the Content Hugging Priority and Content Compression Resistance Priority as well as the label don't be cropped and fit correctly.
Set the UITextFields constraints to Align Leading edges and Align Trailing edges to each other and that will do the trick.
Related
I have a UITableView filled with one type of custom UITableViewCell. Each cell contains a title (UILabel), subtitle (UILabel), and thumbnail (UIImageView), as shown below.
The widths of all three subviews are fixed, but only the subtitle label has a fixed height. The title label's height is dynamic according to the amount of text. I have set the title label's numberOfLines to 0 to reflect this.
My objective: The height of the cell should be determined by the height of the title label. In other words, the cell height should be the sum of the two labels' heights plus the three vertical padding gaps. The image height should be set to the resulting height of the cell.
My current implementation: I have set the following autolayout constraints:
Thumbnail is pegged to superview on top, left and bottom
Thumbnails width is proportional to superview width by a multiple of 0.2
Thumbnail is pegged on its right side to title and subtitle (trailing space = 8)
Title is pegged to superview on top and right (trailing space = 8)
Title is pegged on its bottom to subtitle (trailing space = 8)
Title height is greater than or equal to 24
Subtitle is pegged to superview on right and bottom (trailing space = 8)
Subtitle height is equal to 20
Additionally:
Title numberOfLines = 0
Thumbnail contentMode = Aspect Fill
Thumbnail clipToBounds = true
The problem: The image height does not respect the height of the labels, so the height of the cell is set to the full height of the image and the title label is stretched vertically. This is understandable given the constraints I have used, but is not what I want to happen.
My question: How do I constrain the UIImageView's height to respect the cell height (determined by dynamic label height), instead of having the cell height respect the UIImageView height?
My Environment:
Xcode 10
iOS 11 & 12
Swift 4.2
Try setting thumbnail vertical compression resistance lower than title vertical hugging priority. In code it'll look like this:
thumbnail.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
titleLabel.setContentHuggingPriority(.required, for: .vertical)
I'll explain the vertical constraints to make this possible.
Pin the top of the label to the contentView with enough padding so that image view stays inside it.
Pin the top of the subtitleLabel to the bottom of the titleLabel.
Pin the bottom of the subtitle similarly to the bottom of the contentView, so that the image view stays inside it.
Your cell now has a height based on the above constraints. Now you just have to make sure you place the image view inside this height.
Pin the top of the thumbnail to the top of the label, bottom to the bottom of the subtitle label.
Example:
titleLabel.top -> contentView.top + 10
subtitleLabel.top -> titleLabel.bottom + 5
subtitleLabel.bottom -> contentView.bottom - 10
imageView.top -> titleLabel.top - 5
imageView.bottom -> subtitleLabel.bottom + 5
I have a UICollectionViewCell prototype which should look like this:
On the left side, there is a UIImageView which I centered vertically in and pinned it to the start of the UICollectionViewCell.
Next comes a title UILabel which I pinned to the top and end of the UICollectionViewCell. I also pinned it to the end of the UIImageView.
Below comes a detail UILabel which I pinned to the bottom of the title, the end of the UICollectionViewCell and constraint it to have the same width as the title.
Below that comes a description UILabel which I pinned to the bottom of the detail, the end of the UICollectionViewCell and the bottom of the UICollectionViewCell. It also has the same width as the title.
What I want to achieve
The title and description labels have 0 number of lines (multiline) because I don't know how many lines the labels will have at runtime.
Via my custom UICollectionViewLayout I provide the target width for the cell. Now I need to figure out, how height the cell must be for the given width.
I have a non visible UICollectionViewCell which I think I can use to calculate the sizes by setting the model from my datasource. But I have a problem.
The Problem
After I set the text of the labels in my "size calculation cell", what do I do with the width and what else do I have to do so I can let the auto layout system calculate a proper height for the cell which I can then return to my UICollectionViewLayout
Or in other words: how can I tell the cell to use the new width to calculate its height.
Add this extension to the String:
extension String {
func height(withConstrainedWidth width: CGFloat, font: UIFont) -> CGFloat {
let constraintRect = CGSize(width: width, height: .greatestFiniteMagnitude)
let boundingBox = self.boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)
return boundingBox.height
}
}
And then do this:
let heightOfLabel = self.string.height(withConstrainedWidth: labelWidth, font: UIFont(name: "HelveticaNeue", size: 15)!)
You will get the height of the label with that font and that width. After that you can sum up the label heights and give it to the cell.
Okay it seems that I misunderstood how to use sizeThatFits. I now went with a different approach, though. This is what I did to get my self-sizing cells:
I put the three labels in a vertical UIStackView
I put the UIImageView and the UIStackView in a horizontal UIStackView
I pinned to parent stack view to left, right, top and bottom of the cell where the bottom constraint is >= 0 instead of just = 0
I use a protocol in my UICollectionViewLayout to ask for the height of a given width
In my UICollectionViewController, I implement the protocol and where I register my cells, I also instantiate one of the cells which I use for measuring in the protocol function
In the protocol, I first get the model object, then I hand down the model to my measuring cell which I keep in an instance variable in my view controller
I then call getHeight(forWidth) on my cell. In this function, I set the cells frame.size.height to a very large number. Then I use AutoLayout to calculate the actual height (setNeedsDisplay(), layoutIfNeeded())
The height I need is the height of the parent stack view plus the margings
I hope this helps somebody like me in the future. I also wrote a small blog post with some code samples of what I'm doing.
I have a UILabel that I have layed out in a storyboard centred in the view. It has some initial text "tap to login".
I am changing the value of the label to be the user's name once they have logged in. Once I do this, the label is no longer centered as the UILabel has not changed size.
How can I do this with autolayout in interface builder?
see this scrrenshot
1 first select your label width constraint
2 set the relation Greater than or equal
3 set the default constant value here i set it 10
when you change the label text it change the size of label according to its text. hope it's help :)
Iam guessing the label is not getting height and width, consider giving those constraints and then when your text changes use this method to determine the height and width:
func labelSizeWithString(text: String, maxWidth : CGFloat,numberOfLines : Int) -> CGRect{
let label = UILabel(frame: CGRectMake(0, 0, maxWidth, CGFloat.max))
label.numberOfLines = numberOfLines
label.text = text
label.sizeToFit()
return label.frame
}
You can set the UILabel frame.width bigger, maybe the same width as the screen.
Then you set the
label.textAlignment = NSTextAlignmentCenter
1.showing initial text to label
2.After pressing button changing text, that showing in middle of screen 3.Look at constraints third image, two constraints given 1.top to view 2.horizontally middle in container
Another thing if u given fixed width for the label,change relation as more than equal.[Click width constraint ,see right side top there change the relation
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'm working on a detail view for my app that lets people discover movies. I set up my detail view in interface builder by first adding a contentView to which I added my other views. I understand from previous topics that the contentView should dictate the contentSize of the scrollView. I also understand that the views inside the contentView should have auto layout constraints that enable the contentView to calculate its full size (in my case the height). I haven't been able to get this to work perfectly. I suspect that this is because I have a textview that displays the movie description and because some of these are long and some are short I don't know how to set its size. I want the textView to display the full text and I want to disable scrolling. In the current situation when the text is long the labels or the buttons get compressed. When I add height constraints to the labels and other views the textview gets compressed. Could anyone guide me in the correct direction/ let me know what I'm doing wrong?
This is how I set up the view hierarchy:
UIScrollView
UIView (trailing, leading, top & bottom = 0, centerY & centerX)
UIImageView (trailing, leading, top to contentView = 0, aspectRatio)
UILabel (centerX, top to ImageView & bottom to label = standard)
UILabel (centerX, top to label & bottom to label = standard)
UILabel (centerX, top to label & bottom to label = standard)
UILabel (centerX, top to label & bottom to textView = standard)
UITextView (leading, trailing & bottom horizontalScroller = standard)
HorizontalImageScroller (leading, trailing & bottom to UIButtons = standard , Height of contentView == height of scroller to disable vertical scrolling)
UIButton (leading, bottom to contentView = standard)
UIButton (leading, bottom to contentView = standard)
Solution:
Instead of a textView I used a UILabel with number of lines set to 0 and line breaks set to word wrap. After that I still didn't get the results that I was looking for. Then I had to change some auto layout constraints after which it finally worked.
The following articles where helpful to me while solving this issue:
Article about content sizing priorities
Article about Debugging AutoLayout