Is view.leadingAnchor.constraint higher precedence than view.trailingAnchor.constraint? - ios

I have my ViewController code as below
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let titleLabel = UILabel()
titleLabel.text = "Hello World!"
view.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor),
titleLabel.topAnchor.constraint(equalTo: view.topAnchor),
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
titleLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
}
I'm expecting my "Hello World!" to be center aligned. Why wasn't it center aligned? (Vertically it is centered, but horizontally it is aligned to left as shown below.)
p/s: If I remove itleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor), then it is aligned right.

Do you want your label to cover the entire view with the text centered? If so, keep your constraints as they are and set .textAlignment = .center.
I've set the background color of the label to cyan so you can see what's happening:
However, I suspect you just want your label centered in the view. In that case, change your constraints to this:
NSLayoutConstraint.activate([
titleLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor),
titleLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
Result:

Until and unless we are not changing priority of constraint the default priority is 1000 for all. So no precedence setting automatically.
For your code, add this line and it will work.
titleLabel.textAlignment = .center

In iOS 9 and later, the default value of textAlignment property of UILabel is NSTextAlignment.natural, prior to iOS 9, the default value was NSTextAlignment.left
If you want your label to cover the entire view with the text-centered then your constraints are perfect, just set
titleLabel.textAlignment = .center

Related

Setting a bottom constraint to a UITextView if needed

I'm trying to fix an issue with a UItextview which I placed at the bottom of a viewcontroller programmatically and sometimes it can clip through the bottom of the view if I don't set a constraint like so.
https://i.stack.imgur.com/YfyPi.png
Whenever I try to constraint the textview to the bottom of the safe area, the text needlessly expands too much if there's less text.
https://i.stack.imgur.com/8w1v2.png
Here's the relevant code snippets from the textview and the constraints respectively:
private let summaryTextView: UITextView = {
let summaryTextView = UITextView()
summaryTextView.textColor = .label
summaryTextView.backgroundColor = .customWhite
summaryTextView.textAlignment = .center
summaryTextView.font = UIFont.systemFont(ofSize: 24)
summaryTextView.clipsToBounds = true
summaryTextView.layer.cornerRadius = 20
summaryTextView.layer.masksToBounds = true
summaryTextView.isSelectable = false
summaryTextView.isEditable = false
summaryTextView.isScrollEnabled = false
summaryTextView.translatesAutoresizingMaskIntoConstraints = false
return summaryTextView
}()
private func setupConstraints() {
NSLayoutConstraint.activate([
imageContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 120),
imageContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
imageContainerView.heightAnchor.constraint(equalToConstant: 280),
imageContainerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.4),
summaryTextView.topAnchor.constraint(equalTo: imageContainerView.bottomAnchor,constant: 15),
summaryTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 10),
summaryTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -10),
summaryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
backgroundImage.fillSuperView(to: view)
bookCover.fillSuperView(to: imageContainerView)
}
Any help would be appreciated!
You need to set the height constraint for UITextView(). Because you are giving fixed top anchor and and bottom anchor so it stretches the textview.

Layout problems after replacing UILabel with UITextView in a UITableViewCell

I've got basic chat functionality as part of an App I'm building. It is basically a UITable View where the UITableViewCell only contains a UILabel (the chat message text) and a UIView (serving as a speech bubble, surrounding the text. Here's the code:
class ChatMessageViewCellController: UITableViewCell {
var ChatMessageText = UILabel()
var ChatBubble = UIView()
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var isIncoming: Bool! {
didSet {
if self.isIncoming {
self.ChatBubble.backgroundColor = UIColor(named: "customGrey")
self.leadingConstraint.isActive = true
self.trailingConstraint.isActive = false
} else {
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.leadingConstraint.isActive = false
self.trailingConstraint.isActive = true
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
addSubview(ChatBubble)
addSubview(ChatMessageText)
self.ChatBubble.translatesAutoresizingMaskIntoConstraints = false
self.ChatMessageText.translatesAutoresizingMaskIntoConstraints = false
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.ChatBubble.layer.cornerRadius = 10
self.ChatMessageText.numberOfLines = 0
self.ChatMessageText.textColor = .white
self.ChatMessageText.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.light)
let constraints = [
self.ChatMessageText.topAnchor.constraint(equalTo: topAnchor, constant: 16),
self.ChatMessageText.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -32),
self.ChatMessageText.widthAnchor.constraint(lessThanOrEqualToConstant: 220),
self.ChatBubble.topAnchor.constraint(equalTo: ChatMessageText.topAnchor, constant: -16),
self.ChatBubble.trailingAnchor.constraint(equalTo: ChatMessageText.trailingAnchor, constant: 16),
self.ChatBubble.bottomAnchor.constraint(equalTo: ChatMessageText.bottomAnchor, constant: 16),
self.ChatBubble.leadingAnchor.constraint(equalTo: ChatMessageText.leadingAnchor, constant: -16),
]
NSLayoutConstraint.activate(constraints)
self.leadingConstraint = self.ChatMessageText.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32)
self.trailingConstraint = self.ChatMessageText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32)
}
My problem is this:
I'm not feeding the UILabel with standard strings but with NSAttributedStrings, as I'd like to get some of the links in there clickable and parts of the text selectable by the user.
So I've been told to use a UITextView instead of the UILabel. I've thus made the following 2 changes:
Changed var ChatMessageText = UILabel()to var ChatMessageText = UITextView()
Did remove self.ChatMessageText.numberOfLines = 0 as UITextView doesn't have a numberOfLines member
Xcode doesn't complain and the app compiles and runs but it completely messes with my layout and I just can't figure out why. All the constraints from the UILabel should also work for the UITextView - at least I thought so. But here's how the screen looks like.
What am I doing wrong? Do I need to add / alter constraints?
By default, a UITextView has scrolling enabled.
While this seems obvious, that allows the user to enter more lines of text than will fit in the frame, and the user can scroll the text up and down.
In order for this to happen, UIKit has to know the frame of the text view. If the frame is not set, UIKit has no way to know how many lines to display, or how wide the view should be. So unless we have given the text view a full set of constraints, auto-layout will give it a size of .zero. Even if given a width (or max width) constraint, auto-layout still doesn't know how many scrollable lines of text we want displayed.
Setting .isScrollEnabled = false on the text view changes all of that.
Now, if we only constrained the position and width of the text view, UIKit will calculate the height based on the content size of the .text property.
This can be easily demonstrated. We'll create two text views, give them each top, leading and max-width (lessThanOrEqualTo) constraints, and the same text... but set .isScrollEnabled = false on one of them:
class TextViewTestViewController: UIViewController {
let nonScrollingTextView = UITextView()
let scrollingTextView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
let s = "This is a test string to demonstrate UITextView size behavior."
[nonScrollingTextView, scrollingTextView].forEach {
tv in
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont.systemFont(ofSize: 17.0)
tv.text = s
view.addSubview(tv)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
nonScrollingTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
nonScrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
nonScrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
scrollingTextView.topAnchor.constraint(equalTo: nonScrollingTextView.bottomAnchor, constant: 40.0),
scrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
scrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
])
// disable scrolling on the "top" text view
nonScrollingTextView.isScrollEnabled = false
// top text view is cyan
nonScrollingTextView.backgroundColor = .cyan
// bottom text view is green (although we won't see it)
scrollingTextView.backgroundColor = .green
}
}
Result:
We've added two text views, but only disabled scrolling on the "top" one (cyan background). We don't even see the second one (green background), because auto-layout gives it a height of Zero.
Worth noting... if the text view has scrolling disabled and has editing enabled, it will automatically grow / shrink as the user adds / deletes text.

Resize uiscrollview after adding content programmatically

I have an Contentview with uielements inside Uiscrollview. Below screenshot of my storyboard:
I want to add the option for the app that if the user clicks the button it will add label on the bottom of the contentview (below red underscored label - dzialTerminOutlet).
I'm adding the new label programmatically using following code:
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = UIColor.orange
label.textColor = UIColor.black
label.textAlignment = NSTextAlignment.center
label.text = "test label"
contentView.addSubview(label)
label.topAnchor.constraint(equalTo: dzialTerminOutlet.bottomAnchor, constant: 10).isActive = true
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 85.0)
label.widthAnchor.constraint(equalToConstant: 200.0)
label.heightAnchor.constraint(equalToConstant: 10.0)
The scrollview does not resize though. What's the issue here?
1- You need to activate
NSLayoutConstraint.activate([
label.topAnchor.constraint(equalTo: dzialTerminOutlet.bottomAnchor, constant: 10),
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 85.0),
label.widthAnchor.constraint(equalToConstant: 200.0),
label.heightAnchor.constraint(equalToConstant: 10.0)
])
2- You need to remove the bottom constraint established in IB between dzialTerminOutlet and contentView to be able to insert the new one and make the scrollView resize accordingly to avoid conflicts between it and
label.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 85.0)
so hook it as an outlet and deactivate it or search contentView for the bottom constraint and remove it
Did you set scrollView contentSize with new size?
scrollView.contentSize = CGSize(width: self.contentView.frame.size.width, height: self.contentView.frame.size.height)
a very simple way i'm using it a lot is to connect your contentView height constraint with an IBOutlet object and update it's value.
#IBOutlet weak var contentViewHeight : NSLayoutConstraint!
And after adding your label:
contentViewHeight.constant += labelHeight
Don't forget to set the contentView constraint (top, bottom, leading, trailing) = 0 with scroll view

UILabel won't show in multiple line

This is my UILabel:
let lblTitle: UILabel = {
let lbl=UILabel()
lbl.textColor=UIColor.darkGray
lbl.textAlignment = .center
lbl.font = UIFont.systemFont(ofSize: 36)
lbl.lineBreakMode = .byWordWrapping
lbl.numberOfLines=0
lbl.translatesAutoresizingMaskIntoConstraints=false
return lbl
}()
I have added lineBreakMode and set numberOfLines=0 which supposed to enable the multiline. My view is added programatically:
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.addSubview(lblTitle)
lblTitle.text="Testing testing testing testing"
lblTitle.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive=true
lblTitle.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive=true
scrollView.addSubview(btnGetChoose)
btnGetChoose.heightAnchor.constraint(equalToConstant: 50).isActive=true
btnGetChoose.widthAnchor.constraint(equalToConstant: 150).isActive=true
btnGetChoose.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive=true
btnGetChoose.topAnchor.constraint(equalTo: lblTitle.bottomAnchor, constant: 10).isActive=true
Why my UILabel won't show in multiple line?
Replace equal to with greater than or equal to.
Try this
lblTitle.heightAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive=true
Also note, you should use leading and trailing anchors (or width constraint) to provide content size to its super view (scrollview).
You have to set width or leading & trailing to wrap
lblTitle.widthAnchor.constraint(equalToConstant: self.view.frame.width / 2).isActive=true
Solved this problem by setting label's leading/trailing anchor as suggested by #keithbhunter
lblTitle.text="Testing testing testing testing"
lblTitle.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive=true
lblTitle.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive=true
lblTitle.leftAnchor.constraint(equalTo: scrollView.leftAnchor, constant: 12).isActive=true
lblTitle.rightAnchor.constraint(equalTo: scrollView.rightAnchor, constant: -12).isActive=true

UILabel prevents UIButton from being shown until you continue from debug view hierarchy

I have a stack view with 3 arranged subviews; a button with a checkbox image, a label with some text and another button with an 'i' image. When the view is created the last button isn't shown for some reason, so I go to debug view hierarchy, where I can see the button in the list of views but nowhere in the actual view. When I then press continue to stop debugging the view hierarchy, the button all of a sudden shows up and squeezes the label a bit so it fits.
If I remove the label, both buttons are shown just fine. If I put the views in a UIView instead of a stack view the 'i' button is not shown. If I shorten the text on the label, the 'i' button is shown correctly. EDIT: I also tried switching the label and the 'i' button and then both are shown correctly as well.
Do you know what methods are called when you continue from debugging that could change the views? Do you know another way to solve this issue?
Thanks
EDIT: Stackviews constraints are just superviews constrainst with some inset constants:
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 12),
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 12),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 10)
])
The buttons and label are just added as arranged subviews with no added constraints
Select the StackView go to Attributes Inspector check if Alignment = fill & Distribution = fill Now select the label inside the StackView go to Size Inspector find content Hugging priority & content Compression Resistance Priority the Horizontal priority to less than 250 So the label can easily be stretched and pressed back
Update:
I added 2 buttons and a label I just used text and colour in the buttons to simulate what you need.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let label = UILabel()
let checkmarkButton = UIButton()
let infoButton = UIButton()
label.text = "Jeg acceptere vilkår og betinging"
label.setContentHuggingPriority(240.0, for: .horizontal)
label.setContentCompressionResistancePriority(240, for: .horizontal)
label.textAlignment = .center
checkmarkButton.setTitle("Check", for: .normal)
checkmarkButton.backgroundColor = UIColor.blue
infoButton.setTitle("info", for: .normal)
infoButton.backgroundColor = UIColor.gray
view.addSubview(label)
view.addSubview(checkmarkButton)
view.addSubview(infoButton)
let stackView = UIStackView(arrangedSubviews: [checkmarkButton, label, infoButton])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fill
view.addSubview(stackView)
// You have set the trailing constant to 12 which is going outside of the screen if you want to set the trailingAnchor constant programmatically it should be negative
NSLayoutConstraint.activate([
stackView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80),
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -12),
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 12)
])
}

Resources