如何避免嵌套RealityViews中的不必要update调用
解决RealityView附件视图不必要的update调用问题
我有两个包含RealityView的视图First和Second,其中Second作为First的附件使用。First视图中声明了@State属性,当修改该状态时,SwiftUI会触发Second视图的update块(该状态并未在Second中使用)。如何避免Second中不必要的update块调用?
问题代码
import SwiftUI import RealityKit struct First: View { @State private var isButtonTapped = false var body: some View { print("Body #1") return VStack { RealityView { content, attach in let model = sphereModel(y: 0, color: .brown) content.add(model) } update: { content, attach in print(isButtonTapped) print("Update #1") guard let someView = attach.entity(for: "id") else { return } content.add(someView) } attachments: { Attachment(id: "id") { Second() } } Button("BUTTON") { isButtonTapped.toggle() } } } } struct Second: View { var body: some View { print("Body #2") return RealityView { content in let model = sphereModel(y: 0, color: .red) content.add(model) } update: { content in print("Update #2") } } } func sphereModel(y: Float, color: UIColor) -> ModelEntity { let collisionComponent = CollisionComponent( shapes: [ShapeResource.generateSphere(radius: 0.01)] ) let hoverEffectComponent = HoverEffectComponent(.highlight(.init(color: .blue))) var sphereModelMaterial = PhysicallyBasedMaterial() sphereModelMaterial.baseColor = .init(tint: color) let sphereMesh = MeshResource.generateSphere(radius: 0.01) let sphereModel = ModelEntity( mesh: sphereMesh, materials: [sphereModelMaterial] ) sphereModel.transform.translation = .init(x: 0, y: y, z: 0) sphereModel.components.set( [ collisionComponent, hoverEffectComponent ] ) return sphereModel }
解决方法
1. 让Second视图遵循Equatable并使用EquatableView包裹
SwiftUI默认会在父视图状态变化时刷新所有子视图(包括附件视图)。通过让Second遵循Equatable,并利用EquatableView,可以告诉SwiftUI只有当Second的依赖属性变化时才刷新它。因为Second没有任何外部依赖,直接返回true即可。
修改Second视图:
struct Second: View, Equatable { static func == (lhs: Second, rhs: Second) -> Bool { // 视图无外部依赖,始终视为相等 true } // 原body代码不变 }
然后在First的附件中替换为:
Attachment(id: "id") { EquatableView(content: Second()) }
2. 优化First的RealityView update逻辑
原代码中First的update块每次都会执行content.add(someView),这会重复添加实体,不仅浪费性能,也可能间接导致附件视图刷新。修改成只在实体未添加时才执行:
RealityView { content, attach in let model = sphereModel(y: 0, color: .brown) content.add(model) // 首次加载时添加附件实体 if let someView = attach.entity(for: "id") { content.add(someView) } } update: { content, attach in print(isButtonTapped) print("Update #1") guard let someView = attach.entity(for: "id"), !content.entities.contains(someView) else { return } content.add(someView) }
修改后的完整代码
import SwiftUI import RealityKit struct First: View { @State private var isButtonTapped = false var body: some View { print("Body #1") return VStack { RealityView { content, attach in let model = sphereModel(y: 0, color: .brown) content.add(model) // 首次添加附件实体 if let someView = attach.entity(for: "id") { content.add(someView) } } update: { content, attach in print(isButtonTapped) print("Update #1") guard let someView = attach.entity(for: "id"), !content.entities.contains(someView) else { return } content.add(someView) } attachments: { Attachment(id: "id") { EquatableView(content: Second()) } } Button("BUTTON") { isButtonTapped.toggle() } } } } struct Second: View, Equatable { static func == (lhs: Second, rhs: Second) -> Bool { true } var body: some View { print("Body #2") return RealityView { content in let model = sphereModel(y: 0, color: .red) content.add(model) } update: { content in print("Update #2") } } } func sphereModel(y: Float, color: UIColor) -> ModelEntity { let collisionComponent = CollisionComponent( shapes: [ShapeResource.generateSphere(radius: 0.01)] ) let hoverEffectComponent = HoverEffectComponent(.highlight(.init(color: .blue))) var sphereModelMaterial = PhysicallyBasedMaterial() sphereModelMaterial.baseColor = .init(tint: color) let sphereMesh = MeshResource.generateSphere(radius: 0.01) let sphereModel = ModelEntity( mesh: sphereMesh, materials: [sphereModelMaterial] ) sphereModel.transform.translation = .init(x: 0, y: y, z: 0) sphereModel.components.set( [ collisionComponent, hoverEffectComponent ] ) return sphereModel }
效果验证
修改完成后,点击按钮切换isButtonTapped状态时,只会打印Body #1和Update #1,Second的Body #2和Update #2不会再被不必要地触发。
内容的提问来源于stack exchange,提问作者Pogos Anesyan
相关产品推荐
相关产品推荐

