Ошибка безопасной зоны push-перехода UINavigationController в компактном UISplitViewController на iOS 26IOS

Программируем под IOS
Ответить
Anonymous
 Ошибка безопасной зоны push-перехода UINavigationController в компактном UISplitViewController на iOS 26

Сообщение Anonymous »

Раньше представление «основной-подробный» со стеком навигации реализовывалось с помощью UISplitViewController с двумя контроллерами навигации.
В компактной компоновке эти 2 контроллера волшебным образом «сворачиваются» в один с помощью UISplitViewController, и мы можем сформировать базовый стек навигации: мастер -> деталь -> поддеталь -> подподробность.
Эта настройка работала задолго до iOS 26.
В iOS 26 push-переход навигации работает с ошибками на подуровнях из-за двух отдельных проблем с макетом:
  • Когда переход начинается, макет контроллера принудительного представления не обновляется. Начальный размер (до обновления) — .ноль, и в результате мы видим, как он анимируется до окончательного размера и положения. Посмотрите, как при записи экрана синий вид расширяется с нуля.
  • Когда начинается переход, руководство по расположению безопасной зоны не обновляется. Начальный размер безопасной области равен нулю. Если на него опирается макет контроллера представления, мы видим, как он анимируется, чтобы соответствовать размеру и положению безопасной области. Посмотрите, как при записи экрана розовый вид уменьшается по сравнению с полноэкранным размером.
Эта анимация не ожидается! Макет контроллера принудительного представления должен быть исправлен ограничениями во время принудительного перехода (как это было до iOS 26).
Ошибку (1) можно обойти, добавив вызов LayoutIfNeeded() внутри viewDidLoad() (см. SubSubDetailViewController ниже).
Но (2) сложно обойти.
Что пробовали:
  • Замените настройку viewControllers на более новый API iOS 14, это не дало эффекта — те же ошибки:

    Код: Выделить всё

    super.init(style: .doubleColumn)
    self.setViewController(MasterViewController(), for: .primary)
    self.setViewController(DetailViewController(), for: .secondary)
    
  • Используйте один UINavigationController, разворачивая либо MasterViewController, либо DetailViewController от наличия UINavigationController. Это приводит к нежелательным последствиям в некомпактном режиме «основной-подробный» (отображается на iPad и больших iPhone).
  • Использование верхнего ограничения, связанного с нижней привязкой панели навигации, вместо SafeAreaLayoutGuide не учитывает области вырезов на левом и правом краях (на телефонах с альбомной ориентацией).
  • Наличие одной специальной версии кода на маленьких iPhone и другой версии кода на других устройствах на модели устройства нежелательно, поскольку многозадачность iPad может использоваться для динамического переключения в режим компактной компоновки.
Чтобы воспроизвести, создайте проект iOS в Xcode 26, вставьте этот код и запустите на небольшом симуляторе iPhone iOS 26.0:

Код: Выделить всё

import UIKit

class ViewController: UISplitViewController {
required init?(coder: NSCoder) {
super.init(coder: coder)
self.viewControllers = [
UINavigationController(rootViewController: MasterViewController()),
UINavigationController(rootViewController: DetailViewController()),
]
}

class MasterViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "master"
self.view.backgroundColor = .red
}
}

class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "detail"
self.view.backgroundColor = .orange
addBox(to: self.view).backgroundColor = .yellow
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { _ in
assert(self.navigationController != nil)
self.navigationController?.pushViewController(SubDetailViewController(), animated: true)
}
}
}

class SubDetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "sub detail"
self.view.backgroundColor = .green
addBox(to: self.view).backgroundColor = .cyan
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { _ in
assert(self.navigationController != nil)
self.navigationController?.pushViewController(SubSubDetailViewController(), animated: true)
}
}
}

class SubSubDetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "sub sub detail"
self.view.backgroundColor = .blue
addBox(to: self.view).backgroundColor = .magenta
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.layoutIfNeeded()
}
}

func addBox(to parent: UIView) -> UIView {
let box = UIView()
box.backgroundColor = .yellow
box.translatesAutoresizingMaskIntoConstraints = false
parent.addSubview(box)

let container = parent.safeAreaLayoutGuide
NSLayoutConstraint.activate([
NSLayoutConstraint(item: box, attribute: .top, relatedBy: .equal, toItem: container, attribute: .top, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .bottom, relatedBy: .equal, toItem: container, attribute: .bottom, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .leading, relatedBy: .equal, toItem: container, attribute: .leading, multiplier: 1, constant: 0),
NSLayoutConstraint(item: box, attribute: .trailing, relatedBy: .equal, toItem: container, attribute: .trailing, multiplier: 1, constant: 0),
])

return box
}
Изображение



Подробнее здесь: https://stackoverflow.com/questions/798 ... tviewcontr
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»