我定义了一个
CustomProtocol
,它需要一个唯一的标识符。我还创建了一个实现此协议的CustomModel
。尽管具有 Identifiable
协议要求的 id 属性,但我无法将 CustomModel
用作 SwiftUI 中的可识别类型。
protocol CustomProtocol: Identifiable {
var id: String { get }
}
struct CustomModel: CustomProtocol {
let id = UUID().uuidString
}
class CustomModelStore: ObservableObject {
@Published var models: [any CustomProtocol] = []
init() {
models = Array(repeating: CustomModel(), count: 10)
}
}
struct CustomProtocolView: View {
@StateObject var store = CustomModelStore()
@State var selectedModel: (any CustomProtocol)?
var body: some View {
VStack {
ForEach(store.models) { model in
Text(model.id)
.font(.footnote)
.onTapGesture {
selectedModel = model
}
}
.sheet(item: $selectedModel) { model in
Text(model.id)
.font(.subheadline)
}
}
}
}
struct CustomProtocolView_Previews: PreviewProvider {
static var previews: some View {
CustomProtocolView()
}
}
当然我可以在
id
中指定ForEach
,但是这种方式对我来说是不可接受的,因为我没有机会在.sheet
或.fullScreenCover
视图修饰符中这样做。在我的商店中,我也无法将类型从 CustomProtocol
更改为 CustomModel
这是标准的“协议存在(
any
类型)不符合协议。” selectedModel
不符合 CustomProtocol(或 Identifiable)。只有具体类型符合协议。
正如 Baglan 指出的那样,您可以通过传递
id: \.id
来处理 ForEach,但是您必须重新设计 .sheet
以不需要可识别的,或者您需要摆脱 any CustomProtocol
并使用混凝土类型。
具体如何工作在很大程度上取决于 CustomProtocol 的作用以及
models
中的对象类型。如果它们都是同一个对象(就像你的例子),那么你通常会让 CustomModelStore 和 CustomProtocolView 通用:
// Make generic over the model
class CustomModelStore<Model: CustomProtocol>: ObservableObject {
@Published var models: [Model] = []
// Initialize with the correct type, not hard-coded CustomModel
// init() {
// models = Array(repeating: CustomModel(), count: 10)
// }
}
// Make generic over the model
struct CustomProtocolView<Model: CustomProtocol>: View {
@StateObject var store = CustomModelStore<Model>()
@State var selectedModel: Model?
// Now your original code is fine.
var body: some View {
// ...
}
}
如果数组中有不同的类型,那么设计正确的解决方案将取决于所有这些的实际工作方式。一般来说,答案是
.map
对象到视图使用的一些具体模型结构而不是底层对象,但这取决于你的问题。