使用 SwiftUI 时无法正确调整 SKScene 的大小

Posted

技术标签:

【中文标题】使用 SwiftUI 时无法正确调整 SKScene 的大小【英文标题】:Unable to properly size SKScene when working with SwiftUI 【发布时间】:2019-10-07 14:17:42 【问题描述】:

我正在尝试将SpriteKit 与基于SwiftUI 的接口一起使用,但在初始化我的SKScene 时遇到了问题。

首先,包含SKViewUIViewRepresentable 取自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)
    

这里的一个关键部分是它需要在初始化 SpriteKitContainer 之前初始化 SKScene

我目前有以下缩写的 sn-p 这样做:

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)
    

我只包含TapGesture 来说明为什么body 的结构是这样的。包含代码后,一切都会按预期工作,但场景使用的大小显然是错误的(它无法考虑填充和导航元素)。

但是,我目前还没有找到任何其他方法来获取大小或框架,并且为 Scene(size:) 调用发送 .zero 大小也不会正确调整大小。

我的假设是,由于我仍在使用 SwiftUI,因此我错过了一些前导步骤。否则,我可以根据我所知道的计算出一个新的CGSize,但这让我觉得这里的方法是错误的。

【问题讨论】:

你的场景总是会根据视图调整大小,如果你需要填充,你需要在视图级别考虑它 【参考方案1】:

在 SwiftUI 中,您需要使用 GeometryReader 作为代理来获取视图的当前大小(每次重建视图时都会发生这种情况)。然后,您希望根据父级的大小调整 UIViewRepresentable 的大小只有在大小实际发生变化时。我在这里有一个 GeometryReader 示例:https://github.com/joshuajhomann/SwiftUI-Spirograph

【讨论】:

谢谢,我终于能够回到这个问题上,你把我带到了正确的区域并结合***.com/questions/56729619/…,我能够让它工作。【参考方案2】:

SKScene 有一个 didChangeSize 方法用于这样的场景:

override func didChangeSize(_ oldSize: CGSize) 
    self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)

来自Apples Documentation:

此方法旨在在子类中被覆盖。通常,您 使用此方法调整场景中节点的位置。

【讨论】:

【参考方案3】:

您不需要 GeometryReader。只需让 SwiftUI 本身为您的 SpriteView 提供可用空间。重要的是,视图大小不是自动场景大小。因此,您必须明确设置场景大小。

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

场景大小可以与视图大小完全不同,这就是场景 scaleMode 发挥作用的时候。

【讨论】:

以上是关于使用 SwiftUI 时无法正确调整 SKScene 的大小的主要内容,如果未能解决你的问题,请参考以下文章

调整窗口大小时,SwiftUI TextField 文本边界无法使用 GeometryReader 更新,导致外观错误

SwiftUI:更改数据源时选择器无法正确更新

带有 SpriteView 和 SwiftUI 的场景不会填满整个屏幕

在 SwiftUI 中添加 Rectangle() 时单元格行自动调整大小损坏

SwiftUI解决List子项无法正确触发onAppear和onDisappear事件的问题

SwiftUI解决List子项无法正确触发onAppear和onDisappear事件的问题