Hi I have the following code:
class MyController {
override func viewDidLoad() {
self.addButtons()
super.viewDidLoad()
}
func addButtons() {
let cancelButton = UIButton(type: .custom)
let validateButton = UIButton(type: .custom)
cancelButton.setImage(UIImage(named: "cancel_icon"), for: .normal)
validateButton.setImage(UIImage(named: "validate_icon"), for: .normal)
cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
validateButton.addTarget(self, action: #selector(validateAction), for: .touchUpInside)
cancelButton.translatesAutoresizingMaskIntoConstraints = false
validateButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(cancelButton)
self.view.addSubview(validateButton)
let leftCancel = NSLayoutConstraint(item: cancelButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 16)
let bottomCancel = NSLayoutConstraint(item: cancelButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 20)
let widthCancel = NSLayoutConstraint(item: cancelButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let heightCancel = NSLayoutConstraint(item: cancelButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let rightValidate = NSLayoutConstraint(item: validateButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: 16)
let bottomValidate = NSLayoutConstraint(item: validateButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: 20)
let widthValidate = NSLayoutConstraint(item: validateButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let heightValidate = NSLayoutConstraint(item: validateButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
NSLayoutConstraint.activate([leftCancel, bottomCancel, rightValidate, bottomValidate, widthCancel, heightCancel, widthValidate, heightValidate])
self.view.layoutIfNeeded()
}
func cancelAction() {
self.dismiss(animated: true, completion: nil)
}
func validateAction() {
self.dismiss(animated: true, completion: nil)
}
}
So basically I'm just adding two buttons with a width and height constraint, and I set the cancelButton
to the bottom left and the validateButton
to the bottom right.
My buttons don't appear. When I set the frame of the buttons this way, it works though:
cancelButton.frame = CGRect(x: 16, y: self.view.frame.height - 40, width: 20, height: 20)
validateButton.frame = CGRect(x: self.view.frame.width - 36, y: self.view.frame.height - 40, width: 20, height: 20)
Does someone know what's wrong with my constraints? Thanks!
VisualFormat has its uses, but it also has its own quirks - such as flexible spacing between elements. Here's a solution without VisualFormat:
//: Playground - noun: a place where people can play
import UIKit
import PlaygroundSupport
class MyController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.addButtons()
}
func addButtons() {
let cancelButton = UIButton(type: .custom)
let validateButton = UIButton(type: .custom)
cancelButton.setImage(UIImage(named: "cancel_icon"), for: .normal)
validateButton.setImage(UIImage(named: "validate_icon"), for: .normal)
cancelButton.addTarget(self, action: #selector(cancelAction), for: .touchUpInside)
validateButton.addTarget(self, action: #selector(validateAction), for: .touchUpInside)
cancelButton.translatesAutoresizingMaskIntoConstraints = false
validateButton.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(cancelButton)
self.view.addSubview(validateButton)
validateButton.backgroundColor = UIColor.red
cancelButton.backgroundColor = UIColor.blue
validateButton.setTitle("V", for: UIControlState())
cancelButton.setTitle("C", for: UIControlState())
let leftCancel = NSLayoutConstraint(item: cancelButton, attribute: .leading, relatedBy: .equal, toItem: self.view, attribute: .leading, multiplier: 1, constant: 16)
let bottomCancel = NSLayoutConstraint(item: cancelButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -20)
let widthCancel = NSLayoutConstraint(item: cancelButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let heightCancel = NSLayoutConstraint(item: cancelButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let rightValidate = NSLayoutConstraint(item: validateButton, attribute: .trailing, relatedBy: .equal, toItem: self.view, attribute: .trailing, multiplier: 1, constant: -16)
let bottomValidate = NSLayoutConstraint(item: validateButton, attribute: .bottom, relatedBy: .equal, toItem: self.view, attribute: .bottom, multiplier: 1, constant: -20)
let widthValidate = NSLayoutConstraint(item: validateButton, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
let heightValidate = NSLayoutConstraint(item: validateButton, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 20)
NSLayoutConstraint.activate([leftCancel, bottomCancel, rightValidate, bottomValidate, widthCancel, heightCancel, widthValidate, heightValidate])
self.view.layoutIfNeeded()
}
func cancelAction() {
// self.dismiss(animated: true, completion: nil)
print("cancel")
}
func validateAction() {
// self.dismiss(animated: true, completion: nil)
print("validate")
}
}
var vc = MyController()
vc.view.backgroundColor = UIColor.green
vc.view.frame = CGRect(x: 0, y: 0, width: 400, height: 400)
PlaygroundPage.current.liveView = vc.view
PlaygroundPage.current.needsIndefiniteExecution = true
You should be able to copy/paste this into a Playground page to see the results. Note that I don't have your button images, so I "V" and "C" titles.
The key difference is setting the Bottom and Trailing values to negative - that is, you want the Bottom of the Button to be (Bottom of containing view MINUS 20). Likewise with the Trailing edge of the Button to be -16 from the trailing edge of the containing view.