在Xcode中为visionOS开发的Model Entity实现悬停触发操作的方法
解决visionOS中ModelEntity聚焦/失焦回调与高亮问题
你遇到的问题核心是:onHover是SwiftUI View的专属方法,RealityKit的ModelEntity并没有这个API,所以直接调用会报错。下面提供两种可行的解决方案,同时实现高亮效果和聚焦/失焦回调:
方案一:用SwiftUI HoverGesture结合RealityView更新
通过SwiftUI手势监听实体聚焦状态,再在RealityView的update闭包里同步更新实体外观,同时触发回调函数:
struct ContentView: View { @State private var hoveredEntityName: String? let uniformedScale: Float = 0.1 enum LightIDs: String, CaseIterable { case light1, light2, light3 } var body: some View { ZStack { RealityView { content, attachments in // 批量创建实体 for lightID in LightIDs.allCases { let model = ModelEntity( mesh: .generateSphere(radius: uniformedScale), materials: [SimpleMaterial(color: .red, isMetallic: false)] ) let x = Float.random(in: -1.0 ... 1.0) model.position = SIMD3(x, 1.0, -2.0) model.name = lightID.rawValue model.components.set(InputTargetComponent()) model.components.set(HoverEffectComponent()) model.components.set(CollisionComponent(shapes: [.generateSphere(radius: uniformedScale)])) content.add(model) } } update: { content, attachments in // 根据聚焦状态更新实体材质 for entity in content.entities { guard let modelEntity = entity as? ModelEntity else { continue } let isHovered = modelEntity.name == hoveredEntityName modelEntity.model?.materials = [ SimpleMaterial( color: isHovered ? .lightBlue : .red, isMetallic: false ) ] } } .gesture( HoverGesture() .targetedToAnyEntity() .onChanged { value in let entityName = value.entity.name hoveredEntityName = entityName handleFocus(entityName: entityName) } .onEnded { _ in guard let entityName = hoveredEntityName else { return } hoveredEntityName = nil handleBlur(entityName: entityName) } ) } } // 聚焦回调函数 private func handleFocus(entityName: String) { print("聚焦实体:\(entityName)") // 这里添加你的业务逻辑 } // 失焦回调函数 private func handleBlur(entityName: String) { print("失焦实体:\(entityName)") // 这里添加你的业务逻辑 } }
方案二:RealityKit原生InputTargetComponent事件处理
直接给InputTargetComponent绑定事件闭包,原生响应hoverEnter和hoverExit事件,更贴合RealityKit的处理逻辑:
struct ContentView: View { let uniformedScale: Float = 0.1 enum LightIDs: String, CaseIterable { case light1, light2, light3 } var body: some View { ZStack { RealityView { content in for lightID in LightIDs.allCases { let model = ModelEntity( mesh: .generateSphere(radius: uniformedScale), materials: [SimpleMaterial(color: .red, isMetallic: false)] ) let x = Float.random(in: -1.0 ... 1.0) model.position = SIMD3(x, 1.0, -2.0) model.name = lightID.rawValue // 绑定InputTarget事件处理 let inputTarget = InputTargetComponent { entity, event in switch event { case .hoverEnter: // 聚焦时更新材质+触发回调 model.model?.materials = [SimpleMaterial(color: .lightBlue, isMetallic: false)] handleFocus(entityName: entity.name) case .hoverExit: // 失焦时恢复材质+触发回调 model.model?.materials = [SimpleMaterial(color: .red, isMetallic: false)] handleBlur(entityName: entity.name) default: break } } model.components.set(inputTarget) model.components.set(HoverEffectComponent()) model.components.set(CollisionComponent(shapes: [.generateSphere(radius: uniformedScale)])) content.add(model) } } } } private func handleFocus(entityName: String) { print("聚焦实体:\(entityName)") // 业务逻辑 } private func handleBlur(entityName: String) { print("失焦实体:\(entityName)") // 业务逻辑 } }
关键注意事项
- 必须确保实体添加了
InputTargetComponent、CollisionComponent和HoverEffectComponent,这是接收眼睛聚焦事件的前提。 - 两种方案都可以实现高亮+回调的需求,方案二更轻量化,直接在RealityKit层处理事件;方案一适合需要和SwiftUI状态深度绑定的场景。
内容的提问来源于stack exchange,提问作者Vi Tiet
相关产品推荐
相关产品推荐

