Programmatic constraints cut the bottom of my label off - ios

I have a Nib file with a root UITableViewCell and child UILabel that I anchor at run time using programmatic constraints
lblAccountItemTitle.translatesAutoresizingMaskIntoConstraints = false
lblAccountItemTitle.topAnchor.constraint(lessThanOrEqualTo: self.topAnchor, constant: 16).isActive = true
lblAccountItemTitle.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -16).isActive = true
lblAccountItemTitle.leadingAnchor.constraint(equalTo: imgAccountItemLeft.trailingAnchor, constant: 16).isActive = true
lblAccountItemTitle.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -32).isActive = true
lblAccountItemTitle.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
Also, I've noticed that the shorter I make my bottom anchor, the less clipped the text is
How can I get rid of the clipping while still maintaining the equal 16 vertical padding?

change the both image and label bottom anchor from equalTo to lessThanOrEqualTo
lblAccountItemTitle.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: -16).isActive = true

I was programmatically pinning the label to the cell and not to the Content View
self.topAnchor
should've been
self.contentView.topAnchor

Related

ios Swift tableView dynamic cells constraints for labels and image programatically

I have labels and an image. I want labels above image. And image without leading and trailing margin constraints.
I have tried the following constraints but can't seem to get it right. And the row height for the cells doesn't adjust accordingly in landscape mode.
addSubview(videolabel1)
videolabel1.translatesAutoresizingMaskIntoConstraints = false
videolabel1.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
videolabel1.trailingAnchor.constraint(equalTo: trailingAnchor
, constant: 20).isActive = true
videolabel1.topAnchor.constraint(equalTo: topAnchor, constant: 20).isActive = true
videolabel1.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 20).isActive = true
addSubview(image)
image.translatesAutoresizingMaskIntoConstraints = false
image.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
image.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
image.heightAnchor.constraint(equalToConstant: 150).isActive = true
image.widthAnchor.constraint(equalTo: videoimage.heightAnchor
, multiplier: 16/9).isActive = true
image.topAnchor.constraint(equalTo: videodate.bottomAnchor, constant: 12).isActive = true
image.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 12).isActive = true
Try using UIStackView's when you want to arrange views in a horizontal or vertical fashion. The UIStackView can take care of most constraints for you.
You can try adding a parent UIStackView with vertical orientation, that contains another UIStackView with horizontal orientation (this UIStackView will contain two UILabel's), followed by the UIImageView, and another UILabel.
parentStackView = UIStackView()
parentStackView.axis = .vertical
parentStackView.distribution = .equalSpacing
parentStackView.translatesAutoresizingMaskIntoConstraints = false
self.contentView.addSubview(parentStackView)
parentStackView.topAnchor.constraint(equalTo: self.contentView.topAnchor).isActive = true
parentStackView.widthAnchor.constraint(equalTo: self.contentView.widthAnchor, multiplier: 1).isActive = true
parentStackView.heightAnchor.constraint(equalTo: self.contentView.heightAnchor, multiplier: 1).isActive = true
parentStackView.isLayoutMarginsRelativeArrangement = true
parentStackView.addArrangedSubview(stackView)
stackView.axis = .horizontal
stackView.distribution = .equalSpacing
stackView.spacing = 10
stackView.addArrangedSubview(label)
label.text = "First Label";
stackView.addArrangedSubview(label2)
label2.text = "Second Label";
parentStackView.addArrangedSubview(imageView);
imageView.image = UIImage(named: "imageview");
NSLayoutConstraint.activate([imageView.heightAnchor.constraint(equalToConstant:UIScreen.main.bounds.width / 2) ]);
imageView.contentMode = .scaleAspectFill;
imageView.clipsToBounds = true;
parentStackView.addArrangedSubview(label3)
label3.text = "Vertical Label below Image";
The result will look something like:
Set bottom and top anchor according to sequential view on top and bottom. Here, you are setting bottom anchor of both videolabel1 and videodate to bottom anchor which is wrong:-
set videoLabel trailing and top anchor and remove bottom anchor:-
videolabel1.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
videolabel1.topAnchor.constraint(equalTo: topAnchor, constant: 20).isActive = true
set videoLabel trailing anchor as below and remove bottom anchor:-
videodate.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
Also, remove bottom anchor of imagview and set bottom anchor constant as shown below:-
videotitle.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -12).isActive = true

Issue with constraints inside a UITableViewCell

I have a custom cell for a UITableView. I'd like the following elements inside the cell:
1) UITextView with the following constraints:
it starts at the top left corner of the cell
it goes from the left side of the cell + 10 until the right side of the cell - 10.
its bottom should be 5 points above the next element (see 2 below)
2) UIButton with the following constraints:
it is X and Y centered in the cell
it goes from the left side of the cell + 20 until the right side of
the cell - 20.
it has a height of 60
The cell itself is defined with a height of 100.
However it seems my constraints have some conflicts according to the errors I get but I don't see where. Here is my code:
// constraints for the UIButton
answerTextButton.heightAnchor.constraint(equalToConstant: 60).isActive = true
answerTextButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
answerTextButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
answerTextButton.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
answerTextButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
// constraints for the UITextView
answerTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
answerTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
answerTextView.topAnchor.constraint(equalTo: topAnchor).isActive = true
answerTextView.bottomAnchor.constraint(equalTo: answerTextButton.topAnchor, constant: -5).isActive = true
What's the conflict here?
Thanks.
EDIT : I don't believe my mistake. While you are all right that the X-center constraint is useless, it was not the issue. The issue was... that I forgot to add "answerTextView.translatesAutoresizingMaskIntoConstraints = false".
Sorry for that, never happened before! So basically all my constraints for the UITextView were messy because of that. Adding it fixed everything but I kept your recommendation by removing the X-center constraint on the UIButton.
Copy and paste. It will work
// constraints for the UIButton
answerTextButton.heightAnchor.constraint(equalToConstant: 60).isActive = true
answerTextButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
answerTextButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20).isActive = true
answerTextButton.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
// constraints for the UITextView
answerTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
answerTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
answerTextView.topAnchor.constraint(equalTo: topAnchor).isActive = true
answerTextView.bottomAnchor.constraint(equalTo: answerTextButton.topAnchor, constant: -5).isActive = true

Why constraints are updated on scrolling of UITableView?

I have 2 simple UILabel that needs to be placed horizontally. One of them on right side of view, while second one is on the left side and gets all possible space. Here is my constraints:
amountOfQuestions.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
amountOfQuestions.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
topicName.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
topicName.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8).isActive = true
topicName.rightAnchor.constraint(equalTo: amountOfQuestions.leftAnchor, constant: -8).isActive = true
Seems like everything is ok, but when table view is shown, it looks like this:
But after I scroll couple of times my table up and down, it became normal:
Why my table is not shown immediately like in second picture?
Solution:
amountOfQuestions.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
amountOfQuestions.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
amountOfQuestions.setContentHuggingPriority(.required, for: .horizontal)
amountOfQuestions.setContentCompressionResistancePriority(.required, for: .horizontal)
topicName.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
topicName.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true
topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8).isActive = true
topicName.rightAnchor.constraint(equalTo: amountOfQuestions.leftAnchor, constant: -8).isActive = true
topicName.setContentHuggingPriority(.required, for: .horizontal)
topicName.setContentCompressionResistancePriority(.required, for: .vertical)
let myBottom = topicName.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8)
myBottom.priority = UILayoutPriority(rawValue: UILayoutPriority.required.rawValue - 1)
myBottom.isActive = true
There are multiple problems that can affect the layout and make it incorrectly defined:
Never constrain content to the cell itself. Always constrain to the contentView of the cell, e.g.:
amountOfQuestions.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -8).isActive = true
(make sure you are adding the labels as subviews of the contentView, not of the cell itself).
If you want the size of the labels to have any effect, you need to set hugging and compression resistance priorities correctly. Otherwise the layout does not have to respect the content:
// you want topic name to resize vertically to respect the content
topicName.setHuggingPriority(.required, for: .vertical)
topicName.setContentCompressionResistancePriority(.required, for: .vertical)
// you want amount of question to resize horizontally to respect the content
amountOfQuestions.setHuggingPriority(.required, for: .horizontal)
amountOfQuestions.setContentCompressionResistancePriority(.required, for: .horizontal)
To avoid layout conflicts during the first layout, it's a good idea to give one the of the vertical constraints a slightly lower layout priority:
let bottomAnchor = topicName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8)
bottomAnchor.priority = UILayoutPriority(rawValue: UILayoutPriority.required.rawValue - 1)
bottomAnchor.isActive = true
1- UILabels have an intrinsic content value , that means you don't have to specify a width / height for them , so when you place them horizontally like this
| - lbl1 - lbl2 - |
This causes a constraint ambiguity because autolayout need to know which label to trim when a one content is too bigger that's why you need to set horizontal compression resistance && setHuggingPriority to lbl2 = 1000
2- You need to lower priority of bottom constraint as the cell assumes a fixed height in the begining of layout and that height may conflict with current total height of the vertical content , so you lower it to avoid this conflict

Swift iOS -Push uiview down while typing inside textfield

I have a textfield that I set to a height of >= 50. Underneath the textfield I have uiview that is anchored to the bottom of the textfield. Once the textfield gets past the 50 point height I want it to push the uiview down so the text continues downward. What's happening right now is it hits the bottom of itself and instead of pushing the uiview down it scrolls the text up and the uiview doesn't move.
How can i get the textfield to stop scrolling up and instead push the uiview down once the textfield goes past the 50 point height? I can use a textfield with number of lines to 0 instead but I'd rather use a textfield.
// imageView is pinned to the top of the screen
textView.topAnchor.constraint(equalTo: iamgeView.bottomAnchor, constant: 32).isActive = true
textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 50).isActive = true
bottomLine.topAnchor.constraint(equalTo: textView.bottomAnchor, constant: 8).isActive = true
bottomLine.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
bottomLine.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -8).isActive = true
bottomLine.heightAnchor.constraint(equalToConstant: 0.5).isActive = true
I had to disable scrolling on the textField
textView.isScrollEnabled = false

UIScrollView nested subviews not displayed using Auto Layout

I am trying to generate views displayed in a UIScrollView based on structured (XML) data at runtime. I generate all the views I need and add them to my scrollview afterwards, using Auto Layout to let the scrollview calculate its contentSize. Everything worked fine as long as I was only using UILabels and a custom UIImageView subclass, but now I want to add a nested view containing another view and a label to represent text with a vertical line on the left side.
The general layout code I'm using to add all generated views to the scrollview is as follows:
var previousAnchor = scrollView.topAnchor
views.forEach { (view) in
scrollView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,
constant: UI.smallPadding).isActive = true
view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,
constant: -UI.smallPadding).isActive = true
previousAnchor = view.bottomAnchor
}
scrollView.bottomAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
And the layout code for my nested subview looks like this (it's a UIView subclass):
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor).isActive = true
line.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
line.widthAnchor.constraint(equalToConstant: 2).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
If I use this code, I cannot see this view in my scrollview at all and the scrollview has a warning of ambiguous content size attached to it in the Debug View Hierarchy view. This is somewhat irritating, as using labels without being nested in a view like this does work like a charm. So I started playing around and what really puzzles me is that if I use the following code I can see the view itself (as I gave it a gray background color), but still can't see line and/or label, as both seem have a frame of (0,0,0,0) upon inspection:
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
line.widthAnchor.constraint(equalToConstant: 20).isActive = true
line.heightAnchor.constraint(equalToConstant: 20).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
heightAnchor.constraint(equalToConstant: 100).isActive = true
heightAnchor.constraint(equalTo: label.heightAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
What am I missing? I also tried putting all my views into a container view instead of using them as subviews of the scrollview directly, but this did not help with my nested view and brought up other problems.

Resources