web-dev-qa-db-fra.com

Utilisation de SpriteKit dans SwiftUI

Je rencontre un problème lors de la création d'une scène SpriteKit dans SwiftUI. J'ai créé ce projet initialement comme un projet SwiftUI.

Voici le code que j'ai jusqu'à présent:

ContentView.Swift:

/// Where the UI content from SwiftUI originates from.
struct ContentView : View {
    var body: some View {
        // Scene
        SceneView().edgesIgnoringSafeArea(.all)
    }
}

SceneView.Swift:

/// Creates an SKView to contain the GameScene. This conforms to UIViewRepresentable, and so can be used within SwiftUI.
final class SceneView : SKView, UIViewRepresentable {

    // Conformance to UIViewRepresentable
    func makeUIView(context: Context) -> SKView {
        print("Make UIView")
        return SceneView(frame: UIScreen.main.bounds)
    }
    func updateUIView(_ uiView: SKView, context: Context) {
        print("Update UIView")
    }

    // Creating scene
    override init(frame: CGRect) {
        super.init(frame: frame)

        let scene = Scene(size: UIScreen.main.bounds.size)
        presentScene(scene)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

Scene.Swift:

/// The scene for the game in SpriteKit.
final class Scene : SKScene {

    override func didMove(to view: SKView) {
        super.didMove(to: view)

        print("Scene didMove:")
    }
}

Problème

Le problème est que la scène se recharge plusieurs fois, comme le montrent les journaux (car il y a prints dans le code):

La scène a bougé:

Faire UIView

La scène a bougé:

Mettre à jour UIView


Comme vous pouvez le voir, Scene didMove: est imprimé deux fois. Je veux seulement que cela soit appelé une fois, car je veux créer mes sprites ici. Des idées?

10
George_E

L'implémentation de votre SceneView est incorrecte.

SwiftUI utilise des structures pour créer des vues dans son DSL - pas des vues.

Vous souhaitez créer un struct conforme à UIViewRepresentable.

struct SceneView: UIViewRepresentable {

    let scene: SKScene

    func makeUIView(context: Context) -> SKView {
        // Let SwiftUI handle the sizing
        return SKView(frame: .zero)
    }

    func updateUIView(_ uiView: SKView, context: Context) {
        uiView.presentScene(scene)
    }
}

Pour plus d'informations sur la façon de porter des vues basées sur UIKit vers SwiftUI, consultez cette excellente vidéo WWDC 2019: Integrating SwiftUI .

8
Matteo Pacini

Voici une vue de conteneur SpriteKit qui peut être utilisée de cette façon:

SpriteKitContainer(sceneName: "MainScene")

struct SpriteKitContainer : UIViewRepresentable {

    let sceneName: String

    class Coordinator: NSObject {
        var scene: SKScene?
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }

    func makeUIView(context: Context) -> SKView {
        let view = SKView(frame: .zero)
        view.preferredFramesPerSecond = 60
        view.showsFPS = true
        view.showsNodeCount = true

       //load SpriteKit Scene
       guard let aScene = SKScene(fileNamed: sceneName)
       else {
            view.backgroundColor = UIColor.red
            return view
       }
       aScene.scaleMode = .resizeFill
       context.coordinator.scene = aScene
       return view
    }


    func updateUIView(_ view: SKView, context: Context) {
       view.presentScene(context.coordinator.scene)
    }

}
#if DEBUG
struct ContentView_Previews : PreviewProvider {

   static var previews: some View {

      // Replace "MainScene" with your SpriteKit scene file name
      SpriteKitContainer(sceneName: "MainScene")
         .edgesIgnoringSafeArea(.all)
         .previewLayout(.sizeThatFits)
      }
}
#endif
3
Vlad Lego