You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可展示多类型对象的SwiftUI复用视图?

SwiftUI视图复用方案咨询

现有如下SwiftUI视图代码:

struct CardView: View {
    let item: Items

    var body: some View {
        VStack(alignment: .center) {
            Text("\(item.name)")
                .font(.headline)
                .accessibilityAddTraits(.isHeader)
            Spacer()
            HStack {
                Label("\(item.description)", systemImage: "questionmark.square.dashed")
            }
            .font(.caption)
        }
        .padding()
    }
}

struct CardView_Previews: PreviewProvider {
    static var item = Items.abilityScores[0]
    static var previews: some View {
        CardView(item: item)
            .previewLayout(.fixed(width: 400, height: 60))
    }
}

(Items.abilityScores是自定义类文件中生成的Items类型列表)

我希望复用该视图展示多种不同类型的对象,目前想到几种实现思路:

  • 为Item创建基类,让其他拥有更多属性的类继承它,再传入视图中,但不确定是否可行。
  • 为每个类型单独创建类及对应的视图,尽管它们的实现与用法非常相似。
  • 其他方案?比如将参数设为AnyObject,确保所有对象拥有相同字段名,在展示前检查字段是否存在。

请问方案1是否可行?还是只能选择方案2重复创建相似视图?


解答

方案1的可行性

方案1可行,但在Swift中并非最优选择。如果你的各类对象都是类(Class)而非结构体(Struct),可以定义一个包含name和description属性的基类,让其他类继承它,之后CardView的参数类型改为这个基类即可。示例如下:

class BaseItem {
    let name: String
    let description: String
    
    init(name: String, description: String) {
        self.name = name
        self.description = description
    }
}

// 子类示例
class AbilityScore: BaseItem {
    let value: Int
    
    init(name: String, description: String, value: Int) {
        self.value = value
        super.init(name: name, description: description)
    }
}

// 修改后的CardView
struct CardView: View {
    let item: BaseItem

    var body: some View {
        // 原有视图代码不变
    }
}

但Swift更推荐使用**协议(Protocol)**而非类继承,尤其是当你的数据类型可能是结构体时(结构体不支持继承),协议的灵活性更高。

更优方案:使用协议

定义一个协议,要求实现name和description属性,让所有需要展示的类型遵循该协议,这样CardView可以接收任意遵循该协议的类型,不管是类还是结构体:

protocol CardDisplayable {
    var name: String { get }
    var description: String { get }
}

// 让原有Items类型遵循协议
extension Items: CardDisplayable {}

// 其他类型也遵循协议,比如结构体
struct Weapon: CardDisplayable {
    let name: String
    let description: String
    let damage: Int
}

// 修改CardView
struct CardView: View {
    let item: any CardDisplayable

    var body: some View {
        // 原有视图代码不变
    }
}

这种方式无需继承,支持值类型(结构体)和引用类型(类),完全符合Swift的设计理念,是复用视图的最佳实践。

方案3的问题

使用AnyObject并检查字段存在的方式不推荐:

  • 失去编译时类型安全,容易因字段名拼写错误导致运行时崩溃。
  • 代码可读性差,需要大量的类型转换和可选绑定,维护成本高。

方案2的弊端

为每个类型单独创建视图会导致大量重复代码,违背DRY(Don't Repeat Yourself)原则,后续修改UI逻辑时需要在多个地方同步修改,极易出错。

内容的提问来源于stack exchange,提问作者phroureo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 13:42:48