1

My app has certain orientations that are set depending on device. For iPads it is set to landscape and portrait for iPhones. This works fine except I want to allow one view to be shown in landscape for iPhone. I need the view to automatically rotate to landscape and rotate back to portrait after leaving that view. I have the following code that locks the orientation for that view but the device has to be manually rotated back and forth. Is there a way to auto-rotate the device?

.onAppear {
            if(deviceType == .phone){
                UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
                AppDelegate.orientationLock = .landscape

            }
        }
.onDisappear {
            if(deviceType == .phone){
                AppDelegate.orientationLock = .portrait
            }
        }
Brian Kalski
  • 897
  • 9
  • 35

1 Answers1

5

enter image description here

This can be achieved using a View extension that will update the view's orientation. Let’s make our own extension which will rotate the view on given orientation, back and forth.

extension View {
    @ViewBuilder
    func forceRotation(orientation: UIInterfaceOrientationMask) -> some View {
        if UIDevice.current.userInterfaceIdiom == .phone {
            self.onAppear() {
                AppDelegate.orientationLock = orientation
            }
            // Reset orientation to previous setting
            let currentOrientation = AppDelegate.orientationLock
            self.onDisappear() {
                AppDelegate.orientationLock = currentOrientation
            }
        } else {
            self
        }
    }
}

In AppDelegate, add:

class AppDelegate: NSObject, UIApplicationDelegate {

    static var orientationLock = UIInterfaceOrientationMask.portrait {
        didSet {
            if #available(iOS 16.0, *) {
                UIApplication.shared.connectedScenes.forEach { scene in
                    if let windowScene = scene as? UIWindowScene {
                        windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: orientationLock))
                    }
                }
                UIViewController.attemptRotationToDeviceOrientation()
            } else {
                if orientationLock == .landscape {
                    UIDevice.current.setValue(UIInterfaceOrientation.landscapeRight.rawValue, forKey: "orientation")
                } else {
                    UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
                }
            }
        }
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        return true
    }
    
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        return AppDelegate.orientationLock
    }
}

This little example demonstrate how you can use this extension:

struct ContentView: View {
        
    var body: some View {
        NavigationView {
            List(Orientation.allCases, id: \.self) { orientation in
                       NavigationLink(orientation.title) {
                           ZStack {
                               Text(orientation.title)
                           }
                           .forceRotation(orientation: orientation.mask)  // << Update the required orientation here.. 
                       }
                   }
               }
        }
}

enum Orientation: Int CaseIterable {
    case landscapeLeft
    case landscapeRight
    
    var title: String {
        switch self {
        case .landscapeLeft:
            return "LandscapeLeft"
        case .landscapeRight:
            return "LandscapeRight"
        }
    }
    
    var mask: UIInterfaceOrientationMask {
        switch self {
        case .landscapeLeft:
            return .landscapeLeft
        case .landscapeRight:
            return .landscapeRight
        }
    }
}
Kush Bhavsar
  • 909
  • 2
  • 12