Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add fixed size UIView to UIStackView?

On the latest Xcode (10.1), I'm having trouble adding a fixed size UIView to my UIStackView using programmatic constraints. I think this should be straightforward, but I don't understand where extra constraints are coming from (that UIKit has to break some to layout the views).

The problem is that I am expecting a blue UIView of 100x100. The reality is that the blue UIView is 100% screen width & 100% screen height.

I realize UIStackView uses intrinsicContentSize, but how do I set that correctly using programmatic constraints?

The following is a working playground with an UIStackView & a vanilla UIView added.

Note: if I add the blue UIView directly to the ViewController's view, the size is correct at 100x100 at origin (0,0). Adding it to the stack view causes constraint conflicts.

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view

        // vertical stack view (full screen)
        let stackView = UIStackView()
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
            ])

        // view (100x100)
        let fixedSizeView = UIView()
        fixedSizeView.translatesAutoresizingMaskIntoConstraints = false
        fixedSizeView.backgroundColor = .blue

        stackView.addArrangedSubview(fixedSizeView)

        NSLayoutConstraint.activate([
            fixedSizeView.widthAnchor.constraint(equalToConstant: 100),
            fixedSizeView.heightAnchor.constraint(equalToConstant: 100),
            ])
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

like image 705
xta Avatar asked Mar 05 '23 09:03

xta


1 Answers

The main purpose of a UIStackView is to arrange its subviews.

So, you are constraining your stack view to "fill the screen" and then adding your "blue view" as an arranged subview ... at which point the stack view will "take over" the arrangement of the blue view.

Assuming you are using a stack view because you are planning on adding additional views, you can either allow the subviews to determine the stack view's frame (that is, don't constrain your stack view's width and/or height), or you need to change the stack view's .alignment and/or .distribution properties.

Here is a modification of your playground page to put the 100 x 100 blue view centered in the superview:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view

        // vertical stack view (full screen)
        let stackView = UIStackView()
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical

        view.addSubview(stackView)

//      NSLayoutConstraint.activate([
//          stackView.topAnchor.constraint(equalTo: view.topAnchor),
//          stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
//          stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
//          stackView.widthAnchor.constraint(equalTo: view.widthAnchor),
//          ])

        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            ])

        // view (100x100)
        let fixedSizeView = UIView()
        fixedSizeView.translatesAutoresizingMaskIntoConstraints = false
        fixedSizeView.backgroundColor = .blue

        stackView.addArrangedSubview(fixedSizeView)

        NSLayoutConstraint.activate([
            fixedSizeView.widthAnchor.constraint(equalToConstant: 100),
            fixedSizeView.heightAnchor.constraint(equalToConstant: 100),
            ])
    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()

and, here's a modification where two views - blue and red, each at 100 x 100 - get added to a stack view that is constrained to the top of the superview, with .alignment set to .center:

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    override func loadView() {
        let view = UIView()
        view.backgroundColor = .white
        self.view = view

        // vertical stack view (full screen)
        let stackView = UIStackView()
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .vertical
        stackView.alignment = .center

        view.addSubview(stackView)

        NSLayoutConstraint.activate([
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            ])

        // view (100x100)
        let fixedSizeBlueView = UIView()
        fixedSizeBlueView.translatesAutoresizingMaskIntoConstraints = false
        fixedSizeBlueView.backgroundColor = .blue

        stackView.addArrangedSubview(fixedSizeBlueView)

        NSLayoutConstraint.activate([
            fixedSizeBlueView.widthAnchor.constraint(equalToConstant: 100),
            fixedSizeBlueView.heightAnchor.constraint(equalToConstant: 100),
            ])

        // view (100x100)
        let fixedSizeRedView = UIView()
        fixedSizeRedView.translatesAutoresizingMaskIntoConstraints = false
        fixedSizeRedView.backgroundColor = .red

        stackView.addArrangedSubview(fixedSizeRedView)

        NSLayoutConstraint.activate([
            fixedSizeRedView.widthAnchor.constraint(equalToConstant: 100),
            fixedSizeRedView.heightAnchor.constraint(equalToConstant: 100),
            ])

    }
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
like image 120
DonMag Avatar answered Mar 06 '23 21:03

DonMag