I am using Swift and I want to be able to load a UIViewController when I rotate to landscape, can anyone point me in the right direction?
I Can't find anything online and a little bit confused by the documentation.
I am using Swift and I want to be able to load a UIViewController when I rotate to landscape, can anyone point me in the right direction?
I Can't find anything online and a little bit confused by the documentation.
Here's how I got it working:
In AppDelegate.swift
inside the didFinishLaunchingWithOptions
function I put:
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
and then inside the AppDelegate class I put the following function:
func rotated() {
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
}
if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
}
}
selector
have a string format of "rotated:"
?? –
Corot rotated()
doesn't) –
Nagoya Selector that specifies the message the receiver sends notificationObserver to notify it of the notification posting. The method specified by notificationSelector must have one and only one argument (an instance of NSNotification).
–
Pol UIDeviceOrientation
is something different to UIInterfaceOrientation
.. This is because UIDeviceOrientation
also detects face down and face up orientations meaning that your code can jump between portrait and landscape randomly if your device is resting on an almost flat but slightly uneven surface (i.e rocking on the protruding camera of the 6/6s) –
Coadjutant UIDeviceOrientationDidChange
notification is sent. Although the screen orientation is unchanged, the device orientation has changed in 3D space, therefore you get the notification –
Beckibeckie NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
–
Dorsy NSNotification.Name.UIDeviceOrientationDidChange
–
Nook According to the Apple docs:
This method is called when the view controller's view's size is changed by its parent (i.e. for the root view controller when its window rotates or is resized).
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
}
if UIDevice.current.orientation.isFlat {
print("Flat")
} else {
print("Portrait")
}
}
I need to detect rotation while using the camera with AVFoundation
, and found that the didRotate
(now deprecated) & willTransition
methods were unreliable for my needs. Using the notification posted by David did work, but is not current for Swift 3.x & above.
The following makes use of a closure, which appears to be Apple's preference going forward.
var didRotate: (Notification) -> Void = { notification in
switch UIDevice.current.orientation {
case .landscapeLeft, .landscapeRight:
print("landscape")
case .portrait, .portraitUpsideDown:
print("Portrait")
default:
print("other (such as face up & down)")
}
}
To set up the notification:
NotificationCenter.default.addObserver(forName: UIDevice.orientationDidChangeNotification,
object: nil,
queue: .main,
using: didRotate)
To tear down the notification:
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
Regarding the deprecation statement, my initial comment was misleading, so I wanted to update that. As noted, the usage of @objc
inference has been deprecated, which in turn was needed to use a #selector
. By using a closure instead, this can be avoided and you now have a solution that should avoid a crash due to calling an invalid selector.
Using -orientation
property of UIDevice
is not correct (even if it could work in most of cases) and could lead to some bugs, for instance UIDeviceOrientation
consider also the orientation of the device if it is face up or down, there is no direct pair in UIInterfaceOrientation
enum for those values.
Furthermore, if you lock your app in some particular orientation, UIDevice will give you the device orientation without taking that into account.
On the other side iOS8 has deprecated the interfaceOrientation
property on UIViewController
class.
There are 2 options available to detect the interface orientation:
What is still missing is a way to understand the direction of a change of interface orientation, that is very important during animations.
In the session of WWDC 2014 "View controller advancement in iOS8" the speaker provides a solution to that problem too, using the method that replaces -will/DidRotateToInterfaceOrientation
.
Here the proposed solution partially implemented, more info here:
func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
let orientation = orientationFromTransform(coordinator.targetTransform())
let oldOrientation = UIApplication.sharedApplication().statusBarOrientation
myWillRotateToInterfaceOrientation(orientation,duration: duration)
coordinator.animateAlongsideTransition({ (ctx) in
self.myWillAnimateRotationToInterfaceOrientation(orientation,
duration:duration)
}) { (ctx) in
self.myDidAnimateFromInterfaceOrientation(oldOrientation)
}
}
Since iOS 8 this is the correct way to do it.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: { context in
// This is called during the animation
}, completion: { context in
// This is called after the rotation is finished. Equal to deprecated `didRotate`
})
}
NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
. The issue with UIDeviceOrientation is that it detects face down and face up orientations which we don't need. –
Salvation I know this question is for Swift
, but since it's one of the top links for a Google search and if you're looking for the same code in Objective-C
:
// add the observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(rotated:) name:UIDeviceOrientationDidChangeNotification object:nil];
// remove the observer
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
// method signature
- (void)rotated:(NSNotification *)notification {
// do stuff here
}
Easy, this works in iOS8 and 9 / Swift 2 / Xcode7, just put this code inside your viewcontroller.swift. It will print the screen dimensions with every orientation change, you can put your own code instead:
override func didRotateFromInterfaceOrientation(fromInterfaceOrientation: UIInterfaceOrientation) {
getScreenSize()
}
var screenWidth:CGFloat=0
var screenHeight:CGFloat=0
func getScreenSize(){
screenWidth=UIScreen.mainScreen().bounds.width
screenHeight=UIScreen.mainScreen().bounds.height
print("SCREEN RESOLUTION: "+screenWidth.description+" x "+screenHeight.description)
}
didRotateFromInterfaceOrientation()
doesn't work reliably. It misses some rotations. iewWillTransitionToSize:withTransitionCoordinator:
works ok. –
Assegai I like checking the orientation notification because you can add this feature in any class, no needs to be a view or a view controller. Even in your app delegate.
SWIFT 5:
//ask the system to start notifying when interface change
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
//add the observer
NotificationCenter.default.addObserver(
self,
selector: #selector(orientationChanged(notification:)),
name: UIDevice.orientationDidChangeNotification,
object: nil)
than caching the notification
@objc func orientationChanged(notification : NSNotification) {
//your code there
}
In Objective C
-(void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
In swift
func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
Override this method to detect the orientation change.
Swift 3 | UIDeviceOrientationDidChange Notification Observed Too Often
The following code prints "deviceDidRotate" every time your device changes orientation in 3D space - regardless of a change from portrait to landscape orientation. For example, if you hold your phone in portrait orientation and tilt it forward and backward - deviceDidRotate() is called repeatedly.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceDidRotate),
name: .UIDeviceOrientationDidChange,
object: nil
)
}
func deviceDidRotate() {
print("deviceDidRotate")
}
To work around this you could hold the previous device orientation and check for a change in deviceDidRotate().
var previousDeviceOrientation: UIDeviceOrientation = UIDevice.current.orientation
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceDidRotate),
name: .UIDeviceOrientationDidChange,
object: nil
)
}
func deviceDidRotate() {
if UIDevice.current.orientation == previousDeviceOrientation { return }
previousDeviceOrientation = UIDevice.current.orientation
print("deviceDidRotate")
}
Or you can use a different notification that only gets called when the device changes from landscape to portrait. In this case you'd want to use the UIApplicationDidChangeStatusBarOrientation
notification.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(
self,
selector: #selector(deviceDidRotate),
name: .UIApplicationDidChangeStatusBarOrientation,
object: nil
)
}
func deviceDidRotate() {
print("deviceDidRotate")
}
override func didRotate(from fromInterfaceOrientation: UIInterfaceOrientation) {
//swift 3
getScreenSize()
}
func getScreenSize(){
let screenWidth = UIScreen.main.bounds.width
let screenHeight = UIScreen.main.bounds.height
print("SCREEN RESOLUTION: \(screenWidth.description) x \(screenHeight.description)")
}
Full working implementation of how to detect orientation change in Swift 3.0.
I chose to use this implementation because phone orientations of face up
and face down
were important to me, and I wanted the view to change only once I knew the orientation was in the specified position.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//1
NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange), name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
deinit {
//3
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIDeviceOrientationDidChange, object: nil)
}
func deviceOrientationDidChange() {
//2
switch UIDevice.current.orientation {
case .faceDown:
print("Face down")
case .faceUp:
print("Face up")
case .unknown:
print("Unknown")
case .landscapeLeft:
print("Landscape left")
case .landscapeRight:
print("Landscape right")
case .portrait:
print("Portrait")
case .portraitUpsideDown:
print("Portrait upside down")
}
}
}
The important pieces to note are:
unknown
orientation at times.Hope someone finds this helpful.
If you want to do something AFTER the rotation is complete, you can use the UIViewControllerTransitionCoordinator
completion handler like this
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
// Hook in to the rotation animation completion handler
coordinator.animate(alongsideTransition: nil) { (_) in
// Updates to your UI...
self.tableView.reloadData()
}
}
Swift 4:
override func viewWillAppear(_ animated: Bool) {
NotificationCenter.default.addObserver(self, selector: #selector(deviceRotated), name: UIDevice.orientationDidChangeNotification, object: nil)
}
override func viewWillDisappear(_ animated: Bool) {
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
}
@objc func deviceRotated(){
if UIDevice.current.orientation.isLandscape {
//Code here
} else {
//Code here
}
}
A lot of answers dont help when needing to detect across various view controllers. This one does the trick.
viewWillDisappear
and add addObserver
in viewDidLoad
. iOS unsubscribes view controller automatically. –
Lycanthrope UIDevice.current.orientation.isFlat
–
Jacobinism Check if rotation had changed with: viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
With the coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext)
you can check if the transition is finished.
See code below:
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition(nil) { (UIViewControllerTransitionCoordinatorContext) in
// if you want to execute code after transition finished
print("Transition finished")
}
if size.height < size.width {
// Landscape
print("Landscape")
} else {
// Portrait
print("Portrait")
}
}
Here is an easy way to detect the device orientation: (Swift 3)
override func willRotate(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
handleViewRotaion(orientation: toInterfaceOrientation)
}
//MARK: - Rotation controls
func handleViewRotaion(orientation:UIInterfaceOrientation) -> Void {
switch orientation {
case .portrait :
print("portrait view")
break
case .portraitUpsideDown :
print("portraitUpsideDown view")
break
case .landscapeLeft :
print("landscapeLeft view")
break
case .landscapeRight :
print("landscapeRight view")
break
case .unknown :
break
}
}
willRotate
is deprecated now, better use viewWillTransition
. –
Burgin Swift 5
Setup class to receive notifications of device orientation change:
class MyClass {
...
init (...) {
...
super.init(...)
// subscribe to device orientation change notifications
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
NotificationCenter.default.addObserver(self, selector: #selector(orientationChanged), name: UIDevice.orientationDidChangeNotification, object: nil)
...
}
...
}
Setup handler code:
@objc extension MyClass {
func orientationChanged(_ notification: NSNotification) {
let device = notification.object as! UIDevice
let deviceOrientation = device.orientation
switch deviceOrientation {
case .landscapeLeft: //do something for landscape left
case .landscapeRight: //do something for landscape right
case .portrait: //do something for portrait
case .portraitUpsideDown: //do something for portrait upside-down
case .faceDown: //do something for face down
case .faceUp: //do something for face up
case .unknown: //handle unknown
@unknown default: //handle unknown default
}
}
}
My approach is similar to what bpedit shows above, but with an iOS 9+ focus. I wanted to change the scope of the FSCalendar when the view rotates.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context) in
if size.height < size.width {
self.calendar.setScope(.Week, animated: true)
self.calendar.appearance.cellShape = .Rectangle
}
else {
self.calendar.appearance.cellShape = .Circle
self.calendar.setScope(.Month, animated: true)
}
}, completion: nil)
}
This below worked, but I felt sheepish about it :)
coordinator.animateAlongsideTransition({ (context) in
if size.height < size.width {
self.calendar.scope = .Week
self.calendar.appearance.cellShape = .Rectangle
}
}) { (context) in
if size.height > size.width {
self.calendar.scope = .Month
self.calendar.appearance.cellShape = .Circle
}
}
I use UIUserInterfaceSizeClass
to detect a orientation changed in a UIViewController
class just like that:
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
let isiPadLandscapePortrait = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .regular
let isiPhonePlustLandscape = newCollection.horizontalSizeClass == .regular && newCollection.verticalSizeClass == .compact
let isiPhonePortrait = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .regular
let isiPhoneLandscape = newCollection.horizontalSizeClass == .compact && newCollection.verticalSizeClass == .compact
if isiPhonePortrait {
// do something...
}
}
For Swift 3
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
//Landscape
}
else if UIDevice.current.orientation.isFlat {
//isFlat
}
else {
//Portrait
}
}
UIDevice.current.orientation.isFlat
–
Jacobinism With iOS 13.1.2, orientation always return 0 until device is rotated. I need to call UIDevice.current.beginGeneratingDeviceOrientationNotifications() before any rotation event occurs to get actual rotation.
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(OrientationDidChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
}
-(void)OrientationDidChange:(NSNotification*)notification {
UIDeviceOrientation Orientation=[[UIDevice currentDevice]orientation];
if(Orientation==UIDeviceOrientationLandscapeLeft || Orientation==UIDeviceOrientationLandscapeRight) {
NSLog(@"Landscape");
} else if(Orientation==UIDeviceOrientationPortrait) {
NSLog(@"Potrait Mode");
}
}
NOTE: Just use this code to identify UIViewController is in which orientation
override func viewDidLoad() {
NotificationCenter.default.addObserver(self, selector: #selector(MyController.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)
//...
}
@objc
private func rotated() {
if UIDevice.current.orientation.isLandscape {
} else if UIDevice.current.orientation.isPortrait {
}
//or you can check orientation separately UIDevice.current.orientation
//portrait, portraitUpsideDown, landscapeLeft, landscapeRight...
}
Iphone 15 pro traits on landscape orientation is compact and compact. Iphone 15 pro max is regular in horizantal trait on lanscape mode. It is so strange from Apple!
© 2022 - 2024 — McMap. All rights reserved.