1

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!

Roman
  • 1,309
  • 14
  • 23
  • 1
    To make the non-autolayout example equivalent, you also have to add a `myView.frame = myView.superview.bounds`. – Rob Jan 05 '21 at 05:02
  • 1
    Possible duplicate for your second question: https://stackoverflow.com/questions/36434947/in-ios-whats-the-difference-between-autoresizing-autolayout-and-constraints – Sweeper Jan 05 '21 at 05:04
  • @Rob I've tried all imaginable combinations of all these properties, I've never got the equivalent result. I've just tried your suggestion one more time. Nope. – Roman Jan 05 '21 at 05:10
  • @Sweeper Yes, it's related and interesting. But they don't discuss how to achieve equivalence. – Roman Jan 05 '21 at 05:17
  • @Roman Can you explain how, after applying Rob's suggestion, the actual result differs from your expectation? – Sweeper Jan 05 '21 at 05:19
  • 1
    This technique of (a) settings the `frame` of the subview to the `bounds` of its superview; and (b) setting the autoresizing mask, is precisely how we used to do this before constraints. I just tested it, and it works fine. You're going to need to create a [MCVE](https://stackoverflow.com/help/mcve) that manifests the problem. Maybe you've got some unrelated code that is changing either the `translatesAutoresizingMaskIntoConstraints` or the `autoresizingMask` after the fact. Or you have some other conflicting constraints. But this `autoresizingMask` technique is a tried and true technique... – Rob Jan 05 '21 at 05:25
  • Ok, guys, thank you. I will create MCVE and let you know. Maybe a little later. – Roman Jan 05 '21 at 05:29
  • @Sweeper I've updated my post, take a look please :) – Roman Jan 06 '21 at 08:54
  • You should consider using the [view debugger](https://help.apple.com/xcode/mac/current/#/dev4dfdedb7a) to diagnose what’s going on. It looks like there are ambiguous constraints, but it’s impossible to tell on the basis of a series of screen snapshots. – Rob Jan 06 '21 at 15:29

2 Answers2

3

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Well... it's probably a Maximum Unreproducible Example ))) I've updated my post, take a look please :) – Roman Jan 06 '21 at 08:56
0

Hi you can use an extension like this:

func constraint(to view: UIView, padding: CGFloat = 0) {
    self.translatesAutoresizingMaskIntoConstraints = false
    self.topAnchor.constraint(equalTo: view.topAnchor, constant: padding).isActive = true
    self.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -padding).isActive = true
    self.leftAnchor.constraint(equalTo: view.leftAnchor, constant: padding).isActive = true
    self.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -padding).isActive = true

and with another one to create a short cut for superview:

func constraintToSuperview(padding: CGFloat = 0) {
    guard let superview = self.superview else {
        return
    }
    
    self.constraint(to: superview, padding: padding)
}
Sniro
  • 1
  • 1