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

Swift中如何实现带TextField输入的通用匹配验证及动态卡片添加?

需求说明

我需要实现以下功能:

  • 制作一个带通用函数的按钮,该函数能从数组中随机选取元素,检查TextField的输入是否匹配该元素对应的正确内容,匹配时返回true
  • 将现有代码改造为通用实现,支持常量、整数等多种类型(不局限于变量)
  • 支持通过按钮向视图中添加更多任务卡片

现有代码如下:

//MARK: LOGIC
import Foundation
import Observation

@Observable
class VariablesViewModel {
    var textField = ""
    private var words = ["\"Hello\"","\"Hello \"" ,"\"hello\"", "\" Hello\"", "\"      hello\""]
    
    func wordMatch() -> Bool {
        words.contains(where: textField.contains)
    }
}

//MARK: VIEW 
import SwiftUI

struct Variables: View {
    @State var viewModel = VariablesViewModel()
    var body: some View {
        VStack(alignment: .leading, spacing: 40) {
          Text("Create a variable called \"greeting\" and assign the value     \"Hello\".")
                .font(.title2)
                .fontWeight(.semibold)
            HStack {
                Text("1")
                    .font(.title2)
                    .foregroundStyle(.gray)
                Text("var")
                    .font(.title2)
                    .foregroundStyle(.pink)
                Text("greeting =")
                    .font(.title2)
                TextField(text: $viewModel.textField ) {
                    Text("Tap to assign \"Hello\"")
                        .font(.title2)
                }
                .font(.title2)
                .foregroundStyle(.orange)
                if viewModel.wordMatch() {
                    Image(systemName: "checkmark.circle.fill")
                        .imageScale(.large)
                        .foregroundStyle(.green)
                }
            }
        }
        .alignment(.topLeading)
    }
}
#Preview {
    Variables()
}

extension View {
    @ViewBuilder func alignment(_ alignment: Alignment) -> some View {
        self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: alignment)
    }
}

解决方案

实现思路

  1. 定义通用任务结构:用结构体封装不同类型的任务,包含任务描述、正确答案集合、任务类型标识,覆盖变量、常量、整数等场景
  2. 通用ViewModel改造:支持管理多任务列表,实现随机选任务、添加新任务、通用输入匹配检查的核心逻辑
  3. 动态视图适配:让视图根据ViewModel中的任务列表动态渲染卡片,添加按钮触发任务添加操作

完整代码实现

1. 通用任务模型与ViewModel
import Foundation
import Observation

// 任务类型枚举,区分变量、常量、整数场景
enum TaskType: String {
    case variable = "var"
    case constant = "let"
    case integer = "integer"
}

// 通用任务结构体,封装任务的所有核心信息
struct Task {
    let id: UUID
    let description: String
    let correctAnswers: [String]
    let type: TaskType
    var inputText: String = "" // 存储当前任务的用户输入
}

@Observable
class GenericTaskViewModel {
    var tasks: [Task] = []
    
    init() {
        // 初始化默认任务
        let initialTask = Task(
            id: UUID(),
            description: "Create a variable called \"greeting\" and assign the value \"Hello\".",
            correctAnswers: ["\"Hello\"", "\"Hello \"", "\"hello\"", "\" Hello\"", "\"      hello\""],
            type: .variable
        )
        tasks.append(initialTask)
    }
    
    // 通用输入匹配检查:判断当前输入是否匹配任务的任一正确答案
    func isInputCorrect(for task: Task) -> Bool {
        task.correctAnswers.contains(where: task.inputText.contains)
    }
    
    // 添加随机任务:从预设任务库中随机选一个添加到列表
    func addRandomTask() {
        let presetTasks: [Task] = [
            Task(
                id: UUID(),
                description: "Create a constant called \"maxScore\" and assign the value 100.",
                correctAnswers: ["100", " 100", "100 "],
                type: .constant
            ),
            Task(
                id: UUID(),
                description: "Create an integer variable called \"age\" and assign the value 25.",
                correctAnswers: ["25", " 25", "25 "],
                type: .integer
            ),
            Task(
                id: UUID(),
                description: "Create a variable called \"name\" and assign the value \"Alice\".",
                correctAnswers: ["\"Alice\"", "\"Alice \"", "\"alice\"", "\" Alice\""],
                type: .variable
            )
        ]
        guard let randomTask = presetTasks.randomElement() else { return }
        tasks.append(randomTask)
    }
    
    // 更新指定任务的输入内容
    func updateInput(for taskId: UUID, newText: String) {
        guard let index = tasks.firstIndex(where: { $0.id == taskId }) else { return }
        tasks[index].inputText = newText
    }
}
2. 通用视图实现
import SwiftUI

struct GenericTaskView: View {
    @State var viewModel = GenericTaskViewModel()
    
    var body: some View {
        VStack(alignment: .leading, spacing: 30) {
            // 添加任务按钮
            Button(action: viewModel.addRandomTask) {
                Text("Add New Task")
                    .font(.title3)
                    .fontWeight(.semibold)
                    .padding()
                    .background(Color.blue)
                    .foregroundColor(.white)
                    .cornerRadius(10)
            }
            
            // 动态渲染所有任务卡片
            ForEach($viewModel.tasks) { $task in
                TaskCard(task: $task, isCorrect: viewModel.isInputCorrect(for: task))
            }
        }
        .padding()
        .alignment(.topLeading)
    }
}

// 单个任务卡片视图
struct TaskCard: View {
    @Binding var task: Task
    let isCorrect: Bool
    
    var body: some View {
        VStack(alignment: .leading, spacing: 15) {
            Text(task.description)
                .font(.title2)
                .fontWeight(.semibold)
            
            HStack(alignment: .center, spacing: 10) {
                // 简化的任务序号(取UUID首字符)
                Text("\(task.id.uuidString.prefix(1))")
                    .font(.title2)
                    .foregroundStyle(.gray)
                
                // 根据任务类型显示关键字
                if task.type != .integer {
                    Text(task.type.rawValue)
                        .font(.title2)
                        .foregroundStyle(task.type == .variable ? .pink : .purple)
                }
                
                // 根据任务类型显示对应的前缀文本
                switch task.type {
                case .variable:
                    Text("greeting =")
                        .font(.title2)
                case .constant:
                    Text("maxScore =")
                        .font(.title2)
                case .integer:
                    Text("var age =")
                        .font(.title2)
                }
                
                TextField(
                    text: $task.inputText,
                    prompt: Text(getPlaceholder(for: task.type))
                        .font(.title2)
                ) {
                    EmptyView()
                }
                .font(.title2)
                .foregroundStyle(.orange)
                
                // 匹配成功时显示对勾
                if isCorrect {
                    Image(systemName: "checkmark.circle.fill")
                        .imageScale(.large)
                        .foregroundStyle(.green)
                }
            }
            .padding(.bottom, 10)
            .border(.gray.opacity(0.3))
            .padding(.horizontal, 5)
        }
    }
    
    // 根据任务类型返回对应的占位提示文本
    private func getPlaceholder(for type: TaskType) -> String {
        switch type {
        case .variable:
            return "Tap to assign \"Hello\""
        case .constant:
            return "Tap to assign 100"
        case .integer:
            return "Tap to assign 25"
        }
    }
}

#Preview {
    GenericTaskView()
}

extension View {
    @ViewBuilder func alignment(_ alignment: Alignment) -> some View {
        self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: alignment)
    }
}

关键说明

  • 通用任务结构:Task结构体通过TaskType枚举适配不同任务场景,每个任务独立维护自己的正确答案集合,扩展性强
  • ViewModel通用化:GenericTaskViewModel统一管理任务列表,核心逻辑与任务类型解耦,支持随机添加任务、通用输入检查
  • 动态视图:通过ForEach($viewModel.tasks)实现任务卡片的动态渲染,每个卡片根据任务类型自动调整显示内容,匹配成功时实时反馈

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 17:49:53