简体   繁体   English

我可以在 SwiftUI 的 state 变量中使用泛型类型吗?

[英]Can I use a generic type in a state variable in SwiftUI?

Is there a way to use a generic type in a state variable in Swift?有没有办法在 Swift 的 state 变量中使用泛型类型?

I have a view which presents 3 buttons.我有一个显示 3 个按钮的视图。 Each button loads an array of items of either MyGenericObject<TypeA> , MyGenericObject<TypeB> or MyGenericObject<TypeC> type and the aim is to then present a list of objects from the chosen array.每个按钮加载MyGenericObject<TypeA>MyGenericObject<TypeB>MyGenericObject<TypeC>类型的项目数组,然后目的是显示所选数组中的对象列表。

But XCode throws an error when I write @State var objects: [MyGenericObject<T>]?但是当我编写@State var objects: [MyGenericObject<T>]?

The error is there even when working with an individual object instead of an array, as in the following example:即使使用单个 object 而不是数组,也会出现错误,如下例所示:

struct ObjectView: View {

// I would like the following to be @State var object: MyGenericObject<T> instead...
    
@State var object: MyGenericObject<TypeA>? 
    
    var body: some View {
        
        Text("\(object?.data.name)")

// because I would then like to do something like this, if possible:

    if type(of: object) == MyGenericObject<TypeA> {
        ...show list of item of this type
    } else

    if type(of: object) == MyGenericObject<TypeB> {
        ...show list of item of this type
    }

    else {
        ...show list of item of type MyGenericObject<TypeC>
    }

// or better yet, use a switch

}

How can I use a generic type in a @State variable in SwiftUI?如何在 SwiftUI 的 @State 变量中使用泛型类型? And is there a better way to achieve what I want?有没有更好的方法来实现我想要的?

I stumbled across this question and took a look at the gist.我偶然发现了这个问题,并查看了要点。 It's a little hard to tell if this is exactly what you want, but it seems like it might get you on the right path:很难判断这是否正是您想要的,但似乎它可能会让您走上正确的道路:

protocol InstrumentType {
    var name : String { get set }
    var int : Int { get set }
    var bool : Bool { get set }
}

class Instrument<T : InstrumentType>: ObservableObject {
    @Published var object: T
    
    init(object: T){
        self.object = object
    }
}

class Trumpet: NSObject, ObservableObject, InstrumentType {
    @Published var name: String
    @Published var int: Int
    @Published var bool: Bool
    
    init(name: String = "newTrumpet", int: Int = 1, bool: Bool = true){
        self.name = name
        self.int = int
        self.bool = bool
    }
}

class Guitar: NSObject, ObservableObject, InstrumentType  {
    @Published var name: String
    @Published var int: Int
    @Published var bool: Bool
    
    init(name: String = "newGuitar", int: Int = 2, bool: Bool = true){
        self.name = name
        self.int = int
        self.bool = bool
    }
}

class Clarinet: NSObject, ObservableObject, InstrumentType  {
    @Published var name: String
    @Published var int: Int
    @Published var bool: Bool
    
    init(name: String = "newClarinet", int: Int = 3, bool: Bool = true){
        self.name = name
        self.int = int
        self.bool = bool
    }
}

struct ContentView : View {
    var body: some View {
        _ContentView(instrument: Instrument(object: Trumpet(name: "testTrumpet")))
    }
}

//VIEWS
struct _ContentView<T : InstrumentType>: View {
    @StateObject var instrument: Instrument<T>
    
    var body: some View {
        InstrumentView(instrument: instrument)
            .environmentObject(instrument)
            .frame(width: 300, height: 100)
    }
}

struct InstrumentView<T : InstrumentType>: View {
    
    @ObservedObject var instrument: Instrument<T>
    
    var body: some View {
        
        VStack {
            if type(of: instrument) == Instrument<Trumpet>.self {
                Text("Instrument is a trumpet")
                Text("\(instrument.object.name)")
                Text("\(instrument.object.int)")
                Text("\(instrument.object.bool ? "true" : "false")")
            } else
            
            if type(of: instrument) == Instrument<Guitar>.self {
                Text("Instrument is a guitar")
                Text("\(instrument.object.name)")
                Text("\(instrument.object.int)")
            }
            
            else {
                Text("Instrument is a clarinet")
            }
            
            Button("Update int") {
                instrument.object.int += 1
            }
        }  
    }
}
  1. Added a protocol InstrumentType that defines the available properties on each instrument -- that allowed me to get rid of the Metadata , since it was all stored on each instrument anyway添加了一个协议InstrumentType ,它定义了每个仪器上的可用属性——这使我能够摆脱Metadata ,因为它都存储在每个仪器上

  2. Constrained each generic to InstrumentType将每个泛型约束到InstrumentType

  3. I was a little confused by the @StateObject for each type of instrument -- I assumed that maybe what I did was what you were looking for (one generic @StateObject, but perhaps this is where the answer differs from the intent)我对每种乐器的@StateObject感到有些困惑——我认为我所做的可能就是您要寻找的东西(一个通用的@StateObject,但也许这就是答案与意图不同的地方)

  4. I was able to use environmentObject and object.name in the way you were hoping我能够以您希望的方式使用environmentObjectobject.name

  5. Added a Button to show that the @Published properties propagate correctly.添加了一个 Button 以显示 @Published 属性正确传播。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM