Set constraint on label from below collection view programmatically - ios

Set constraint lbl_Title from bottom to collectionView.
On setting the bottom constraint 60, the label goes below the collection view, after setting it to -60 then it's adjusted to location.
How to set constraints based on collection?
func setCollectionViewConstraints() -> Void {
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 10).isActive = true
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0).isActive = true
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0).isActive = true
collectionView.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
}
func setRecentJobLabelConstraints() -> Void {
lbl_Title.translatesAutoresizingMaskIntoConstraints = false
lbl_Title.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -60).isActive = true
lbl_Title.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
lbl_Title.heightAnchor.constraint(greaterThanOrEqualToConstant: 20).isActive = true
}
Here the issue is fixed if the constraint is set to -60, I think it's the wrong way.

Setting -60 is the right way. The coordinate system for CocoaTouch is a bit strange because it's (0,0) is in the top-left corner of the device, compared to the coordinated in Cocoa which starts from bottom-left. You'll get used to this once you do more auto-layout programmatically.
Note: Also, you need to give negative values when trying to constraint sub-views to super-views from right.
Different Approach: Another approach would be to constraint the super-view to the sub-view this way it's more readable and self-explanatory. Constraint the bottomAnchor of super-view to sub-view's bottomAnchor with a padding of 60 points.
bottomAnchor.constraint(equalTo: lbl_Title.bottomAnchor, constant: 60).isActive = true

It is not a wrong way , calling the constant while using bottom & trailing constraints should be with a minus value , you can use the below extension i created rather than repeating the same autolayout lines over & over
// MARK: - Anchors Method
extension UIView {
func anchors (top:NSLayoutYAxisAnchor? , leading:NSLayoutXAxisAnchor? , bottom : NSLayoutYAxisAnchor? , trailing: NSLayoutXAxisAnchor? , padding : UIEdgeInsets = .zero){
translatesAutoresizingMaskIntoConstraints = false
if let top = top {
topAnchor.constraint(equalTo: top , constant: padding.top).isActive = true
}
if let leading = leading {
leadingAnchor.constraint(equalTo: leading , constant: padding.left).isActive = true
}
if let bottom = bottom {
bottomAnchor.constraint(equalTo: bottom , constant: -padding.bottom).isActive = true
}
if let trailing = trailing {
trailingAnchor.constraint(equalTo: trailing , constant: -padding.right).isActive = true
}
}
}
and call it like below this :
YourUIView.anchors(top: View.topAnchor , leading: View.leadingAnchor , bottom: View.bottomAnchor , trailing: View.trailingAnchor , padding: .init(top: 10, left: 10, bottom: 10, right: 10))
A quick advice , there is no need to assign a void as a return since the function is not returning something .

Related

IOS SafeAreaLayoutGuide anchor for landscape screen

I've been following Paul Hudsons' Hacking with Swift tutorials and I'm up to project 6 where he uses layout constraint programmatically. I've been doing this kind of task solely using Interface Builder, but I'm keen to learn on how to do it programmatically.
From his tutorial, we have the following code that add 5 UILabels to the main controller's view.
let label1 = UILabel()
label1.translatesAutoresizingMaskIntoConstraints = false
label1.text = "THESE"
label1.backgroundColor = #colorLiteral(red: 0.5725490451, green: 0, blue: 0.2313725501, alpha: 1)
label1.sizeToFit()
// do the same with label2, label3, label4, label5
view.addSubview(label1)
view.addSubview(label2)
view.addSubview(label3)
view.addSubview(label4)
view.addSubview(label5)
and then I can add constraints manually:
let dictionary = [
"label1": label1,
"label2": label2,
"label3": label3,
"label4": label4,
"label5": label5
]
let metrics=[ "labelHeight":80]
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|[label1(labelHeight#999)]-[label2(label1)]-[label3(label1)]-[label4(label1)]-[label5(label1)]-(>=10)-|", options: [], metrics: metrics, views: dictionary))
for label in dictionary.keys {
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|[\(label)]|", options: [], metrics:nil, views: dictionary))
}
as you can see, I'm setting the first label's height to be 80. Then set label1 to have priority of 999, and make the remaining labels to follow label1's height constraint.
This is working fine, both in portrait and landscape mode.
Now i'm converting the code to use anchor.
let heightConstraint = label1.heightAnchor.constraint(equalToConstant: 88)
heightConstraint.priority = UILayoutPriority(rawValue: 999)
heightConstraint.isActive = true
for label in [label2, label3, label4, label5] {
label.heightAnchor.constraint(equalTo: label1.heightAnchor, multiplier: 1).isActive = true
}
var previousLabel : UILabel?
for label in [label1, label2, label3, label4, label5] {
label.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0).isActive = true
label.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0).isActive = true
if let previousLabel = previousLabel {
label.topAnchor.constraint(equalTo: previousLabel.bottomAnchor, constant: 10).isActive = true
} else {
label.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
}
previousLabel = label
}
label5.bottomAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 10.0).isActive = true
I think I'm missing something here, because
when the app is in portrait mode, it is trying to fill the entire screen
when the app is in landscape mode, label5 is chopped off.
I think i'm missing something here when using anchor? I'm guessing it is this bit:
-(>=10)-
But i'm not sure how to do it with anchor mode. Any assistance would be greatly appreciated!
For label5 you should change a bottom constraint to:
label5.bottomAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true
It should be a negative number since you fix it to the anchor, which is below (in contrast to previous anchors where you fix it to the label above your current one). And for negative numbers you need to use lessThanOrEqualTo.
For the vertical layout it works fine too since constraint is lessThanOrEqualTo:
This question is pretty old but I am also following too Paul Hudsons' Hacking with Swift tutorials and I have difficulties in Project 6 about same issue. But I figured out and it might be helpful to any other person.
For trailing and bottom constraints, value should be negative both view.safeAreaLayoutGuide.bottomAnchor and view.trailingAnchor. As far as I understand, dimension is determined always to rightward and downward.
label5.bottomAnchor.constraint(greaterThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 10.0).isActive = true
With this code, chopping off is pretty normal because by entering a positive value (+10.0), you already initialize the contsraint in the way that your label5's bottomAnchor will be under the safeAreaLayoutGuide bottomAnchor's by 10 units.
So, why does label5 seem in the view, entirely? Because label1 is attached to safeAreaLayoutGuide, your labels have determined vertical space between each other. Probably when you run the code, XCode will show some error and ignore some constraint. You may try to not determine a specific height. You can use:
label1.heightAnchor.constraint(greaterThanOrEqualToConstant: 80).isActive = true
And for the safeAreaLayoutGuide:
label5.bottomAnchor.constraint(lessThanOrEqualTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10.0).isActive = true

constraints not working programmatically swift

func textFields() {
let nameField = MDCFilledTextField()
view.addSubview(nameField)
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
nameField.placeholder = "Name"
}
I am using google material design and create a textfield.For this purpose and i have used this code i want to space from both sides left and right but it works only one side left or right i want to space from both side.
You are trying to set the MDCFilledTextField position in a wrong way. The following line tells your view to use a static predefined frame size:
let estimatedFrame = CGRect(x: 10, y: 200, width: UIScreen.main.bounds.width-20, height: 50)
let nameField = MDCFilledTextField(frame: estimatedFrame)
But further down with the following rows you tell your view to use autolayout:
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
So now you have to decide which one would you like to use, the static frame or the autolayout. If you decide to go with the autolayout you have to improve your code in order to remove the error you get. First you need to set the translatesAutoresizingMaskIntoConstraints to true instead of false.
nameField.translatesAutoresizingMaskIntoConstraints = false
This line will tell that you want to use the autolayout for nameField instead of a static frame. Further you need to add your view to the superview first, otherwise you can't define your constraints(therefore the error you have). So your code becomes:
func textFields() {
let nameField = MDCFilledTextField()
view.addSubview(nameField)
nameField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
nameField.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10),
nameField.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10),
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
nameField.placeholder = "Name"
}
Add the nameField as a subview of the view before activating the constraint and remove of using frames.
To add padding use for example centerXAnchor and widthAnchor + multiplier
func textFields() {
let nameField = MDCFilledTextField()
nameField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(nameField)
NSLayoutConstraint.activate([
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
nameField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
nameField.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.9)
])
nameField.placeholder = "Name"
nameField.label.text = "Name"
nameField.setFloatingLabelColor(.lightGray, for: MDCTextControlState.editing)
}
or add constants to the leftAnchor and rightAnchor.
func textFields() {
let nameField = MDCFilledTextField()
nameField.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(nameField)
NSLayoutConstraint.activate([
nameField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
nameField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
nameField.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 10),
nameField.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -10)
])
nameField.placeholder = "Name"
nameField.label.text = "Name"
nameField.setFloatingLabelColor(.lightGray, for: MDCTextControlState.editing)
}

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.

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

Programmatic constraints cut the bottom of my label off

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

Resources