Изменение оси stackView, приводящее к ошибке макета - PullRequest
0 голосов
/ 02 ноября 2018

У меня есть UIStackView, который меняет ось в зависимости от ее ширины. Включает только два UView с. Существует очень простая настройка (можно скопировать / вставить в проект Xcode по умолчанию):

class ViewController: UIViewController {

    enum DisplayMode {
        case regular
        case compact
    }

    private let stackView = UIStackView(frame: .zero)
    private let firstView = UIView(frame: .zero)
    private let secondView = UIView(frame: .zero)
    private var firstViewWidth: NSLayoutConstraint?
    private var secondViewWidth: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()

        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            ])

        firstView.backgroundColor = .red
        stackView.addArrangedSubview(firstView)
        firstView.heightAnchor.constraint(equalToConstant: 80).isActive = true
        firstViewWidth = firstView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1/3)
        firstViewWidth?.isActive = true

        secondView.backgroundColor = .black
        stackView.addArrangedSubview(secondView)
        secondView.heightAnchor.constraint(equalToConstant: 80).isActive = true
        secondViewWidth = secondView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 2/3)
        secondViewWidth?.isActive = true
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        if view.bounds.width < 400 {
            switchMode(.compact)
        } else {
            switchMode(.regular)
        }
    }
}

private extension ViewController {

    func switchMode(_ mode: DisplayMode) {
        switch mode {
        case .regular:
            stackView.axis = .horizontal
            firstViewWidth?.isActive = true
            secondViewWidth?.isActive = true
        case .compact:
            stackView.axis = .vertical
            firstViewWidth?.isActive = false
            secondViewWidth?.isActive = false
        }
    }
}

Работает просто отлично, но дает бессмысленную ошибку компоновки (все ограничения в выводе кажутся нормальными) при переходе от компактного к обычному:

(
    "<NSLayoutConstraint:0x6000002c1c20 UIView:0x7fbd4dc149a0.width == 0.333333*UIStackView:0x7fbd4df02430.width   (active)>",
    "<NSLayoutConstraint:0x6000002c2170 UIView:0x7fbd4dc14d90.width == 0.666667*UIStackView:0x7fbd4df02430.width   (active)>",
    "<NSLayoutConstraint:0x6000002f2080 'UISV-canvas-connection' UIStackView:0x7fbd4df02430.leading == UIView:0x7fbd4dc149a0.leading   (active)>",
    "<NSLayoutConstraint:0x6000002f1c70 'UISV-canvas-connection' H:[UIView:0x7fbd4dc14d90]-(0)-|   (active, names: '|':UIStackView:0x7fbd4df02430 )>",
    "<NSLayoutConstraint:0x6000002f05f0 'UISV-spacing' H:[UIView:0x7fbd4dc149a0]-(0)-[UIView:0x7fbd4dc14d90]   (active)>"
)

Может кто-нибудь объяснить, почему это происходит? Является ли динамическая ось виновником?

1 Ответ

0 голосов
/ 06 ноября 2018

Прежде всего viewDidLayoutSubviews не является хорошим вариантом для обработки изменений ориентации. Рекомендуемый метод Apple - viewWillTransitionToSize. Для более подробной информации: Каков «правильный» способ обработки изменений ориентации в iOS 8?

Когда вы в первый раз запускаете свой код, вид стека отображается с вертикальным распределением, случаи как в первом, так и во втором видах имеют следующее ограничение

FirstView ->

         Leading =  StackView Leading,
         Trailing =  StackView Trailing,
         Top      = StackView Top,
         height   =   80

SecondView ->

            Leading =  StackView Leading    ,
              Top      = FirstView Top,
              Trailing =  StackView Trailing,
              height   =   80

При повороте устройства из книжного в альбомный вид стека отображается с горизонтальным распределением и активируется следующая зависимость.

FirstView.Width = StackView.Width * 1/3

Результирующий конфликт FirstView с предыдущим ограничением (ведущий StackView, конечный StackView). Потому что теперь у вас есть два ограничения ширины

  1. (FirstView.leading = StackView Leading & FirstView.trailing = StackView Trailing) == (FirstView Width == StackView.width)
  2. FirstView.Width = StackView.Width * 1/3

Почему StackView не удаляет одно из конфликтующих ограничений автоматически?

-> потому что оба ограничения имеют высокий приоритет (1000). Если мы просто изменим firstViewWidth на более низкий (999) и удалим ограничение secondViewWidth, чем ваше решение, будет работать

Попробуйте заменить ваш код следующим кодом

   import UIKit

class ViewController: UIViewController {

    enum DisplayMode {
        case regular
        case compact
    }

    private let stackView = UIStackView(frame: .zero)
    private let firstView = UIView(frame: .zero)
    private let secondView = UIView(frame: .zero)
    private var firstViewWidth: NSLayoutConstraint?
    private var secondViewWidth: NSLayoutConstraint?

    override func viewDidLoad() {
        super.viewDidLoad()

        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        NSLayoutConstraint.activate([
            stackView.topAnchor.constraint(equalTo: view.topAnchor),
            stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            ])

        firstView.backgroundColor = .red
        stackView.addArrangedSubview(firstView)
        firstView.heightAnchor.constraint(equalToConstant: 80).isActive = true
        firstViewWidth = firstView.widthAnchor.constraint(equalTo: stackView.widthAnchor, multiplier: 1/3)
        firstViewWidth?.priority = UILayoutPriority(rawValue: 999)
        firstViewWidth?.isActive = true

        secondView.backgroundColor = .black
        stackView.addArrangedSubview(secondView)
        secondView.heightAnchor.constraint(equalToConstant: 80).isActive = true


        if self.view.bounds.width < 400 {
            self.switchMode(.compact)
        } else {
            self.switchMode(.regular)
        }
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

    }

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)


        coordinator.animate(alongsideTransition: { (context) in

        }) { (context) in
            if self.view.bounds.width < 400 {
                self.switchMode(.compact)
            } else {
                self.switchMode(.regular)
            }
        }

    }

}

private extension ViewController {

    func switchMode(_ mode: DisplayMode) {
        switch mode {
        case .regular:
            stackView.axis = .horizontal
            firstViewWidth?.isActive = true

        case .compact:
            stackView.axis = .vertical
            firstViewWidth?.isActive = false
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...