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:
UIViewを作成する
シーンdidMove:
UIViewを更新する
ご覧のように、 Scene didMove:
は2回出力されます。ここで自分のスプライトを作成したいので、これを一度だけ呼び出したいです。何か案は?
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ビデオをご覧ください: Integrating SwiftUI 。
以下は、この方法で使用できる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