How to contain overflowed UILabel's text inside UIView?
Solution 1:
Here are my thoughts on what I think is happening.
I will start with the padding bit. I believe both these cases are actually the same:
The default behaviour is to center the text and in both cases this is happening. In the second case (image on the right) which you say there is a padding, the UILabel cannot fit another line of text at that font size and so it stops at line 4 and centers the text with the label's frame.
If you increase the label height to 500 for example, you will see that it is not a random padding, but rather a centering.
Here is a simple approximation of how many lines of text a given height might support.
let label = UILabel()
label.text = "This is my text.This is my text.This is my text.This is my text.This is my text.This is my text.This is my text."
label.numberOfLines = 0
label.sizeToFit()
// I added these line
print("Max lines that will fit: \(floor(100.0 / label.font.lineHeight))")
print("Max lines that will fit: \(floor(130.0 / label.font.lineHeight))")
The answer printed out is 4.0 and 6.0. As I increased your label height to 130 as follows, and it gave me 6 lines as approximated:
// Inside your set up function
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
container.leadingAnchor.constraint(equalTo: view.leadingAnchor),
container.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
container.heightAnchor.constraint(equalToConstant: 130.0),
container.widthAnchor.constraint(equalToConstant: 100.0)
])
let label = UILabel()
label.text = "This is my text.This is my text.This is my text.This is my text.This is my text.This is my text.This is my text."
label.numberOfLines = 0
label.sizeToFit()
So I hope that explains the bit about the padding.
Now coming to how to prevent the overflow, I think your autolayout already prevents that. I think also adding clipsToBounds
on the container will prevent the label from overflowing.
Finally, aligning the label to the top is a different question altogether. However there was a wonderful solution I came across here which was subclassing a UILabel and overriding drawRect like so:
// Credit to Ma11hew28 https://stackoverflow.com/a/41367948/1619193
class TopAlignedLabel: UILabel {
override func drawText(in rect: CGRect) {
let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
super.drawText(in: textRect)
}
}
// Your new set up function will be like this, I have commented
// the 2 changes I made
private func setup() {
let container = UIView()
container.backgroundColor = .red
view.addSubview(container)
container.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
container.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
container.heightAnchor.constraint(equalToConstant: 100.0),
container.widthAnchor.constraint(equalToConstant: 100.0)
])
// Use top aligned label
let label = TopAlignedLabel()
label.text = "This is my text."
label.numberOfLines = 0
label.sizeToFit()
container.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
label.widthAnchor.constraint(equalTo: container.widthAnchor)
])
// Clip container to bounds to prevent overflow
container.clipsToBounds = true
}
Final version prevents overflow and aligns text to the top when there is less text: