Как в UIViewController отследить поворот экрана или изменение размеров?

Ответ

Основной метод — viewWillTransition(to:with:). Он предпочтительнее уведомлений, так как работает универсально на всех устройствах (включая iPad с многозадачностью).

Использование viewWillTransition(to:with:):

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

    // size — новые размеры view контроллера
    let isLandscape = size.width > size.height

    // coordinator позволяет встроить свою анимацию в системную
    coordinator.animate(alongsideTransition: { [weak self] _ in
        // Анимируем изменения layout при повороте
        self?.updateLayoutForOrientation(isLandscape: isLandscape)
    }) { [weak self] _ in
        // Действия после завершения анимации поворота
        self?.logOrientationChange()
    }
}

private func updateLayoutForOrientation(isLandscape: Bool) {
    // Обновляем constraints или frames
    someConstraint.constant = isLandscape ? 100 : 50
    view.layoutIfNeeded() // Применяем изменения с анимацией
}

Альтернатива — уведомление (менее предпочтительно):

// Подписка (например, в viewDidLoad)
NotificationCenter.default.addObserver(
    self,
    selector: #selector(orientationDidChange),
    name: UIDevice.orientationDidChangeNotification,
    object: nil
)

@objc private func orientationDidChange() {
    let orientation = UIDevice.current.orientation
    // Обработка...
}

Почему viewWillTransition лучше?

  1. Универсальность: Срабатывает при любом изменении размера view (поворот, split view на iPad, изменение window size).
  2. Координация анимаций: UIViewControllerTransitionCoordinator позволяет синхронизировать ваши анимации с системными.
  3. Контроль: Вы получаете точные новые размеры (CGSize), а не абстрактную "ориентацию устройства".

Дополнительно: Для реагирования на изменение размеров, вызванное Auto Layout, также можно переопределить viewWillLayoutSubviews() или traitCollectionDidChange(_:) (для изменений trait collections, таких как size class).

Ответ 18+ 🔞

А, слушай, вот этот момент с поворотом экрана — это вообще отдельная песня, блядь. Все эти новички лезут в NotificationCenter, подписываются на ориентацию устройства, а потом на iPad у них всё ебётся, потому что split view или slide over. Пиздец, а не подход.

Так вот, есть у нас метод viewWillTransition(to:with:). Вот это, блядь, наш бро, наш чёрный властелин. Он работает везде, сука, как швейцарский нож: и на айфоне при повороте, и на айпаде, когда ты там окно резиновое таскаешь. Универсальный солдат, ёпта.

Как его, сука, применять:

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

    // size — это вот новые габариты твоего вью, уже по факту, блядь
    let isLandscape = size.width > size.height // Альбомная? Да по ширине смотри!

    // А coordinator — это вообще магия, он позволяет твою анимацию в системную впихнуть
    coordinator.animate(alongsideTransition: { [weak self] _ in
        // Тут всё, что должно анимироваться плавненько
        self?.updateLayoutForOrientation(isLandscape: isLandscape)
    }) { [weak self] _ in
        // А это уже после того, как всё завертелось
        self?.logOrientationChange() // Ну, логируй там, если охота
    }
}

private func updateLayoutForOrientation(isLandscape: Bool) {
    // Ну тут уже твоё творчество: констрейнты двигай, фреймы меняй
    someConstraint.constant = isLandscape ? 100 : 50
    view.layoutIfNeeded() // Без этого — нихуя не анимируется, запомни
}

А есть же ещё старый способ, через уведомления (но он так себе, честно):

// Вешаем слушателя (например, в viewDidLoad)
NotificationCenter.default.addObserver(
    self,
    selector: #selector(orientationDidChange),
    name: UIDevice.orientationDidChangeNotification,
    object: nil
)

@objc private func orientationDidChange() {
    let orientation = UIDevice.current.orientation
    // И тут начинается геморрой с обработкой .faceUp, .faceDown...
}

Так почему же viewWillTransition — это охуенно, а уведомления — говно?

  1. Универсальность, блядь! Тебе приходит просто новый размер вью. Похуй, как он изменился: поворот, многозадачность на айпаде, окно поменяли — всё одно.
  2. Координатор анимаций! Ты можешь свою анимацию встроить в системную, и будет красиво, а не дёргано. Без него — волнение ебать, всё моргает.
  3. Контроль полный! Тебе дают конкретные цифры — CGSize. Не надо гадать, что там с устройством, может оно вообще лежит экраном вверх.

Ну и на закуску: Если тебе надо просто на изменение размеров от Auto Layout среагировать — есть viewWillLayoutSubviews(). А если тебе важны size classes (компактный, регулярный) — то traitCollectionDidChange(_:). Но для поворота — первый метод, царь и бог, запомни это, чтобы не выдумывать хуйню.