Search code examples
iosswiftsprite-kitswiftuiios13

Unable to properly size SKScene when working with SwiftUI


I am attempting to use SpriteKit with a SwiftUI based interface, but have encountered an issue when initializing my SKScene.

First, my UIViewRepresentable that contains the SKView has been taken from a similar question:

struct SpriteKitContainer: UIViewRepresentable {
    let scene: SKScene

    func makeUIView(context: Context) -> SKView {
        return SKView(frame: .zero)
    }
    
    func updateUIView(_ view: SKView, context: Context) {
        view.presentScene(scene)
    }
}

A key piece here is that it requires the SKScene to be initialized prior to SpriteKitContainer being initialized.

I currently have the following abbreviated snippet doing this:

import SwiftUI
import SpriteKit

struct GameView: View {
    @State private var isEnabled = true
    
    var body: some View {
        let tapGesture = TapGesture()
            .onEnded { value in }
        
        // This size is obviously wrong
        let size = UIScreen.main.bounds.size
        let scene = Scene(size: size)
        scene.scaleMode = .resizeFill
        
        return SpriteKitContainer(scene: scene)
            .disabled(!isEnabled)
            .frame(maxWidth: .infinity,
                   maxHeight: .infinity,
                   alignment: .center)
            .gesture(tapGesture)
            .padding(.all, 30)
    }
}

I only include the TapGesture to show why the body is structured the way it is. With the code included, everything will work as expected, but the size used for the scene is obviously wrong (it fails to account for the padding, and navigation elements).

However, I have not found any other way to obtain a size or frame at this point, and sending over a .zero size for the Scene(size:) call will not size it correctly either.

My assumption is that I am missing some precursor step as I am still ramping up with SwiftUI. Otherwise, I could calculate a new CGSize given what I know, but that strikes me as the wrong approach here.


Solution

  • In SwiftUI you need to use GeometryReader as a proxy to get the view's current size (this happens every time the view is rebuilt). You then want to resize your UIViewRepresentable based on the size of its parent only if the size has actually changed. I have a GeometryReader example here: https://github.com/joshuajhomann/SwiftUI-Spirograph