Прежде всего 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). Потому что теперь у вас есть два ограничения ширины
- (FirstView.leading = StackView Leading & FirstView.trailing = StackView Trailing) == (FirstView Width == StackView.width)
- 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
}
}
}