Reputation: 36670
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.
Upvotes: 4
Views: 781
Reputation: 7930
You don't need GeometryReader. Just let SwiftUI itself give the available space to your SpriteView. An important thing is, that the views size is not automatically the scene size. So, you have to explicitly set the scene size.
import SwiftUI
import SpriteKit
struct ContentView: View {
var body: some View {
SpriteView(scene: spriteKitScene)
}
private var spriteKitScene : SKScene {
let scene = SpriteKit_View()
scene.scaleMode = .aspectFit
return scene
}
}
class SpriteKit_View : SKScene {
override func didMove(to view: SKView) {
// Set the scene size as you want, in my case
equal to the views size
size = view.frame.size
// ...
}
}
The scene size can be completely different from the view size, that's when the scene scaleMode comes into action.
Upvotes: 0
Reputation: 6070
SKScene
has a didChangeSize
method for scenarios like that:
override func didChangeSize(_ oldSize: CGSize) {
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
}
From Apples Documentation:
This method is intended to be overridden in a subclass. Typically, you use this method to adjust the positions of nodes in the scene.
Upvotes: 0
Reputation: 16347
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
Upvotes: 3