iosswiftipaduiscreenviewwilltransitiontosize

iOS - Wrong UIScreen bounds in viewWillTransition for iPad


I have to check if my device has changed orientation in iOS 8+.

My approach is:

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

    let isLand = UIScreen.main.bounds.width > UIScreen.main.bounds.height

    coordinator.animate(alongsideTransition: nil) { _ in
        let isLand2 = UIScreen.main.bounds.width > UIScreen.main.bounds.height


        print("\(isLand) -> \(isLand2)")
    }
}

it works fine in iPhone but in iPad isLand has already the new value which should be after the orientation completion, so:

Portrait > Landscape: true -> true

Landscape > Portrait: false -> false

According to the documentation the bounds should change with the orientation so it should have a before/after bounds, shouldn't it?

UIScreen main bounds:

This rectangle is specified in the current coordinate space, which takes into account any interface rotations in effect for the device. Therefore, the value of this property may change when the device rotates between portrait and landscape orientations.

Whereas it works fine both iPhone and iPad if I use the bounds of the current root view controller like this:

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

    let isLand = UIApplication.shared.keyWindow!.rootViewController!.view.bounds.width > UIApplication.shared.keyWindow!.rootViewController!.view.bounds.height

    coordinator.animate(alongsideTransition: nil) { _ in
        let isLand2 = UIApplication.shared.keyWindow!.rootViewController!.view.bounds.width > UIApplication.shared.keyWindow!.rootViewController!.view.bounds.height


        print("\(isLand) -> \(isLand2)")
    }
}

Portrait > Landscape: false -> true

Landscape > Portrait: true -> false


Solution

  • You should try using the containerView of the coordinator context instead.

    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
    
        let isLand = coordinator.containerView.bounds.width > coordinator.containerView.bounds.height
    
        coordinator.animate(alongsideTransition: nil) { _ in
            let isLand2 = coordinator.containerView.bounds.width > coordinator.containerView.bounds.height
    
            print("\(isLand) -> \(isLand2)")
        }
    
    }
    

    If you want to get further information regarding the transition you can use the func view(forKey: UITransitionContextViewKey) and func viewController(forKey: UITransitionContextViewControllerKey) using the .from key.