iosswiftsprite-kitdevice-orientationscreen-rotation

Swift: Selectively prevent auto screen rotation with SpriteKit scenes


I have an iOS application that will have a number of SpriteKit scenes. These scenes should remain in the same orientation on the screen in both portrait and landscape. For example one scene will have a dual carriageway road running lengthways on the screen ie.it will run up/down in portrait and left/right in landscape. I don't want it to auto rotate with the device, although I will be rotating text on the screen to keep it readable (I can do this).

There are a number of other views (mainly text) that should still auto rotate so I don't want to deselect options in 'Deployment Info'.

Any ideas how to selectively allow/prevent auto rotation with the device? I want calculations for the vehicle positions to be unchanged between orientations. Ideally just rotate the scene.


Solution

  • here is a way to allow device rotation at top level, while simulating no rotation at SKScene level. you can receive device rotation events in SwiftUI and then pass that information down to your scene

    struct GameSceneView: View {
        @State private var scene = TestScene()
    
        var body: some View {        
            SpriteView(scene: scene)
                .overlay(alignment: .topLeading) {
                    Text("SwiftUI Text").font(.system(size:36))
                }
                .onReceive(NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)) { _ in
                    let isLandscape = UIDevice.current.orientation == .landscapeLeft || UIDevice.current.orientation == .landscapeRight
                    let rotation = isLandscape ? CGFloat.pi/2 : 0
                    scene.set(containerZRotation:rotation)
                }
        }
    }
    

    the corresponding SKScene simply needs to have a top level SKNode that contains all graphical elements as children. you set rotation accordingly on that container

    class TestScene: SKScene {
    
        //property and function for handling rotation
        let container = SKNode()
        func set(containerZRotation:CGFloat) {
            container.zRotation = containerZRotation
        }
        
        override func didMove(to view: SKView) {
            self.addChild(container) //add container
    
            //add all child nodes to *container* rather than self
            let sprite = SKSpriteNode(imageNamed: "road")
            container.addChild(sprite)        
        }
    }