You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

tvOS SwiftUI嵌入SpriteKit场景,如何配置焦点引擎实现遥控器导航?

解决方案:在tvOS SwiftUI中实现SpriteKit节点的焦点导航

1. 自定义SpriteView的UIKit桥接(处理焦点传递)

默认的SpriteView无法正确将焦点事件传递给SKScene,需要自定义UIViewRepresentable封装SKView,确保焦点能向下传递到场景节点:

import SwiftUI
import SpriteKit

struct FocusableSpriteView: UIViewRepresentable {
    var scene: SKScene
    
    func makeUIView(context: Context) -> SKView {
        let skView = SKView()
        skView.scene = scene
        skView.isUserInteractionEnabled = true
        // 允许SKView接收焦点,以便传递给子节点
        skView.isFocusable = true
        return skView
    }
    
    func updateUIView(_ uiView: SKView, context: Context) {}
}

2. 配置SKScene和可聚焦节点

在SwatchGridScene中,给每个需要导航的节点开启可聚焦属性,并实现焦点相关回调:

class SwatchGridScene: SKScene {
    override func didMove(to view: SKView) {
        super.didMove(to: view)
        // 初始化3x5网格节点
        let gridSize = CGSize(width: 100, height: 100)
        let spacing: CGFloat = 20
        
        for row in 0..<3 {
            for col in 0..<5 {
                let node = SKShapeNode(rectOf: gridSize)
                node.fillColor = .random()
                node.strokeColor = .clear
                // 标记节点可被焦点引擎识别
                node.isFocusable = true
                // 设置网格布局位置
                node.position = CGPoint(
                    x: frame.midX + (CGFloat(col) - 2) * (gridSize.width + spacing),
                    y: frame.midY + (CGFloat(row) - 1) * (gridSize.height + spacing)
                )
                addChild(node)
            }
        }
    }
    
    // 处理焦点切换逻辑
    override func didUpdateFocus(in context: UIFocusUpdateContext, with coordinator: UIFocusAnimationCoordinator) {
        super.didUpdateFocus(in: context, with: coordinator)
        
        // 重置失焦节点颜色
        if let previousNode = context.previouslyFocusedItem as? SKShapeNode {
            previousNode.fillColor = .random()
        }
        // 高亮聚焦节点
        if let nextNode = context.nextFocusedItem as? SKShapeNode {
            nextNode.fillColor = .white
        }
    }
    
    // 处理遥控器确认按键事件
    override func pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
        guard let press = presses.first, press.type == .select else { return }
        if let focusedNode = focusedChild as? SKShapeNode {
            // 这里添加节点被选中后的业务逻辑
            print("选中节点位置:\(focusedNode.position)")
        }
    }
}

// 扩展UIColor生成随机颜色,用于节点视觉区分
extension UIColor {
    static func random() -> UIColor {
        UIColor(
            red: CGFloat.random(in: 0.3...1),
            green: CGFloat.random(in: 0.3...1),
            blue: CGFloat.random(in: 0.3...1),
            alpha: 1
        )
    }
}

3. 在SwiftUI视图中使用自定义组件

替换原SpriteView为自定义的FocusableSpriteView,并开启SwiftUI层面的可聚焦属性:

struct SwatchGridSceneView: View {
    private let scene = SwatchGridScene()
    
    var body: some View {
        GeometryReader { geometry in
            FocusableSpriteView(scene: scene)
                .frame(width: geometry.size.width, height: geometry.size.height)
                // 让SwiftUI视图允许焦点进入SpriteKit层级
                .focusable()
        }
    }
}

@main
struct ColourGameApp: App {
    var body: some Scene {
        WindowGroup {
            SwatchGridSceneView()
        }
    }
}

关键要点

  • 焦点传递链路:自定义FocusableSpriteView让SKView成为可聚焦UI元素,SwiftUI的.focusable()确保焦点能从SwiftUI层进入SpriteKit场景。
  • 节点可聚焦标记:必须给每个需要导航的SKNode设置isFocusable = true,否则焦点引擎会忽略该节点。
  • 视觉反馈:通过didUpdateFocus处理焦点切换时的UI变化,让用户清晰感知当前焦点位置。
  • 交互事件:pressesEnded方法处理遥控器确认按键,实现节点选中逻辑。

内容的提问来源于stack exchange,提问作者Jacob King

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 07:45:06