How do I offset UILabel text alignment? - ios

I am trying to offset the center starting point for a UILabel. The problem that I am facing is that I can't make the label text to grow from a point that is offset from center until it reaches one end of the label. Then, I want the text to shift one character to the left with each additional added character until it reaches the the other end. Then it would be acceptable for the text to truncate with an ellipses.
So far, my code looks like this but I don't know where to go from here.
private let amountLabel: UILabel = {
let label = UILabel()
label.textColor = .blue
label.textAlignment = .center
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()

I suggest you put the UILabel into a container view, center horizontally with the offset you want, but set its priority lower than the compression resistance., e.g. 750, compression resistance 999. Then create a trailing constraint >= with priority 1000, and leading constraint >= priority 1000. In that way centering will be the weakest constraint and as the text grows it will shift to the left until it reaches the leading.

Related

How to center text in label with font autoresizing?

I have a label that resizes it's
label.adjustsFontForContentSizeCategory = true
label.adjustsFontSizeToFitWidth = true
If I set short text, it stays in the center. But if I put long text, it goes down. What do I need to do?
Label constrained to be in X and Y centers of the view.
Short text:
Long text:
Add the following code:
label.adjustsFontForContentSizeCategory = true
label.adjustsFontSizeToFitWidth = true
label.label.minimumScaleFactor = 0.4
label.numberOfLines = 1
Set UIlabel Top, hight, >= width and horizontally center constraint see the following image and get the expected output as you want.
Note: Keep the number of line 1 as it is.
Short text output:
Long text output:
Set the following constraint
Top, height, width
Horizontally center constraint
Width is >=

Force a view to take as much space as possible inside UIStackView

I'm creating part of my application's UI with Swift and the problem I'm facing is I have a UIStackView with 3 sub views: 2 UILabels and an UIImageView. Here is my first code
let switchview = UISwitch()
let nodelableview = UILabel()
nodelableview.textAlignment = NSTextAlignment.right
nodelableview.numberOfLines = 0
nodelableview.text = nodes[i].type + " " + nodes[i].node_name
let statLabel = UILabel()
statLabel.textAlignment = NSTextAlignment.left
statLabel.text = nodes[i].stat
let stack = UIStackView()
stack.axis = .horizontal
stack.spacing = 16
stack.addArrangedSubview(statLabel)
stack.addArrangedSubview(nodelableview)
stack.addArrangedSubview(switchview)
cell.nodesView.addArrangedSubview(stack)
the problem with this code is that when the nodelabelview has long text the UIStackView not extending to make space for 2 or more lines. So I set the alignment to .center and here is the result
There is empty space left but the first UILabel is using it for nothing. How can I force the second UILabel to use available spaces?
A setup that would give priority to your second label (the one with unlimited number of lines), would be a stackview set to "Fill Proportionally" distribution (which means that views are sized based on their intrinsic size & hugging/resistance priorities)
combined with a horizontal "Content Compression Resistance Priority" of 1000 ('required') for the left label & the switch (which means 'do not compress')
which is resolved to this:
You may need to set the horizontal contentHuggingPriority and contentCompressionResistance for each label / switch to something different from the others, ensuring that the one you wish to expand to fill remaining available space has the lowest hugging value.

UILabel breaks early inside UIStackView

Given the view hierarchy:
UIStackView
--UILabel
--UISwitch
The label breaks too early, even if it can be fit to a single line.
Setting numberOfLines = 1 forces the label to be laid out correctly.
How to make UILabel perform line break only when needed?
Code:
private lazy var title: UILabel = {
let v = UILabel()
v.numberOfLines = 0
return v
}()
private lazy var toggle = UISwitch()
private lazy var stack = UIStackView(axis: .horizontal,
distribution: .equalSpacing,
alignment: .center,
views: [title,
toggle])
func setupConstraints() {
stack.snp.makeConstraints { (make) in
make.edges.equalTo(contentView.layoutMarginsGuide)
}
}
Result:
Setting numberOfLines = 1 gets me what I'd like to achieve, but the label looses its multi-line functionality:
How to force the desired behavior without losing support for multi-line labels?
When there is a lot of horizontal space, the label gets laid out correctly no matter of the numberOfLines property:
Set your UISwitch's content hugging and resistance priority to 1000.
And stack view distribution and alignment to fill.
Extra Note - If you want label and switch to be top aligned, then set alignment to top.
In your stack view you can give a constraint to your label and switch...
1) give your label leading, top , trailing and bottom constraint. Don't give Width constraint. In trailing constraint take second item Switch.
2) give your switch trailing, top, bottom and Fix width.
Hope it Will work.
Add label inside another stack view.
UIStackView
--UIStackView
--UILabel
--UISwitch

Two labels alignment and its constraints

I have 2 labels: the description label (w/ red background) and the results label (gray text)
How do i set constraints for this example in order to have the results label with the size of its content and the description label until the results leadingAnchor? (like i have in the second row)
Objective C
[self.customTextLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.counterLabel.leadingAnchor].active = YES;
[self.counterLabel.widthAnchor constraintGreaterThanOrEqualToConstant:0].active = YES;
swift
titleLabel.trailingAnchor.constraint(lessThanOrEqualTo: counterLabel.leadingAnchor).isActive = true
counterLabel.widthAnchor.constraint(greaterThanOrEqualToConstant: 0).isActive = true
I have a solution that i think it's ugly.
self.counterLabelWidthConstraint = [self.counterLabel.widthAnchor constraintEqualToConstant:0];
self.counterLabelWidthConstraint.active = YES;
And then after i set the text:
self.counterLabelWidthConstraint.constant = [self.counterLabel sizeThatFits:CGSizeMake(CGFLOAT_MAX, self.counterLabel.height)].width;
The way to do this with auto layout is by using the contentCompressionResistancePriority of the 2 labels. Set the pririty to NSLayoutPriorityRequired for the second label and something lower like NSLayoutPriorityDefaultLow for the first label. Then, as long as the 2 labels have proper constraints anchoring them to their superview and each other, the first label should compress while the second label should not.
You just need to increase the horizontal compression resistance of the right/gray label to be higher than that of the left/red label. This tells the visual layout that, in the event that there is not enough space for both labels, the one on the left will be compressed before shrinking the label on the right. 750 is the default for all views, so just increase the right/gray label's horizontal compression resistance to 751 and you should be good to go.
Swift 5 programatically:
<#label#>.setContentCompressionResistancePriority(.required, for: .horizontal)
Labels with this property will not compress horizontally.
You can set constraints for in storyboard itself. Select Label 1 (red back ground) and label's superview set widths are equal constraint. Select Label 1 and double tap on its width constraint, from the resultant window, you could see Lable 1 width equal to superview with value constant as '1'. change '1' to 0.7 or whichever the percentage you want.

ios autolayout dynamic UILabel height

Say I have three UILabels whose positions are like below:
[Label1] [Label2]
[Label3]
Label1 and Label2 are in the same row and Label3 is below them. All the labels will have a fixed width and will contain dynamic text, so their height will vary.
How do I make the Label3 10 points below the label which has a higher height using AutoLayout?
For example, if Label1's height is 100 points, Label2's height is 120 points (their Y positions are the same), then Label3 should be 10 points below Label2, but if Label1 is 120 points high and Label2 is 100 points high, then Label3 should be 10 points below Label1.
You simply make constraints between both Label3->Label1 and Label3->Label2. Use inequality constraints. There will be only one way to satisfy both!
You will also need a top constraint for Label3; its constant should be very small and its priority should be very low. This will give the two inequality constraints something to "aim at".
Here is an example. This as achieved entirely without code - the buttons have code to add text to the labels, of course, but the constraints are configured entirely in Interface Builder; the labels are resizing, and the bottom label is moving down, automatically. (You can construct the same layout in code if you want to, naturally.)
I suggest you to wrap top two labels to UIView and setup constraints so these labels fit all space inside that view. Then you simple add vertical spacing constraint to bottom label3 with constant = 10. In that case top view will have size of larger label and will satisfy your conditions
I thought this would be an interesting exercise so I create a little test project. The gist of the code is below. You can just copy/paste it in the standard Single View iOS template.
(Note that I use SnapKit for programmatic Auto Layout because it is so much simpler than the UIKit API. I find it even much simpler than doing things in Xcode.)
The result is exactly the same as Matt's great screencast.
// ViewController.swift
import UIKit
import SnapKit
class ViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
let leftLabel = UILabel()
leftLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "addText:"))
leftLabel.userInteractionEnabled = true
view.addSubview(leftLabel)
leftLabel.numberOfLines = 0
leftLabel.text = "All the world's a stage, and all the men and women merely players: they have their exits and their entrances; and one man in his time plays many parts, his acts being seven ages."
leftLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(40)
make.left.equalTo(self.view)
make.right.equalTo(self.view.snp_centerX)
}
let rightLabel = UILabel()
rightLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "addText:"))
rightLabel.userInteractionEnabled = true
view.addSubview(rightLabel)
rightLabel.numberOfLines = 0
rightLabel.text = "There is a tide in the affairs of men, Which taken at the flood, leads on to fortune. Omitted, all the voyage of their life is bound in shallows and in miseries. On such a full sea are we now afloat. And we must take the current when it serves, or lose our ventures."
rightLabel.snp_makeConstraints { (make) -> Void in
make.top.equalTo(40)
make.right.equalTo(self.view)
make.left.equalTo(self.view.snp_centerX)
}
let bottomView = UIView()
view.addSubview(bottomView)
bottomView.backgroundColor = UIColor.redColor()
bottomView.snp_makeConstraints { (make) -> Void in
make.height.equalTo(20)
make.left.right.equalTo(self.view)
make.top.greaterThanOrEqualTo(leftLabel.snp_bottom)
make.top.greaterThanOrEqualTo(rightLabel.snp_bottom)
}
}
#objc func addText(recognizer: UIGestureRecognizer) {
if let label = recognizer.view as? UILabel {
label.text = label.text! + " I like cheese."
}
}
}
Updated the code to add some additional text to the labels when tapped.
First of all remove height constraints and set all 3 labels vertical Content Compression Resistance Priority to 1000. This is the most important part.
Then add vertical space from Label3 to Label 1, and set instead of Equal, Greater Than or Equal with priority say 500. Add same space constraint to Label2.
Last add constraint from Label3 to Top = 0, but set priority to 1.

Resources