Создание UIStackView с процентными значениями для распространения - PullRequest
0 голосов
/ 25 апреля 2020

Я пытаюсь создать пользовательскую функцию UIStackView, которая принимает значения в процентах для заполнения границ оси. Я изо всех сил пытаюсь реализовать это должным образом. Я попробовал несколько вещей, но это не работает должным образом. Есть ли способ, которым я могу это реализовать? Или уже есть способ сделать это встроенным?

extension UIStackView {

    func setHeightPercentageFill(values: [CGFloat]) {
        guard values.count == arrangedSubviews.count else { return }
        axis = .vertical
        distribution = .fillProportionally
        for i in 0..<arrangedSubviews.count {
            arrangedSubviews[i].heightAnchor.constraint(equalToConstant: bounds.height * values[i]).isActive = true
        }
    }
}

Желаемое использование (для установки высоты 20%, 40%, 30%, 10% для каждого из подпредставлений)

let stack = UIStackView(arrangedSubviews: [view1, view2, view3, view4])
addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.topAnchor.constraint(equalTo: topAnchor).isActive = true
stack.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
stack.rightAnchor.constraint(equalTo: rightAnchor).isActive = true

stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10])

Ответы [ 2 ]

1 голос
/ 25 апреля 2020

Ваш код не работает, потому что размер stackView зависит от его внутреннего содержимого subviews c. У вас есть сценарий курица / яйцо. Высота stackView зависит от высоты вложенных просмотров, а высота subview зависит от высоты stackView. Вы должны сообщить stackView, какую вы хотите, чтобы высота была.

Передача высоты в setHeightPercentageFill(values:) работает для меня:

extension UIStackView {
    func setHeightPercentageFill(values: [CGFloat], height: CGFloat) {
        guard values.count == arrangedSubviews.count else { return }
        axis = .vertical
        distribution = .fillProportionally
        for i in 0..<arrangedSubviews.count {
            arrangedSubviews[i].heightAnchor.constraint(equalToConstant: height * values[i]).isActive = true
        }
    }
}

Обратите внимание, что я определяю высоту контейнера как 128 в раскадровка. Вы должны убедиться, что любой вид, который вы ограничиваете свой стек, имеет фиксированную высоту.

class ViewController: UIViewController {

    lazy var view1: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .red
        return view
    }()

    lazy var view2: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .orange
        return view
    }()

    lazy var view3: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .blue
        return view
    }()

    lazy var view4: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .yellow
        return view
    }()

    @IBOutlet weak var container: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let stack = UIStackView(arrangedSubviews: [view1, view2, view3, view4])
        container.addSubview(stack)
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.topAnchor.constraint(equalTo: container.topAnchor).isActive = true
        stack.leftAnchor.constraint(equalTo: container.leftAnchor).isActive = true
        stack.bottomAnchor.constraint(equalTo: container.bottomAnchor).isActive = true
        stack.rightAnchor.constraint(equalTo: container.rightAnchor).isActive = true

        stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10], height: container.frame.size.height)
    }
}
0 голосов
/ 28 апреля 2020

Существуют различные подходы к этой задаче. Вы можете найти это немного более гибким ...

Вместо вызова забавы c для вычисления явных значений, вы можете установить ограничения высоты упорядоченных подпредставлений в процентах от высоты стека. view.

Пара преимуществ:

  • при изменении кадра стекового представления, возможно, при повороте устройства, нет необходимости что-либо пересчитывать
  • Вы можете контролировать общее количество высота представления стека, ограничивая его (с помощью .heightAnchor или .bottomAnchor) или , устанавливая высоту одного из упорядоченных подпредставлений
  • , если представление стека высота не постоянна, вы можете надежно установить ограничения в viewDidLoad(), не дожидаясь автоматического макета, чтобы определить высоту представления стека

Вот полный пример - он напечатает высоты из организованных Subviews для отладки консоли, как на viewDidAppear(), так и на viewWillTransition(to size:...) для подтверждения:

extension UIStackView {
    func setHeightPercentageFill(values: [CGFloat]) {
        guard values.count == arrangedSubviews.count else { return }
        axis = .vertical
        spacing = 0
        distribution = .fill
        for i in 0..<arrangedSubviews.count {
            arrangedSubviews[i].heightAnchor.constraint(equalTo: heightAnchor, multiplier: values[i]).isActive = true
        }
    }
}

class StackPercentViewController: UIViewController {

    let stack: UIStackView = {
        let v = UIStackView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        let colors: [UIColor] = [.red, .green, .blue, .yellow]

        colors.forEach {
            let v = UIView()
            v.backgroundColor = $0
            stack.addArrangedSubview(v)
        }

        view.addSubview(stack)

        // respect safe area
        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([

            // constrain stack view Top / Leading / Trailing at 40-pts
            stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
            stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
            stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -40.0),

        ])

        // change "if true" to "if false" to test giving the stack view a height
        if true {
            // either give one arranged subview a height constraint
            if let v = stack.arrangedSubviews.first {
                v.heightAnchor.constraint(equalToConstant: 20.0).isActive = true
            }
        } else {
            // or, constrain the stack view's height (.heightAnchor or .bottomAnchor)
            stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -40.0).isActive = true
            //stack.heightAnchor.constraint(equalToConstant: 200.0).isActive = true
        }

        stack.setHeightPercentageFill(values: [0.20, 0.40, 0.30, 0.10])

    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        checkHeights()
    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        coordinator.animate(alongsideTransition: nil, completion: {
            _ in
            self.checkHeights()
        })
    }

    func checkHeights() {
        // print heights of arrangedSubviews to debug console
        stack.arrangedSubviews.forEach {
            print($0.frame.size.height)
        }
        print() // blank line in console
    }

}

Results ...

With first subview hei ght ограничено до 20:

image image

с стека высота ограничена до 200:

image image

с вид стека ограничено 40 пунктами со всех 4 сторон:

image image

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...