I want to force the display orientation to portrait programatically as some other screens should still autorotate. I added some code that works but the screen still autorotates and is then quickly corrected when my code runs. I want to prevent the autorotation from occurring at all.
I'm running an SKView (SpriteKit) under SwiftUI using SpriteView. The code I have which works is:
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
This forces portrait mode. I found this on another stack overflow question which is now closed. According to that question, I should also have prevented autorotation. The example used the code below, however I think this only works on a UIView, not on an SKView. I can't find any way to get the code below to work. Code is:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
UIInterfaceOrientationMask.
return .portrait
}
override var shouldAutorotate: Bool {
return false
}
Can anyone explain how to disable autorotation with SpriteView and SKView?
In addition, is it still possible to read the actual orientation (portrait, portrait upside down, landscape left and landscape right) after the screen is forced into portrait? I need this information for some manual tweaking.
CodePudding user response:
SwiftUI does not allow to block auto-rotation and UIHostingController
seems handle it explicitly inside independently of parent UIViewController settings or inherited controller blocks, so representable variant is also not possible (at least as container for SwiftUI content).
A possible found workaround is to use compensate-rotation for view in response of orientation changes.
Here is demo. Tested with Xcode 13.4 / iOS 15.5
@State private var angle: Angle = .radians(0)
var body: some View {
SpriteView(scene: scene)
.rotationEffect(angle, anchor: .center)
.onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
self.updateOrintation()
}
.onAppear {
self.updateOrintation()
}
.edgesIgnoringSafeArea(.all)
}
private func updateOrintation() {
switch UIDevice.current.orientation {
case .landscapeLeft:
self.angle = .radians(-.pi / 2)
case .landscapeRight:
self.angle = .radians(.pi / 2)
case .portraitUpsideDown:
self.angle = .radians(.pi)
default:
self.angle = .radians(0)
}
}
CodePudding user response:
I've tried the solution above with limited success using the iPhone 8 simulator.
If the device is in landscape when SKView is opened, the first view doesn't rotate. Subsequent rotations correct this and landscape left and right will both rotate. I could probably solve this one.
When rotated to landscape the SKView x/y dimensions don’t rotate, changing the scaling and reducing the scene width to the narrow width of the device. Screen touches don’t work to the right or left of this area. The initial scene size is screen width (40m) x 1,000m high (most of the vertical part of the scene is off screen). This scale should also rotate. Having to do this manually seems cumbersome. I guess this would have to be done in the scene and not in SwiftUI?
Any idea how to have the scene dimensions rotate with the image?
Previously I rotated the view using the SpriteKit camera. That way none of the physics were affected. The only problem was the screen partially rotating when orientation was changed and then quickly correcting, which didn’t look good. I tried fixing orientation to portrait in info.plist and it worked perfectly, but of course the navigation controller and screens elsewhere didn’t rotate.
If there isn’t an easy fix for the issues above, do you think the following solution would work using the camera method?
a. Instead of using SpriteView from within a SwiftUI view, I could try including a UIViewController here instead.
b. Within the UIView I could prevent autorotation as per other info I’ve found.
c. Once autorotation is locked I could run the SKView from within the UIView.
I’m not sure if this would work but I can’t see any reason why not. When exiting back to the SwiftUI view, autorotation can be reenabled. This is my first App and I haven’t used a UIViewController yet, however some research I’ve done suggests it may work.