Spritekit and swiftui, change scene a better way
You are using a standard way of switching views. You could clean your code up a bit like this:
struct ContentView: View {
@State var changeScene = false
var body: some View {
ZStack{
SpriteView(scene: (changeScene ? FirstScene() : SecondScene())) // this is a terniary operator
Button {
changeScene.toggle()
} label: {
Text("Change")
}
}
}
}
The new views do not need to be @StateObjects. You can call the View Types directly in your view.
Note: I am away from my computer so this is not tested.
Move From SpriteKit scene to SwiftUI View
You can use
@Environment(.presentationMode) var presentationMode
So you can create a button and call
presentationMode.wrappedValue.dismiss()
Or you can pass a binding var to Content View and set to false like so:
In MainMenu
struct MainMenu: View {
@State var isPresented = false
var body: some View {
NavigationView {
VStack {
Text("Replicator")
.font(.largeTitle)
.fontWeight(.bold)
.padding()
NavigationLink(destination: ContentView(isPresented: $isPresented).navigationBarBackButtonHidden(true), isActive: $isPresented) {
HStack {
Image(systemName: "play.circle")
.font(.title3)
Text("Start")
.font(.title)
}
.frame(width: 250, height: 50)
.background(.cyan)
.cornerRadius(25)
.foregroundColor(.white)
}
}
}
}
}
In ContentView:
struct ContentView: View {
@Binding var isPresented: Bool
var scene: SKScene {
let scene = Level_1()
scene.size = CGSize(width: 750, height: 1334)
scene.scaleMode = .aspectFit
return scene
}
var body: some View {
ZStack {
SpriteView(scene: scene)
.ignoresSafeArea()
Button(action: { //You can put the button wherever you want as long as you pass in the isPresented Binding
isPresented.toggle()
}) {
Text("Back to MainMenu")
}
}
}
}
SwiftUI handling SpriteKit scene recreation when data changes
I managed to work around my problem by creating a view model that manages the SpriteKit scene creation if needed.
class TamagotchiViewModel {
private var spriteKitScenes: [SpriteKitScene] = []
func scene(for tamagotchi: TamagotchiModel) -> SpriteKitScene {
if let scene = spriteKitScenes.first(where: { $0.tamagotchi?.tamagotchiModel.id == tamagotchi.id}) {
return scene
} else {
let newScene = SpriteKitScene(model: tamagotchi)
spriteKitScenes.append(newScene)
return newScene
}
}
}
SwiftUI, changing a view from scene(:continue
It would be appropriate to use EnvironmentObject
in this scenario
class AppState: ObservableObject
@Published var someVar: Sometype
}
class SceneDelegate {
let appState = AppState()
func scene(_ scene: UIScene,
continue userActivity: NSUserActivity) {
// ... other code
appState.someVar = ... // modify
}
}
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// .. other code
let contentView = ContentView()
.environmentObject(appState)
//
}
}
struct ContentView: View {
@EnvironmentObject var appState
var body: some View {
VStack {
// ... other code
appState.someVar // use here as needed
}
}
}
Scene with SpriteView and SwiftUI does not fill whole screen
You have height at width and width at height parameter at scene property. (How @aheze mentioned in the comment)
SpriteKit and SwiftUI. GameScene func not working from UI button
Two problems:
- Your
scene
is a computed property so you are recreating the scene every time you try use it inSpriteView
. - You are creating a new scene when you try add the ball. This should be added to the current scene, not a new instance.
Change scene
to this:
let scene: GameScene = {
let scene = GameScene()
scene.size = CGSize(width: 800, height: 930)
scene.scaleMode = .fill
scene.backgroundColor = .red
return scene
}()
And change the action on the pause button to this:
scene.ball()
print("Image tapped!")
Side note: with a fixed scene size like this, you got to be careful - you can only really see the play/pause buttons on iPad screens since the scene size is so big. Consider keeping it at a specific aspect ratio instead, then fitting it to the screen.
How to switch between scenes in Sprite Kit?
It looks like your issue is in didMoveToView:
in your PauseScene. You don't want to change your anchor point for self.pauseBackground
Normally anchor point is based on 0 to 1. (.5,.5) being the center of the sprite. You are setting it to something much higher than that and if you are setting the position to half the width and hight of the scene I don't see why you would want to change the anchor point at all. Looks like just deleting the line of code should fix the issue.
class PauseScene: SKScene {
let pauseBackground = SKSpriteNode (imageNamed: "pauseBackground")
override func didMoveToView(view: SKView) {
self.pauseBackground.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(pauseBackground)
}
}
Also I see you are changing the size of your scene after you init it. I would just init it with the size you want.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self.pause {
let skView = self.view as SKView!
skView.ignoresSiblingOrder = true
var pauseScene = PauseScene(size: skView.bounds.size)
scene?.paused = true
pauseScene.scaleMode = .AspectFill
skView.presentScene(pauseScene)
}
}
}
Hopefully that helps and makes sense.
Using SpriteKit inside SwiftUI
SwiftUI 2
There is now a native view responsible for displaying a SKScene
- it's called SpriteView
.
Assuming we have a simple SKScene
:
class Scene: SKScene {
override func didMove(to view: SKView) {
...
}
}
we can use a SpriteView
to display it directly in a SwiftUI view:
struct ContentView: View {
var scene: SKScene {
let scene = Scene()
scene.size = CGSize(width: 300, height: 400)
scene.scaleMode = .fill
return scene
}
var body: some View {
SpriteView(scene: scene)
.frame(width: 300, height: 400)
.edgesIgnoringSafeArea(.all)
}
}
You can find more information here:
- How to integrate SpriteKit using SpriteView.
Related Topics
Swift 3: Convert a String to an Array
Firebase Swift 3 Get List of Child in a Array
Xcode 9.3 Watchkit Crash on Wkinterfacebutton Tap
List Inside Scrollview Is Not Displayed on Watchos
Macos Swift Master-Detail Delegate/Protocol Not Working
Get Url from Open Dialog of Standard Swift Document-Based Application
Update Widget When Appearance Changes
Editable Nstextview from Interface Builder
Why Is My App Returning Hundreds of Errors That Say "Undefinedsymbol"
Cannot Convert Value of Type 'Foo!' to Expected Argument Type 'Foo!'
Xcode + Swift + Darwin.Ncurses = "A_Bold Not Found" Compilation Error. I Can't Get Bright Colors
Have Label Appear with Delay in Swift
Images Being Flipped When Adding to Nsattributedstring