Search code examples
swiftuiviewconstraints

Swift: adding a button programmatically without supplying a frame during initialization


Suppose I want to add a UIBUtton programmatically. Every UIView constructor requires a CGFrame, but in my case I want the size to be intrinsic and the location to be anchored to the super view's center.

  • If I instantiate the UIBUtton element without supplying a frame, I see it in the debug view hierarchy but not on the screen.
  • If I supply a frame, I basically guess the size and the x,y values brake the constraint I later add.

What is the proper way to add a button programmatically?

Thanks!

EDIT: There's no problem instantiating without a CGFrame. I didn't see the button because I didn't add

button.translatesAutoresizingMaskIntoConstraints = false

which is done automatically by the interface builder.


Solution

  • If you're using auto layout, set translateAutoResizingMaskIntoConstraints to false and ignore the frame, but don't forget to add constraints manually.

    Here is a simple example:

    override func viewDidLoad() {
        super.viewDidLoad()
        // no auto layout
        let v = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        v.backgroundColor = UIColor.blue
        view.addSubview(v)
        // with auto layout
        let v2 = UIView()
        v2.backgroundColor = UIColor.red
        // use auto layout
        v2.translatesAutoresizingMaskIntoConstraints = false
        // add width / height constraints
        v2.addConstraint(NSLayoutConstraint(item: v2, attribute: .width, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100))
        v2.addConstraint(NSLayoutConstraint(item: v2, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 100))
        // must add to hirarchy before adding the following constraints
        view.addSubview(v2)
        view.addConstraint(NSLayoutConstraint(item: v2, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 100))
        view.addConstraint(NSLayoutConstraint(item: v2, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1, constant: 0))
        // auto layout, visual format
        let v3 = UIView()
        v3.translatesAutoresizingMaskIntoConstraints = false
        v3.backgroundColor = UIColor.green
        let views = [ "v3" : v3 ]
        // must add v3 as subview before adding constraints referencing the parent view
        view.addSubview(v3)
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-200-[v3(100)]", options: [], metrics: nil, views: views))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[v3(100)]", options: [], metrics: nil, views: views))
    }
    

    For many views, there's no need to specify size as some views provide the size they want with intrinsicContentSize.
    You can use that for buttons to make them take the size they 'need', or force another size using constraints.
    For custom views - you may override this property to provide your own 'needed size' logic.