UIScreen.main.bounds Returning Wrong Value During iPhone Simulation

Hi. I have a short program that adds two buttons in a single view and prints the screen bounds, but those bounds aren't right with iPhone 7Plus simulator after rotating device so the two button subviews aren't placed correctly. This issue does not seem to happen when using iPad simulators. I know I can just simply use the size input from viewWillTransition() function but that doesn't tell me why it isn't showing the correct screen bounds rectangle coordinates on just the iPhone simulator. For some reason it thinks device is in whatever mode it was in when I start up the app and rotate it. Like after I start up in Portrait and change to Landscape the code still prints "Device is in portrait mode" and prints the bounds as being in Landscape mode. I am using XCode 13.2.1 and iOS 15.2 for testing. All possible device orientations are in the info.plist for both iPhone and IPad. Here is the code I am testing with:

import UIKit



class TestViewController: UIViewController {

    

    lazy var pickButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: 0,

                                            width: 100,

                                            height: 100))

        button.setTitle("Pick", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showPicker), for: .touchUpInside)

        return button

    }()

    

    lazy var resultsButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: UIScreen.main.bounds.height - 100,

                                            width: UIScreen.main.bounds.width,

                                            height: 100))

        button.setTitle("Show selected", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showResults), for: .touchUpInside)

        return button

    }()



    override func viewDidLoad() {

        super.viewDidLoad()

        

        determineMyDeviceOrientation()

        

        /*let device = UIDevice.current

        device.beginGeneratingDeviceOrientationNotifications()

        let notificationCenter = NotificationCenter.default

        notificationCenter.addObserver(self, selector: #selector(deviceOrientationChanged),

            name: Notification.Name("UIDeviceOrientationDidChangeNotification"),

            object: nil)*/

        

        

        

        view.addSubview(resultsButton)

        view.addSubview(pickButton)

        pickButton.center = view.center

    }

    

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

        

        print ("size \(size)")

        print (UIScreen.main.bounds)

        

        determineMyDeviceOrientation()

        

        pickButton.center = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/2)

        resultsButton.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 100, width: UIScreen.main.bounds.width, height: 100)

        

        //pickButton.center = CGPoint(x: size.width/2, y: size.height/2)

        //resultsButton.frame = CGRect(x: 0, y: size.height - 100, width: size.width, height: 100)

    }

    

    func determineMyDeviceOrientation() {

          

          let i = UIApplication.shared.windows.first!.windowScene!.interfaceOrientation.isLandscape

          print (i)

          

          if i {

              print("Device is in landscape mode")

          } else {

              print("Device is in portrait mode")

          }

      }



    @objc func deviceOrientationChanged() {

        print("Orientation changed")

        inspectDeviceOrientation()

    }



    func inspectDeviceOrientation() {

        let orientation = UIDevice.current.orientation

        switch UIDevice.current.orientation {

        case .portrait:

            print("portrait")

        case .landscapeLeft:

            print("landscapeLeft")

        case .landscapeRight:

            print("landscapeRight")

        case .portraitUpsideDown:

            print("portraitUpsideDown")

        case .faceUp:

            print("faceUp")

        case .faceDown:

            print("faceDown")

        default: // .unknown

            print("unknown")

        }

        if orientation.isPortrait { print("isPortrait") }

        if orientation.isLandscape { print("isLandscape") }

        if orientation.isFlat { print("isFlat") }

    }

}
Answered by zewkini in 705426022

I figured out a solution. I added a completion block because that is where the view gets properly refresehed, using the coordinator object that is passed into the viewWillTransition() Also added a spring animation in UIView.animate() block, just for fun.

class TestViewController: UIViewController {

    

    lazy var pickButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: 0,

                                            width: 100,

                                            height: 100))

        button.setTitle("Pick", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showPicker), for: .touchUpInside)

        return button

    }()

    

    lazy var resultsButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: UIScreen.main.bounds.height - 100,

                                            width: UIScreen.main.bounds.width,

                                            height: 100))

        button.setTitle("Show selected", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showResults), for: .touchUpInside)

        return button

    }()



    override func viewDidLoad() {

        super.viewDidLoad()

        

        determineMyDeviceOrientation()

        

        let device = UIDevice.current

        device.beginGeneratingDeviceOrientationNotifications()

        let notificationCenter = NotificationCenter.default

        notificationCenter.addObserver(self, selector: #selector(deviceOrientationChanged),

            name: Notification.Name("UIDeviceOrientationDidChangeNotification"),

            object: nil)

        

        

        

        view.addSubview(resultsButton)

        view.addSubview(pickButton)

        pickButton.center = view.center

    }

    

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

        

        coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in

            

            UIView.animate(withDuration: 50.0, animations: {

                self.pickButton.center = CGPoint(x: self.view.center.x, y: self.view.center.y - 50)

                self.resultsButton.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 100, width: UIScreen.main.bounds.width, height: 100)

            })

            

            let orient = UIDevice.current.orientation



              switch orient {

              case .portrait:

                  print("Portrait")

              case .landscapeLeft,.landscapeRight :

                  print("Landscape")

              default:

                  print("Anything But Portrait")

              }



              }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in

                  //refresh view once rotation is completed not in will transition as it returns incorrect frame size.Refresh here

                  

                  UIView.animate(withDuration: 2.0, delay: 0.2, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: {

                      

                      self.pickButton.center = CGPoint(x: self.view.center.x, y: self.view.center.y)

                      

                  }, completion: nil)

              

                  print ("finished")

                  print ("size \(size)")

                  print (UIScreen.main.bounds)

              })

        

        super.viewWillTransition(to: size, with: coordinator)

        

    }

    

    func determineMyDeviceOrientation() {

        

        let i = UIDevice.current.orientation.isLandscape

          print (i)

          

          if i {

              print("Device is in landscape mode")

          } else {

              print("Device is in portrait mode")

          }

      }



    @objc func deviceOrientationChanged() {

        print("Orientation changed")

        inspectDeviceOrientation()

    }



    func inspectDeviceOrientation() {

        let orientation = UIDevice.current.orientation

        switch UIDevice.current.orientation {

        case .portrait:

            print("portrait")

        case .landscapeLeft:

            print("landscapeLeft")

        case .landscapeRight:

            print("landscapeRight")

        case .portraitUpsideDown:

            print("portraitUpsideDown")

        case .faceUp:

            print("faceUp")

        case .faceDown:

            print("faceDown")

        default: // .unknown

            print("unknown")

        }

        if orientation.isPortrait { print("isPortrait") }

        if orientation.isLandscape { print("isLandscape") }

        if orientation.isFlat { print("isFlat") }

        

    }

}
Accepted Answer

I figured out a solution. I added a completion block because that is where the view gets properly refresehed, using the coordinator object that is passed into the viewWillTransition() Also added a spring animation in UIView.animate() block, just for fun.

class TestViewController: UIViewController {

    

    lazy var pickButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: 0,

                                            width: 100,

                                            height: 100))

        button.setTitle("Pick", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showPicker), for: .touchUpInside)

        return button

    }()

    

    lazy var resultsButton : UIButton = {

        let button = UIButton(frame: CGRect(x: 0,

                                            y: UIScreen.main.bounds.height - 100,

                                            width: UIScreen.main.bounds.width,

                                            height: 100))

        button.setTitle("Show selected", for: .normal)

        button.setTitleColor(.black, for: .normal)

        //button.addTarget(self, action: #selector(showResults), for: .touchUpInside)

        return button

    }()



    override func viewDidLoad() {

        super.viewDidLoad()

        

        determineMyDeviceOrientation()

        

        let device = UIDevice.current

        device.beginGeneratingDeviceOrientationNotifications()

        let notificationCenter = NotificationCenter.default

        notificationCenter.addObserver(self, selector: #selector(deviceOrientationChanged),

            name: Notification.Name("UIDeviceOrientationDidChangeNotification"),

            object: nil)

        

        

        

        view.addSubview(resultsButton)

        view.addSubview(pickButton)

        pickButton.center = view.center

    }

    

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

        

        coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in

            

            UIView.animate(withDuration: 50.0, animations: {

                self.pickButton.center = CGPoint(x: self.view.center.x, y: self.view.center.y - 50)

                self.resultsButton.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 100, width: UIScreen.main.bounds.width, height: 100)

            })

            

            let orient = UIDevice.current.orientation



              switch orient {

              case .portrait:

                  print("Portrait")

              case .landscapeLeft,.landscapeRight :

                  print("Landscape")

              default:

                  print("Anything But Portrait")

              }



              }, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in

                  //refresh view once rotation is completed not in will transition as it returns incorrect frame size.Refresh here

                  

                  UIView.animate(withDuration: 2.0, delay: 0.2, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: [], animations: {

                      

                      self.pickButton.center = CGPoint(x: self.view.center.x, y: self.view.center.y)

                      

                  }, completion: nil)

              

                  print ("finished")

                  print ("size \(size)")

                  print (UIScreen.main.bounds)

              })

        

        super.viewWillTransition(to: size, with: coordinator)

        

    }

    

    func determineMyDeviceOrientation() {

        

        let i = UIDevice.current.orientation.isLandscape

          print (i)

          

          if i {

              print("Device is in landscape mode")

          } else {

              print("Device is in portrait mode")

          }

      }



    @objc func deviceOrientationChanged() {

        print("Orientation changed")

        inspectDeviceOrientation()

    }



    func inspectDeviceOrientation() {

        let orientation = UIDevice.current.orientation

        switch UIDevice.current.orientation {

        case .portrait:

            print("portrait")

        case .landscapeLeft:

            print("landscapeLeft")

        case .landscapeRight:

            print("landscapeRight")

        case .portraitUpsideDown:

            print("portraitUpsideDown")

        case .faceUp:

            print("faceUp")

        case .faceDown:

            print("faceDown")

        default: // .unknown

            print("unknown")

        }

        if orientation.isPortrait { print("isPortrait") }

        if orientation.isLandscape { print("isLandscape") }

        if orientation.isFlat { print("isFlat") }

        

    }

}
UIScreen.main.bounds Returning Wrong Value During iPhone Simulation
 
 
Q