Search code examples
swiftautolayoutnslayoutconstraint

Bind frame to superview bounds UIView extension


Pretty often I find myself writing these lines of code:

myView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    myView.topAnchor.constraint(equalTo: myView.superview.topAnchor),
    myView.bottomAnchor.constraint(equalTo: myView.superview.bottomAnchor),
    myView.leadingAnchor.constraint(equalTo: myView.superview.leadingAnchor),
    myView.trailingAnchor.constraint(equalTo: myView.superview.trailingAnchor)
])

So I'm thinking to write an extension. Something like this:

extension UIView {
    func bindFrameToSuperviewBounds() {
        self.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            self.topAnchor.constraint(equalTo: self.superview.topAnchor),
            self.bottomAnchor.constraint(equalTo: self.superview.bottomAnchor),
            self.leadingAnchor.constraint(equalTo: self.superview.leadingAnchor),
            self.trailingAnchor.constraint(equalTo: self.superview.trailingAnchor)
        ])
    }
}

My questions:

  • Maybe some built-in function (or technique) like this already exists, I just don't know about it (I've googled a lot, though)

  • Isn't (in theory) this code equivalent to:

    myView.translatesAutoresizingMaskIntoConstraints = true   
    myView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    

Why in theory? Because in practice it's definitely not equivalent. This alternative technique didn't ever give me a result I expected to see. The result is pretty much unpredictable.

Answering comments:

enter image description here

Where:
Green (outer) rectangle is a containerView (UIView).
Purple (inner) rectangle is a UIStackView, which I'm inserting.
As you can see, constraint approach works great.

Next one is a result of autoresizing mask approach:

enter image description here

Why there are three pictures?
Because the result is different with every new launch of the application!


Solution

  • These two techniques yield the same behavior:

    extension UIView {
        func bindFrameToSuperviewBoundsWithConstraints() {
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                topAnchor.constraint(equalTo: superview!.topAnchor),
                bottomAnchor.constraint(equalTo: superview!.bottomAnchor),
                leadingAnchor.constraint(equalTo: superview!.leadingAnchor),
                trailingAnchor.constraint(equalTo: superview!.trailingAnchor)
            ])
        }
    
        func bindFrameToSuperviewBoundsWithAutoResizingMask() {
            translatesAutoresizingMaskIntoConstraints = true
            frame = superview!.bounds
            autoresizingMask = [.flexibleWidth, .flexibleHeight]
        }
    }
    

    Consider:

    class ViewController: UIViewController {
        @IBOutlet weak var containerView: UIView!
    
        weak var timer: Timer?
    
        deinit {
            timer?.invalidate()
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            containerView.clipsToBounds = false
    
            let blueView = UIView()
            blueView.backgroundColor = .blue
            containerView.addSubview(blueView)
            blueView.bindFrameToSuperviewBoundsWithConstraints()
    
            let redView = UIView()
            redView.backgroundColor = .red
            containerView.addSubview(redView)
            redView.bindFrameToSuperviewBoundsWithAutoResizingMask()
    
            // toggle between the red and blue views
    
            blueView.isHidden = true
    
            Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { _ in
                redView.isHidden = !redView.isHidden
                blueView.isHidden = !blueView.isHidden
            }
        }
    }
    

    That yields two alternating subviews, each that uses a different technique, illustrating that their frames are the same:

    enter image description here

    If you are finding that the autoresizing mask solution is yielding different results than the constraint approach, there must be something else going on.