I was following a video on youtube, running through how to build a messaging app in order to further my learning (still fairly new to Swift) The video is outdated as the constraints used are for iOS 9. The code is below:
//iOS9 Constraints for views (text bubbles)
bubbleView.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
bubbleView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
bubbleWidthAnchor = bubbleView.widthAnchor.constraint(equalToConstant: 200)
bubbleWidthAnchor?.isActive = true
bubbleView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
//iOS9 Constraints for views (texts)
//textView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
textView.leftAnchor.constraint(equalTo: bubbleView.leftAnchor, constant: 8).isActive = true
textView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
textView.rightAnchor.constraint(equalTo: bubbleView.rightAnchor).isActive = true
//textView.widthAnchor.constraint(equalToConstant: 200).isActive = true
textView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
What I was wondering is, where can i go to further understand how these constraints where used? How can I figure out what constraints are needed for iOS 10?
Related
I have one view whose height is 50
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
Now what I want to do is when I click button (buton1), I want to change its height to 20. So I make below and it's working fine.
myView.heightAnchor.constraint(equalToConstant: 20).isActive = true
Now in myView I have another button (button2), when I click on it, I want to go back to previous height of 50, but it's not working.
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
Problem is here. Now the view is not re-sizing like when click on button1.
Note :
When I click on button1 & use below code, re-size also not work.
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
What I found is if I try to re-size to higher size of current size, it don't work.
Any idea why view is not re-sizing?
Edit 1
Even I wrote below after updating height anchor but still not working.
myView.layoutIfNeeded()
self.view.layoutIfNeeded()
Edit 2
Working Option 1
When click on button1, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 40).isActive = true
When click on button2, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 30).isActive = true
Not Working Option 2
When click on button1, I do below and works.
myView.heightAnchor.constraint(equalToConstant: 40).isActive = true
When click on button2, I do below and NOT WORK.
myView.heightAnchor.constraint(equalToConstant: 50).isActive = true
So what I found is I change height higher then previous, it don't work
At the moment, you are creating a new rule about the view's height every time. iOS doesn't understand how to make a view both 10, 20 and 30 pixels high, so it will do the lowest one. To avoid creating conflicting heights, you need to have one height rule and change that one's constant when needed. Here's a somewhat contained example.
class CoolView: UIViewController {
lazy var myView = UIView()
lazy var myViewHeightAnchor = myView.heightAnchor.constraint(equalToConstant: 10)
override func viewDidLoad() {
super.viewDidLoad()
myView.backgroundColor = .black//livesMatters
myView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(myView)
NSLayoutConstraint.activate([
myView.topAnchor.constraint(equalTo: view.topAnchor),
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
myView.widthAnchor.constraint(equalToConstant: 10),
myViewHeightAnchor
])
}
#objc func someButtonTapped() {
myViewHeightAnchor.constant = 20
view.layoutIfNeeded()
}
#objc func anotherButtonTapped() {
myViewHeightAnchor.constant = 30
view.layoutIfNeeded()
}
}
You should keep the reference to the constraint and change the constant value and then call layoutIfNeeded method to achieve this. Here's how:
// First
let heightConstraint = myView.heightAnchor.constraint(equalToConstant: 50)
heightConstraint.isActive = true
// Then to change
heightConstraint.constant = 20
myView.layoutIfNeeded()
As requested I'm adding playground sample here (try this out on Xcode-playground):
import UIKit
import PlaygroundSupport
class ViewController: UIViewController {
var heightConstraint: NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
let sampleView = UIView()
sampleView.backgroundColor = .green
sampleView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(sampleView)
heightConstraint = sampleView.heightAnchor.constraint(equalToConstant: 50)
let button = UIButton(type: .contactAdd)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(handleAction), for: .touchUpInside)
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
sampleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
sampleView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
heightConstraint!,
sampleView.widthAnchor.constraint(equalToConstant: 200)
])
}
#objc func handleAction() {
heightConstraint?.constant = heightConstraint?.constant == 50 ? 30 : 50
}
}
PlaygroundPage.current.setLiveView(ViewController())
Here you can see how updating the heightConstraint changes the height of the sampleView when the button is clicked. I'm toggling between 50 and 30 points in height here.
So short story,
I was adding below line every time I want to update height which is wrong.
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true
With this, what it do is take the least height and set that. Ex If I set height 3 times as 100, 20, 80, it will take 20 as the height even I run with 80.
To avoid this, we have to add a variable for the height constraint
var heightConstraint : NSLayoutConstraint?
Now while adding constraints to view, add as below.
heightConstraint = mainCVView.heightAnchor.constraint(equalToConstant: 50)
heightConstraint!.isActive = true
Now whenever you want to update height do it as below instead of adding constraint again.
heightConstraint?.constant = 0
heightConstraint?.isActive = true
myView.layoutIfNeeded()
This is most important step and not to add constraint the way I was adding in question
So whenever you want to update just update constant as show above. That's it.
I want to add UIButton like this:
let switchTheme: UIButton = {
let button = UIButton.init()
button.backgroundColor = .red
button.setTitleColor(.blue, for: .normal)
button.setTitle(Settings.isLightTheme() ? Strings.Various.switchToDark.value : Strings.Various.switchToLight.value, for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
And then set constraints like:
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor)
switchTheme.leftAnchor.constraint(equalTo: view.leftAnchor)
switchTheme.rightAnchor.constraint(equalTo: view.rightAnchor)
switchTheme.heightAnchor.constraint(equalToConstant: 40.0)
But it shown not on bottom as it suppose to but on top and without constraints applied.
You will need to set constraints activate state = true. You can do it simply,
NSLayoutConstraint.activate([
//Move your existing code HERE with comma separated
])
In case of any problem, you can check this following function:
func setConstraints() {
NSLayoutConstraint.activate([
switchTheme.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.bottomAnchor), // bottomAnchor to set bottom target.
switchTheme.leftAnchor.constraint(equalTo: self.view.leftAnchor), // leftAnchor to set X left
switchTheme.rightAnchor.constraint(equalTo: self.view.rightAnchor), // rightAnchor to set X right
switchTheme.heightAnchor.constraint(equalToConstant: 40.0) //heightAnchor to set appropriate height.
])
}
You need to active those constraints just simple like this :
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
switchTheme.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
switchTheme.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
switchTheme.heightAnchor.constraint(equalToConstant: 40.0).isActive = true
Your constrains must be activated :
switchTheme.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
I am have a view with a fixed width anchored to the left. I want this view to animate (moving left to right) by anchoring it to the right (thus removing the left anchor). How do I do this?
I tried setting a priority but I am not sure of the syntax. I also tried to disable the constraint, but that did not work.
fileprivate func sparkle() {
let sparkleView = UIView()
sparkleView.backgroundColor = UIColor.yellow
sparkleView.alpha = 0.5
addSubview(sparkleView)
sparkleView.translatesAutoresizingMaskIntoConstraints = false
sparkleView.topAnchor.constraint(equalTo: topAnchor).isActive = true
sparkleView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
sparkleView.widthAnchor.constraint(equalToConstant: frame.width / 5).isActive = true
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).priority = UILayoutPriorityDefaultLow // Doesn't work
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
layoutIfNeeded()
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).isActive = false // Doesn't work
sparkleView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
UIView.animate(withDuration: 2, animations: {
self.layoutIfNeeded()
}) { (_) in
sparkleView.removeFromSuperview()
}
}
Currently, the width constraint gets broken since we are assigning a left and right anchor.
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000020b8eb0 UIView:0x7fc846d551d0.width == 15.35 (active)>
-- EDIT --
I have tried creating an instance variable to hold the constraint.
let leftCons = sparkleView.leftAnchor.constraint(equalTo: leftAnchor)
leftCons.isActive = true
I then tried to modify the priority
leftCons.priority = UILayoutPriorityDefaultLow
However, this causes error
'Mutating a priority from required to not on an installed constraint (or vice-versa) is not supported. You passed priority 250 and the existing priority was 1000.'
Instead, I need to just set it as inactive (from the answers)
leftCons.active = false
-- Correct code if interested.. --
fileprivate func sparkle() {
let sparkleView = UIView()
sparkleView.backgroundColor = UIColor.yellow
sparkleView.alpha = 0.5
addSubview(sparkleView)
sparkleView.translatesAutoresizingMaskIntoConstraints = false
sparkleView.topAnchor.constraint(equalTo: topAnchor).isActive = true
sparkleView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
sparkleView.widthAnchor.constraint(equalToConstant: frame.width / 5).isActive = true
let leftCons = sparkleView.leftAnchor.constraint(equalTo: leftAnchor)
leftCons.isActive = true
layoutIfNeeded()
leftCons.isActive = false
sparkleView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
UIView.animate(withDuration: 0.5, animations: {
self.layoutIfNeeded()
}) { (_) in
sparkleView.removeFromSuperview()
}
}
You need to create a var
var leftCon:NSLayoutConstraint!
then alter it , you problem is every line create a new constraint
sparkleView.widthAnchor.constraint(equalToConstant: frame.width / 5).isActive = true
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
sparkleView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
note: above 3 constraints create a conflict as the view can't be pinned to left and right at the same time with a width constraint that why you see the conflict break => NSLayoutConstraint:0x6000020b8eb0 UIView:0x7fc846d551d0.width == 15.35 (active)
regardless of the active assignment so those
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).priority = UILayoutPriorityDefaultLow // Doesn't work
sparkleView.leftAnchor.constraint(equalTo: leftAnchor).isActive = false // Doesn't work
are on the fly and don't alter the created constraint , so to be
leftCon = sparkleView.leftAnchor.constraint(equalTo: leftAnchor)
leftCon.isActive = true
Regarding this crash
'Mutating a priority from required to not on an installed constraint (or vice-versa) is not supported
you need to set the priority before the activation
leftCon.priority = UILayoutPriorityDefaultLow
leftCon.isActive = true
I want to vertically centre a UILabel in a UIView. I'm using the following constraints:
layoutGuide = safeAreaLayoutGuide
header.translatesAutoresizingMaskIntoConstraints = false
titleLabel.translatesAutoresizingMaskIntoConstraints = false
header.topAnchor.constraint(equalTo: layoutGuide.topAnchor).isActive = true
header.rightAnchor.constraint(equalTo: layoutGuide.rightAnchor).isActive = true
header.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
header.heightAnchor.constraint(equalToConstant: UI.HEADER_HT).isActive = true
titleLabel.topAnchor.constraint(equalTo: header.topAnchor, constant: SPACING.LG).isActive = true
titleLabel.rightAnchor.constraint(equalTo: header.rightAnchor, constant: -SPACING.LG).isActive = true
titleLabel.leftAnchor.constraint(equalTo: header.leftAnchor, constant: SPACING.LG).isActive = true
titleLabel.heightAnchor.constraint(equalToConstant: UI.SCREEN_TITLE_HT).isActive = true
titleLabel.centerYAnchor.constraint(equalTo: header.centerYAnchor).isActive = true
The label is correctly centred but the height constraints is not being respected
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x6000003ade50 FC.StyledLabel:0x7fa077845330.height == 40.6 (active)>
If you want the title vertically centered, you probably don't want both topAnchor and centerYAnchor constraints. Perhaps you want to anchor that header to the label's top, instead?
Also, check out NSLayoutConstraint.activate. It will allow you to get ride of all the isActive = trues.
I'm setting up constraints programmatically in Swift 3 and have a question about constraints. If I don't set a height constraint but set a topAnchor and bottomAnchor, does that do the same thing?
self.squadTableView.translatesAutoresizingMaskIntoConstraints = false
self.squadTableView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.squadTableView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
self.squadTableView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
self.squadTableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
Compared to taking out the bottomAnchor constraint and then doing
self.squadTableView.heightAnchor.constraint(equalTo: self.view.heightAnchor).isActive = true
Having a top and bottom is sufficient if they are anchored to things that have sufficient constraints. You just need constraints that determine position and size, but it can be any combination that does that.