MH175
MH175

Reputation: 2334

Auto Layout programmatically (NSLayoutConstraint): center inner view in outer view and constrain to min(width, height)

I'm not sure what I'm doing wrong using NSLayoutConstraint to generate Auto Layout.

I want to put an inner view centered in an outer view using the smaller of the outer view's width and height. Finally, I want to apply a scaling factor.

Auto Layout Centered

Here's my code.

     NSLayoutConstraint.activate([
            inner.centerXAnchor.constraint(equalTo: outer.centerXAnchor),
            inner.centerYAnchor.constraint(equalTo: outer.centerYAnchor),
            inner.widthAnchor.constraint(equalTo: outer.heightAnchor, multiplier: imageScale),
            inner.widthAnchor.constraint(equalTo: outer.widthAnchor, multiplier: imageScale),
            inner.heightAnchor.constraint(equalTo: inner.widthAnchor)
    ])

This works for:

But when width > height I get this:

enter image description here

What am I missing? This is very easy to do using frames:

let innerWidth = min(outer.frame.width, outer.frame.height) * imageScale
let innerHeight = innerWidth
inner.frame = CGRect(x: (outer.frame.width -innerWidth) / 2, 
                     y: (outer.frame.height - innerHeight) / 2,
                     width: innerWidth, height: innerHeight)

Upvotes: 1

Views: 568

Answers (1)

par
par

Reputation: 17724

First, it's a bit weird that you are setting the inner.widthAnchor constraint twice in your code. Set it only once.

Also, you need to select the outer anchor for the inner view's dimensions based on the actual outer view's frame. If the smallest outer dimension is width, then you constrain the width of the inner view to outer.widthAnchor * imageScale. Otherwise, when the smallest outer dimension is height, you constrain to outer.heightAnchor * imageScale.

This works for me to get the layout you're looking for (I just created a simple single-view project):

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let inner = UIView()
        inner.backgroundColor = .yellow
        inner.translatesAutoresizingMaskIntoConstraints = false

        let outer = UIView()
        outer.backgroundColor = .blue
        outer.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(outer)
        view.addSubview(inner)

        // get these from somewhere, e.g. outerWidth.frame.size
        let outerWidth = CGFloat(200)
        let outerHeight = CGFloat(400)
        let outerAnchor: NSLayoutDimension

        if outerWidth >= outerHeight {
            outerAnchor = outer.heightAnchor
        } else {
            outerAnchor = outer.widthAnchor
        }

        let imageScale = CGFloat(0.5)

        NSLayoutConstraint.activate([
            outer.widthAnchor.constraint(equalToConstant: outerWidth),
            outer.heightAnchor.constraint(equalToConstant: outerHeight),
            outer.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            outer.centerYAnchor.constraint(equalTo: view.centerYAnchor),

            inner.centerXAnchor.constraint(equalTo: outer.centerXAnchor),
            inner.centerYAnchor.constraint(equalTo: outer.centerYAnchor),
            inner.widthAnchor.constraint(equalTo: outerAnchor, multiplier: imageScale),
            inner.heightAnchor.constraint(equalTo: inner.widthAnchor)
        ])
    }
}

Upvotes: 2

Related Questions