How to get the screen height after orientation change?
Asked Answered
G

4

5

I'm getting some strange results when querying UIScreen.main.bounds.height after an orientation change. Maybe this isn't the correct way to do it.

I have an observer that listens for the NSNotification.Name.UIDeviceOrientationDidChange event:

NotificationCenter.default.addObserver(self, selector: #selector(self.orientationChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)

This calls a function that sets a constraint to 75% of the new screen height. This works fine on iPhone but iPad returns the wrong screen height.

If the iPad is in landscape orientation UIScreen.main.bounds.height will return a value equal to the height in portrait orientation and vice versa.

func orientationChange() {
    // This will print the correct value on iPhone and iPad.
    if UIDevice.current.orientation.isLandscape {
        print("Landscape")
    } else {
        print("Portrait")
    }

    let screenSize = UIScreen.main.bounds
    let screenHeight = screenSize.height
    self.searchViewHeightConstraint.constant = screenHeight * 0.75

    // Correct value on iPhone. Incorrect on iPad.
    print("screenHeight: '\(screenHeight)'") 

    UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
        self.searchView.superview?.layoutIfNeeded()
    }, completion: nil)
}

I've also come across the viewWilltransition method of monitoring orientation change but this behaves in the exact opposite way to the method above. ie. the height is correct on iPad but incorrect on iPhone:

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
    let screenSize = UIScreen.main.bounds
    let screenHeight = screenSize.height
    self.searchViewHeightConstraint.constant = screenHeight * 0.75

    // Correct value on iPad. Incorrect on iPhone.
    print("screenHeight: '\(screenHeight)'") 
}

What is the reason for this inconsistent behaviour between iPhone and iPad and is using NotificationCenter the correct approach to monitoring orientation change?

Glycoprotein answered 9/10, 2017 at 14:46 Comment(3)
Are you getting screenHeight value bigger or lesser?Adnopoz
Stop using the screensize = UIScreen.main.bounds and use size instead, it's given to you in the method. you can do size.height * 0.75Aguayo
Also, if you are using constraints and you have your searchView equal to the height of your view, then on that same heighViewConstraint you can set the modifier to 0.75 if it is always going to be that size. Then what you will get is on rotation the view will update itself and you wont have to do any logic at all.Aguayo
A
4

What you should be using is viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator). This will give you the size and is WAY more reliable than using a notification.

Also, if you wanted to do animations, inside of the UIViewControllerTransitionCoordinator you can leverage the method animate(alongsideTransition animation: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)?, completion: ((UIViewControllerTransitionCoordinatorContext) -> Swift.Void)? = nil) -> Bool

Aguayo answered 9/10, 2017 at 14:52 Comment(4)
I have already tried this and it behaves in the exact opposite of the code in my question. ie. it reports the correct dimension on iPad but the wrong dimensions on iPhone.Glycoprotein
Have you tried using the animate(alongsideTransition...)? I have used this and in the completion of that method everything seemed to fall into place.Aguayo
So when I created a test project and printed out the size for iPhone I get: landscape (736.0, 414.0) and portrait (414.0, 736.0) and then for iPad I am getting landscape (1024.0, 768.0) and portrait (768.0, 1024.0). These were iPad Air 2 and iPhone 8.Aguayo
You are correct about viewWillTransition. I should have been using the size parameter rather that querying the UIScreen bounds. Thanks a lot.Glycoprotein
D
3

You need to use :

dispatch_async(dispatch_get_main_queue()) { 
println("h:\(view.bounds.size.height)")
println("w:\(view.frame.size.width)")
         }

// All you need to write code under dispatch_async

This code will be executed after the rotation was completed (in the main queue) and the new sizes are available.

Disadvantageous answered 1/3, 2019 at 8:30 Comment(1)
Amen, thank you! DispatchQueue.main.async { print("Hello") } in swift 3/4Assemblyman
B
1

This works for me in Swift 2.3 for both iPad and iPhone.

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    print(size.height)

  if UIDevice.currentDevice().orientation.isLandscape {
     print("Landscape")
     print(size.height)

  } 
 else {
      print("Portrait")
      print(size.height)
     }

}
Blowup answered 9/10, 2017 at 15:13 Comment(0)
B
-1

You can use delay and you will receive recalculated height

DispatchQueue.main.asyncAfter(deadline: .now() + 0.7, execute: {
        // your code hear
})
Bezique answered 3/1 at 13:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.