简体   繁体   中英

Autolayout adjust height of UIbutton within given range based on available screen size

I am trying to use Autolayout (programmatically) to produce the following scene. As the picture indicates, the button's height cannot be greater then 56 and less than 44 in any screen size. The top content view, has its own size based on its internal contents, but its top anchor has to have a minimum toppadding of 8. It can be greater than 8. The padding between the buttons and the content view is strict 8pts. and the bottom padding with the bottom anchor is a strict 12. The only thing that I can play with is the button's height to adjust all the contents in a small screen.

在此处输入图像描述

Tries 1) I tried putting all the buttons in a stack view, and have put both the height constraint to each of the buttons.

button.heightAnchor.constraint(lessThanOrEqualToConstant: 56).isActive = true
button.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true

but the button height never increases more than 44. Now, it works correctly, when I set the top constraint between main view and top view with a strict 8

contentView.topAnchor.constraint(equalTo: topAnchor, constant: 8).isActive = true

and not a

contentView.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: 8).isActive = true

But that won't work with larger screens as the top view needs to be closer to the buttons then the top view.

2) When I don't use the stack view, and set both the height constraint to each button, no matter which ever device it is, it never increases more than 44. If I remove the greaterthen 44 constraint, then the size of the button reduces to 36. I tried setting priority to topmost greaterthen 8 constraint as well as height of the button constraints, as well as setting aspect ratio constraint between the content view and the top button, and then setting all the other button height to the top button, but then again, all just sticks up to 44.

Please advise or provide any solution how to tackle this.

This is really fairly straight-forward if you "talk it out" to yourself.

The easy parts:

  • Stack View is constrained
    • 8-pts Leading and Trailing
    • 16-pts from the Bottom
    • >= 8-pts from the top
  • each button gets height constraints of
    • >= 44
    • <= 56
  • height of top "dynamic" view determined by its content

The "tricky" parts:

  • add another Top constraint to the Stack View
    • = 8-pts with .priority =.defaultHigh
    • that tells auto-layout to "pull the top of the stack view up" unless it is "pulled down" (by the max-heights of the buttons)
  • constrain the height of buttons 2, 3 & 4 equal to the height of button 1

Here is a full example (all code, no Storyboard connections). The "top" view will start at 240-pts tall... each time you tap the first button the top view's height will increase by 20, to a max of 400. The top view will also display the new heights:

class TestViewController: UIViewController {

    let stackView: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.axis = .vertical
        v.spacing = 8
        return v
    }()

    let dynaView: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = .yellow
        v.textAlignment = .center
        v.numberOfLines = 0
        return v
    }()

    var dynaViewHeightConstraint: NSLayoutConstraint!

    var theButtons: [UIButton] = [UIButton]()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(stackView)

        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // stackView constrained 8-pts Leading and Trailing
            // 16-pts from bottom
            // >= 8-pts from top
            stackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 8.0),
            stackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -8.0),
            stackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -16.0),
            stackView.topAnchor.constraint(greaterThanOrEqualTo: g.topAnchor, constant: 8.0),

        ])

        // we need a stackView TOP anchor = 8 with priorty 750
        //  this will "pull" the top up as far as it can, without violating the
        //  button height constraints
        let stackTop = stackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 8.0)
        stackTop.priority = .defaultHigh
        stackTop.isActive = true

        for i in 1...4 {
            let b = UIButton()
            b.translatesAutoresizingMaskIntoConstraints = false
            b.setTitle("Button \(i)", for: [])
            b.backgroundColor = .red
            NSLayoutConstraint.activate([
                // button heights are min: 44 max: 56
                b.heightAnchor.constraint(lessThanOrEqualToConstant: 56.0),
                b.heightAnchor.constraint(greaterThanOrEqualToConstant: 44.0),
            ])
            theButtons.append(b)
        }

        // let's start with the dynaView height at 240
        dynaViewHeightConstraint = dynaView.heightAnchor.constraint(equalToConstant: 240.0)
        dynaViewHeightConstraint.isActive = true

        // add dynsView to the stack
        stackView.addArrangedSubview(dynaView)

        guard let firstButton = theButtons.first else {
            fatalError("Something is wrong with my setup!")
        }

        // add each button to the stack,
        //  setting its height equal to the first button's height
        theButtons.forEach { b in
            stackView.addArrangedSubview(b)
            if b != theButtons.first {
                b.heightAnchor.constraint(equalTo: firstButton.heightAnchor).isActive = true
            }
        }

        // tapping the first button will increae dynaView's height
        firstButton.addTarget(self, action: #selector(self.didTap(_:)), for: .touchUpInside)

    }

    override func viewDidAppear(_ animated: Bool) {
        showStats()
    }

    func showStats() -> Void {
        var s = "DynaView Height: \(dynaView.frame.height)"
        for i in 0..<theButtons.count {
            let b = theButtons[i]
            s += "\n"
            s += "Button \(i + 1) Height: \(b.frame.height)"
        }
        dynaView.text = s
    }

    @objc
    func didTap(_ sender: Any) -> Void {
        let h = min(dynaViewHeightConstraint.constant + 20, 400)
        dynaViewHeightConstraint.constant = h
        DispatchQueue.main.async {
            self.showStats()
        }
    }

}

Here's how it looks on an iPhone 8...

在此处输入图像描述

在此处输入图像描述

and on an iPhone 11 with top-view height at 400 max:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM