CopyPastor

Detecting plagiarism made easy.

Score: 1; Reported for: Exact paragraph match Open both answers

Possible Plagiarism

Plagiarized on 2023-11-16
by Frost

Original Post

Original - Posted on 2017-01-23
by bmjohns



            
Present in both answers; Present only in the new answer; Present only in the old answer;

So in SwiftUI Apple has tried to force you to use just the Info.plist and Xcode General Deployment Info to force the view orientations. They don't support locking a SwiftUI view into a particular orientation which is ridiculous especially if your're using the camera. They also keep depcrecating orientation vars. So to get around these AppHole issues is complicated, but the code below works.
In AppDelegate have this.
static var orientationLock = UIInterfaceOrientationMask.all static var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return AppDelegate.orientationLock }

Now you need a SwiftUI View Extension
extension View { func detectOrientation(_ binding: Binding<UIDeviceOrientation>) -> some View { self.modifier(OrientationDetector(orientation: binding)) } }
struct OrientationDetector: ViewModifier { @Binding var orientation: UIDeviceOrientation let orientationMask = AppDelegate.orientationLock func body(content: Content) -> some View { content .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
if checkOrientation(orientationMask: orientationMask) { orientation = UIDevice.current.orientation } else { orientation = AppDelegate.deviceOrientation } } } }
Now it gets tricky since UIDevice.current.orientation has .faceUp and .faceDown which you don't care about so you need to ignore these view orientations, this is effectively what the checkOrienation() method does by saving the prior orientation when it does go into .faceUp or .faceDown.
func checkOrientation(orientationMask:UIInterfaceOrientationMask) -> Bool { //Here we're ignoring faceUp and faceDown and storing the prior orientation in AppDelegate.deviceOrientation //This effectively eliminates these two problematic orientations so the App will ignore these and return the prior value var status : Bool = false switch UIDevice.current.orientation { case .portrait : if(orientationMask == .portrait || orientationMask == .portraitUpsideDown || orientationMask == .allButUpsideDown) { status = true AppDelegate.deviceOrientation = UIDevice.current.orientation } case .landscapeLeft : if orientationMask == .landscapeLeft || orientationMask == .allButUpsideDown { status = true AppDelegate.deviceOrientation = UIDevice.current.orientation } case .landscapeRight : if orientationMask == .landscapeRight || orientationMask == .allButUpsideDown { status = true AppDelegate.deviceOrientation = UIDevice.current.orientation } case .portraitUpsideDown : if orientationMask == .portraitUpsideDown || orientationMask == .allButUpsideDown { status = true AppDelegate.deviceOrientation = UIDevice.current.orientation } case .faceUp : status = false case .faceDown : status = false case .unknown: status = false @unknown default: status = false
} return status }
Now in your Primary SwiftUI Views you need an init and .detectOrienation($orientation)
@State private var orientation = UIDevice.current.orientation

init() {
//Lock into all view rotations AppDelegate.orientationLock = .allButUpsideDown if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { windowScene.requestGeometryUpdate(.iOS(interfaceOrientations: .allButUpsideDown)) UIApplication.navigationTopViewController()?.setNeedsUpdateOfSupportedInterfaceOrientations() } }
//At bottom of SwiftUI view add this : .detectOrientation($orientation)
What the above does for the SwiftUI View is let it rotate for .allButUpsideDown view rotations. If you want to lock it, set .portrait, or .landscapeLeft or .landscapeRight.

The Primary SwiftUI View will declare the @State var orientation, and all your SwiftUI SubViews that need orientation need to consume this same var.
Thus a SwiftUI Subview needs this :
@Binding private var orientation:UIDeviceOrientation
init(orientation:Binding<UIDeviceOrientation>) self._orientation = orientation }
Now in your Primary and SubViews you do this to handle the layout in a particular view orientation.
if orientation.isLandscape {
//SwiftUI elements for Landscape
} else {
//SwiftUI elements for Portrait
}
And thats it, it's a lot of code to get around this problem, but it works in SwiftUI version 5 and iOS 16+ and Apple will approve it for an App Release also.
We really need to lobby Apple to support natively locking a SwiftUI View into a particular view orientation, this can be done by just calling a simple SwiftUI method. They also need to create a new var for UIDeviceOrientation that ignores .faceUp and .faceDown so you can consume this var in your SWiftUI view and layout your view elements accordingly.


Things can get quite messy when you have a complicated view hierarchy, like having multiple navigation controllers and/or tab view controllers.
This implementation puts it on the individual view controllers to set when they would like to lock orientations, instead of relying on the App Delegate to find them by iterating through subviews.
**Swift 3, 4, 5**
In AppDelegate:
/// set orientations you want to be allowed in this property by default var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { return self.orientationLock }
In some other global struct or helper class, here I created AppUtility:
struct AppUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) { if let delegate = UIApplication.shared.delegate as? AppDelegate { delegate.orientationLock = orientation } } /// OPTIONAL Added method to adjust lock and rotate to the desired orientation static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) { self.lockOrientation(orientation) UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation") UINavigationController.attemptRotationToDeviceOrientation() }
}

Then in the desired ViewController you want to lock orientations:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) AppUtility.lockOrientation(.portrait) // Or to rotate and lock // AppUtility.lockOrientation(.portrait, andRotateTo: .portrait) }
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) // Don't forget to reset when view is being removed AppUtility.lockOrientation(.all) } **If iPad or Universal App**
Make sure that "Requires full screen" is checked in Target Settings -> General -> Deployment Info. `supportedInterfaceOrientationsFor` delegate will not get called if that is not checked. [![enter image description here][1]][1]

[1]: https://i.stack.imgur.com/w99KB.png

        
Present in both answers; Present only in the new answer; Present only in the old answer;