也许你可以照亮我黑暗无知的道路?我的目标是一个 SwiftUI 应用程序,我可以在其中将组件注入视图并让系统在这些组件上运行一些逻辑。每次我觉得我已经到了;基础的弱点让我重回起点。让我告诉你我试过什么!
protocol DataComponent: Component {
associatedtype DataType
var data: DataType { get set }
init()
init(data: DataType)
}
struct NameComponent: DataComponent { var data = "default" }
这是基本的数据组件。我将EntitasKit用于我的实体组件系统。
enum Entities {
case source
...
}
protocol EntityServicing {
var context: Context { get }
func entity(_ entity: Entities) -> Entity
}
final class EntityService: EntityServicing {
let context = Context()
func entity(_ entity: Entities) -> Entity {
switch entity {
case .source:
guard let first = context.entities.first else {
return context.createEntity()
}
return first
...
}
}
}
这是应用程序范围的 EntityService,它包含实体及其组件的上下文。它是使用Resolver注入的。
现在,这是 SwiftUI 中的视图。
struct ContentView: View {
@EntityState(.source, "Dale Cooper") var name: NameComponent
var body: some View {
Button(action: { name = NameComponent(data: "Dougie Jones") }) {
Text(name.data).padding()
}
}
}
EntityState 就是它听起来的样子;一个属性包装器,它捕获实体组件的状态并使其对 SwiftUI 可见。
@propertyWrapper
struct EntityState<C>: DynamicProperty where C: DataComponent {
@State private var state = C()
@Injected private var entityService: EntityServicing
private let entityID: Entities
private var entity: Entity { entityService.entity(entityID) }
init(_ id: Entities, _ data: C.DataType? = nil) {
self.entityID = id
if let data = data {
entity.set(C(data: data))
} else {
guard entity.has(C.cid) else {
entity.set(state)
return
}
}
_state = State(wrappedValue: entity.get()!)
}
public var wrappedValue: C {
get { entity.get()! }
nonmutating set { state = newValue }
}
mutating func update() {
entity.set(state)
}
}
SwiftUI 会注意到动态属性中的状态变化。单击该按钮将重新呈现视图并更新实体上的组件。
当我添加一个更改组件的系统时,这个基础开始崩溃。
class NameSystem: ReactiveSystem {
@Injected var entityService: EntityServicing
lazy var collector: Collector = Collector(
group: entityService.context.group(
Matcher(
all: [NameComponent.cid]
)
),
type: .added
)
let name = "Name System Added Here!"
func execute(entities: Set<Entity>) {
for e in entities {
if let data = e.get(NameComponent.self)?.data {
e.set(NameComponent(data: "Special Agent " + data))
}
}
}
}
问题是 SwiftUI 状态永远不会被通知,但我希望 SwiftUI 知道组件在系统更新时发生的变化。
我尝试了许多不同的方法:
- 使用@Published 将组件/实体/上下文作为 ObservableObject。我的理解是,这是将数据模型连接到 SwiftUI 的好方法。我有@Published 组件、组件和实体中的数据,但一直阻止我的问题是让 EntityState 重新渲染视图。由于上下文被注入,我不能使用其他属性包装器,如@ObservedObject。
- 绑定。我尝试观察 EntityState 中的组件对象并将组件绑定传递给实体,但我无法将绑定存储到同一个字典中的不同 DataComponents。我什至不确定对绑定的更改是否会返回到 EntityState 以重新呈现视图。同样的限制也适用于@States 的绑定。无论如何,我已经把自己困在这条路上了。
- 发布者和订阅者。将 EntityState 转换为订阅者以便它可以接收值需要它是可变的。要使 EntityState 可变,它必须成为一个类,但这会破坏 SwiftUI 重新渲染。发布者可以是某种主题或组件的自定义发布者。我只是无法解决如何接收值并将它们分配给状态,因此 SwiftUI 重新呈现。整个联合区域对我来说有点模糊。可能有一种聪明的方法可以做一些我不知道的惊人的事情。
如何从系统将组件分配给状态,以便 SwiftUI 重新渲染?
我希望你发现这是一个有趣的问题来解决,它让你和我一样对解决方案感到好奇。至少让我很兴奋。
祝大家拥有美好的一天!