Использование SpriteKit внутри SwiftUI - PullRequest
3 голосов
/ 16 июня 2019

У меня возникла проблема при создании SpriteKit сцены в SwiftUI. Первоначально я создал этот проект как SwiftUI проект.

Вот код, который у меня есть:

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:")
    }
}

Задача

Проблема в том, что сцена перезагружается несколько раз, как показано в журналах (поскольку в коде print s):

Сцена didMove:

Make UIView

Сцена didMove:

Обновление UIView


Как видите, Scene didMove: печатается дважды. Я хочу, чтобы это вызывалось только один раз, так как я хочу создать свои спрайты здесь. Есть идеи?

Ответы [ 2 ]

4 голосов
/ 16 июня 2019

Вы SceneView неверная реализация.

SwiftUI использует структуры для построения представлений в своем DSL, а не представлений.

Вы хотите создать struct, который соответствует 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)
    }
}

Для получения дополнительной информации о том, как переносить представления на основе UIKit в SwiftUI, см. Это превосходное видео WWDC 2019: Интеграция SwiftUI .

0 голосов
/ 19 июля 2019

Вот вид контейнера SpriteKit, который можно использовать следующим образом:

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
...