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

SwiftUI:如何用多态让模型类生成自身视图并实现状态更新?

SwiftUI 题型统一协议+状态自动更新解决方案

核心思路

让每个题型模型同时承担数据存储(ObservableObject)和视图生成的职责,通过统一协议规范接口,主视图仅需遍历模型数组即可渲染所有题型,新增题型无需修改主视图,完全符合开闭原则。

实现步骤

1. 定义通用题型协议

协议继承ObservableObject,确保模型具备状态可观察能力,同时约定通用属性和视图生成方法:

import SwiftUI
import Combine

protocol QuestionType: ObservableObject, Identifiable {
    var id: UUID { get }
    var isEnabled: Bool { get set }
    var title: String { get }
    func makeView() -> AnyView
}

// 给所有QuestionType默认实现Identifiable的id属性
extension QuestionType {
    var id: UUID { UUID() }
}

2. 实现具体题型模型与视图

每个题型模型实现QuestionType协议,用@Published标记需要触发视图更新的属性,同时返回专属视图:

判断题示例
// 判断题专属视图
struct TrueFalseQuestionView: View {
    @ObservedObject var model: TrueFalseQuestion
    
    var body: some View {
        HStack {
            Toggle(model.title, isOn: $model.isEnabled)
            Spacer()
            Toggle("正确答案:对", isOn: $model.isCorrectAnswerTrue)
                .disabled(!model.isEnabled)
        }
        .padding(.vertical, 8)
    }
}

// 判断题模型
class TrueFalseQuestion: QuestionType {
    @Published var isEnabled: Bool
    @Published var isCorrectAnswerTrue: Bool
    let title: String = "判断题"
    
    init(isEnabled: Bool = true, isCorrectAnswerTrue: Bool = true) {
        self.isEnabled = isEnabled
        self.isCorrectAnswerTrue = isCorrectAnswerTrue
    }
    
    func makeView() -> AnyView {
        AnyView(TrueFalseQuestionView(model: self))
    }
}
填空题示例
// 填空题专属视图
struct FillBlankQuestionView: View {
    @ObservedObject var model: FillBlankQuestion
    
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Toggle(model.title, isOn: $model.isEnabled)
            TextField("请输入正确答案", text: $model.correctAnswer)
                .textFieldStyle(.roundedBorder)
                .disabled(!model.isEnabled)
        }
        .padding(.vertical, 8)
    }
}

// 填空题模型
class FillBlankQuestion: QuestionType {
    @Published var isEnabled: Bool
    @Published var correctAnswer: String
    let title: String = "填空题"
    
    init(isEnabled: Bool = true, correctAnswer: String = "") {
        self.isEnabled = isEnabled
        self.correctAnswer = correctAnswer
    }
    
    func makeView() -> AnyView {
        AnyView(FillBlankQuestionView(model: self))
    }
}

3. 主视图与题型管理

主视图通过QuestionManager管理题型数组,无需判断具体类型,直接遍历调用模型的makeView()方法:

struct QuestionCreatorView: View {
    @StateObject private var questionManager = QuestionManager()
    
    var body: some View {
        NavigationStack {
            List {
                ForEach(questionManager.questions) { question in
                    question.makeView()
                }
                .onDelete(perform: questionManager.removeQuestion)
                
                HStack(spacing: 16) {
                    Button("添加判断题") {
                        questionManager.addQuestion(TrueFalseQuestion())
                    }
                    Button("添加填空题") {
                        questionManager.addQuestion(FillBlankQuestion())
                    }
                }
                .padding(.vertical, 8)
            }
            .navigationTitle("测试题创建器")
            .toolbar {
                EditButton()
            }
        }
    }
}

// 题型数组管理器
class QuestionManager: ObservableObject {
    @Published var questions: [any QuestionType] = []
    
    func addQuestion(_ question: any QuestionType) {
        questions.append(question)
    }
    
    func removeQuestion(at offsets: IndexSet) {
        questions.remove(atOffsets: offsets)
    }
}

关键说明

  • 状态自动更新:每个模型都是ObservableObject,@Published标记的属性变化会自动通知关联视图刷新,解决了你之前遇到的视图不更新问题。
  • 开闭原则:新增题型只需实现QuestionType协议,主视图和管理器无需任何修改。
  • 解耦设计:主视图无需关心具体题型类型,仅依赖统一协议,完全解耦了视图与具体业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 20:45:54