0

Environment: SwiftUI with Swift 5.5, Xcode 13

For my app, I need it to lock into certain orientations and then to unlock from that orientation. For a while it was working fine following ideas from this Stack Overflow answer. Currently though, once I lock it into a certain orientation, unlocking it seems to do nothing and the AppDelegate function checking the supportedInterfaceOrientations is no longer called.

Here is my AppDelegate code:

class AppDelegate: NSObject, UIApplicationDelegate {
  
  static var orientationLock = UIInterfaceOrientationMask.allButUpsideDown
  
  func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
    return AppDelegate.orientationLock
  }
}

Here is the entrance point to my app where the AppDelegate is included:

struct OrientationApp: App {
  @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Here is the base part of the app that is locking/unlocking the orientation:

struct ContentView: View {
    var body: some View {
      VStack {
        Text("Hello, world!")
        Button("Lock Portrait") {
          AppDelegate.orientationLock = .portrait
        }
        Button("Lock Landscape") {
          AppDelegate.orientationLock = .landscape
        }
        Button("Unlock Orientation") {
          AppDelegate.orientationLock = .allButUpsideDown
        }
      }
    }
}

Rotating will work fine and when I click "Lock Orientation" it stops it from being able to rotate, as expected, but when I click "Unlock Orientation" it doesn't seem to work. I still can't get it to rotate. I've tried changing AppDelegate to be an ObservableObject and get the same behavior. Also, let me know if there is any better way for iOS 15 or iOS 16 to handle orientation in SwiftUI. This was the only way that I was able to get it somewhat working.

trevash
  • 39
  • 6

1 Answers1

0

The callback application:supportedInterfaceOrientationsFor is called per orientation change, so when you change your custom property it is only property value, nothing else really changed.

We need to inform somehow the run-time that it should revalidate according to orientation.

Here is a possible solution. Tested with Xcode 13.4 / iOS 15.5

class AppDelegate: NSObject, UIApplicationDelegate {
  static var orientationLock = UIInterfaceOrientationMask.allButUpsideDown {
    didSet {
        // post notification so layout be revalidated
        // calling corresponding app delegate as well
        NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, object: nil)
    }
  }

// ...
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • This works with iOS 15 but not on iOS 16. Does anybody know how to get it working for both or do you think this is just a bug since iOS 16 is currently in Beta and will likely be fixed in the future? – trevash Aug 18 '22 at 22:18