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") }
}
}
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") }
}
}