I have an interesting problem. My app is entirely managed through code instead of storyboards. I have a UIViewController which is presented programmatically. It creates a button and constraints for it.
This is what the controller code looks like.
import Foundation
import UIKit
class CreateViewController: UIViewController {
lazy var button: UIButton = {
let button = UIButton()
button.layer.bounds = CGRect(x: 0, y: 0, width: 50, height: 50)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .green
return button
}()
func setupViews() {
self.view.addSubview(button)
}
func setupConstraints() {
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[button]-100-|", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: ["button": button]))
self.view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[button(50)]-|", options: NSLayoutConstraint.FormatOptions(rawValue: 0), metrics: nil, views: ["button": button]))
}
override func viewDidLoad() {
setupViews()
setupConstraints()
}
}
It throws this error.
(
"<NSLayoutConstraint:0x60000288bb60 UIButton:0x7fa1ab422680.leading == UILayoutGuide:0x6000032da760'UIViewLayoutMarginsGuide'.leading NSSpace(0) (active)>",
"<NSLayoutConstraint:0x60000288bb10 UIButton:0x7fa1ab422680.width == 50 (active)>",
"<NSLayoutConstraint:0x60000288d7c0 UILayoutGuide:0x6000032da760'UIViewLayoutMarginsGuide'.trailing == UIButton:0x7fa1ab422680.trailing NSSpace(0) (active)>",
"<NSLayoutConstraint:0x6000028fe990 'UIView-Encapsulated-Layout-Width' UIView:0x7fa1ab4224a0.width == 375 (active)>",
"<NSLayoutConstraint:0x60000288fe30 'UIView-leftMargin-guide-constraint' H:|-(16)-[UILayoutGuide:0x6000032da760'UIViewLayoutMarginsGuide'](LTR) (active, names: '|':UIView:0x7fa1ab4224a0 )>",
"<NSLayoutConstraint:0x60000288d180 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x6000032da760'UIViewLayoutMarginsGuide']-(16)-|(LTR) (active, names: '|':UIView:0x7fa1ab4224a0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x60000288d7c0 UILayoutGuide:0x6000032da760'UIViewLayoutMarginsGuide'.trailing == UIButton:0x7fa1ab422680.trailing NSSpace(0) (active)>
My aim here is pretty simple. I want the button to be 100 pixels above the bottom, to be centered horizontally, and to have a fixed width and height.
This is how the controller is being instantiated. It exists inside of a
let nav2 = UINavigationController()
let create = CreateViewController()
nav2.viewControllers = [create]
nav2.navigationBar.isTranslucent = false
...
let tabs = UITabBarController()
tabs.viewControllers = [..., nav2, ...]
tabs.delegate = self
Any help in understanding why these constraints are being violated would be greatly appreciated! If any part of the question is unclear, just let me know.
FYI the resulting view looks like this:
Centering an element with Visual Format Language is difficult.
You can easily do it using .centerXAnchor
:
class CreateViewController: UIViewController {
lazy var button: UIButton = {
let button = UIButton()
// next line is not needed
// button.layer.bounds = CGRect(x: 0, y: 0, width: 50, height: 50)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .green
return button
}()
func setupViews() {
self.view.addSubview(button)
}
func setupConstraints() {
NSLayoutConstraint.activate([
// button width of 50
button.widthAnchor.constraint(equalToConstant: 50.0),
// button height of 50
button.heightAnchor.constraint(equalToConstant: 50.0),
// bottom of button 100-pts from bottom of view
// note: contant is negative!
button.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -100),
// center the button horizontally
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
])
}
override func viewDidLoad() {
setupViews()
setupConstraints()
}
}