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

SwiftUI:如何从列表选中项关联的底部弹窗TextField获取值并更新Core Data

问题描述

左侧是通过ForEach创建的用户列表,点击任意列表项会弹出绑定选中项的BottomSheet。需要实现:点击保存按钮时获取TextField的值,更新Core Data中对应的NSManagedObject。当前遇到的问题是无法在列表项的onTapGesture中为onSave处理程序赋值,导致无法完成Core Data对象的更新逻辑。

对应的代码实现如下:

struct SettingsView: View {
    @State var isTextInputPresented = false
    @State var editingTextFieldValue = ""
    private var onSave: ((String) -> Void)? = nil

    var body: some View {
        ZStack {
            ScrollView {
              ForEach(users, id: \.self) { user in // users are from @FetchRequest
                HStack {
                    TextLabel(user.name, color: theme.secondTeamColor)
                        .padding(EdgeInsets(vertical: 10))
                    Spacer()
                }
                .onTapGesture {
                    editingTextFieldValue = user.name
                    isTextInputPresented = true
                    // cannot assign onSave handler here🤷🏼‍♂️
                }
              }
            }
            BottomSheetView(title: "Edit input", isShowing: $isTextInputPresented) {
                TextInputView(text: $editingTextFieldValue, onSave: onSave)
            }
        }
    }
}


import SwiftUI

struct TextInputView: View {
    @Environment(Theme.self) private var theme
    @Binding var text: String
    @FocusState private var focus: Bool
    var onSave: ((String) -> Void)?
    var body: some View {
        HStack(spacing: 20) {
            TextField("", text: $text, prompt: Text("Placeholder").foregroundColor(.gray))
                .padding(10)
                .multilineTextAlignment(.center)
                .font(.system(size: 24))
                .foregroundStyle(theme.backgroundColor)
                .tint(theme.backgroundColor.opacity(0.4))
                .focused($focus)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(theme.backgroundColor.opacity(0.5), lineWidth: 3)
                )
            Button {
                onSave?(text)
            } label: {
                Image(systemName: "checkmark")
            }
            .font(.bold(withSize: 22))
            .frame(width: 56, height: 56)
            .background(theme.backgroundColor)
            .foregroundStyle(theme.textColor)
            .cornerRadius(10)
        }
        .padding(20)
        .onAppear {
            focus = true
        }
    }
}
解决方案

核心思路是保存当前选中的Core Data对象实例,而非仅存储文本值,这样在点击保存时可直接修改该对象属性并完成持久化。

修改后的SettingsView代码

struct SettingsView: View {
    @State var isTextInputPresented = false
    @State var editingTextFieldValue = ""
    @State private var selectedUser: User? // 存储选中的Core Data对象(User为你的NSManagedObject子类)
    @Environment(\.managedObjectContext) private var viewContext // 注入Core Data上下文
    @FetchRequest(sortDescriptors: []) private var users: FetchedResults<User> // 补充完整FetchRequest定义

    var body: some View {
        ZStack {
            ScrollView {
                ForEach(users, id: \.self) { user in
                    HStack {
                        TextLabel(user.name ?? "", color: theme.secondTeamColor)
                            .padding(EdgeInsets(vertical: 10))
                        Spacer()
                    }
                    .onTapGesture {
                        editingTextFieldValue = user.name ?? ""
                        selectedUser = user // 记录当前选中的用户对象
                        isTextInputPresented = true
                    }
                }
            }
            BottomSheetView(title: "Edit input", isShowing: $isTextInputPresented) {
                TextInputView(text: $editingTextFieldValue) { newName in
                    guard let targetUser = selectedUser else { return }
                    // 修改Core Data对象属性
                    targetUser.name = newName
                    // 持久化修改
                    do {
                        try viewContext.save()
                    } catch {
                        let nsError = error as NSError
                        fatalError("Core Data保存失败: \(nsError), \(nsError.userInfo)")
                    }
                    isTextInputPresented = false // 关闭BottomSheet
                }
            }
        }
    }
}

修改后的TextInputView代码

struct TextInputView: View {
    @Environment(Theme.self) private var theme
    @Binding var text: String
    @FocusState private var focus: Bool
    let onSave: (String) -> Void // 改为非可选类型,确保调用时有实现

    var body: some View {
        HStack(spacing: 20) {
            TextField("", text: $text, prompt: Text("Placeholder").foregroundColor(.gray))
                .padding(10)
                .multilineTextAlignment(.center)
                .font(.system(size: 24))
                .foregroundStyle(theme.backgroundColor)
                .tint(theme.backgroundColor.opacity(0.4))
                .focused($focus)
                .cornerRadius(10)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(theme.backgroundColor.opacity(0.5), lineWidth: 3)
                )
            Button {
                onSave(text) // 直接调用保存闭包
            } label: {
                Image(systemName: "checkmark")
            }
            .font(.bold(withSize: 22))
            .frame(width: 56, height: 56)
            .background(theme.backgroundColor)
            .foregroundStyle(theme.textColor)
            .cornerRadius(10)
        }
        .padding(20)
        .onAppear {
            focus = true
        }
    }
}

关键改动说明

  1. 新增selectedUser状态:点击列表项时记录完整的Core Data对象,避免仅存文本值无法关联到原对象的问题。
  2. 直接注入保存逻辑:在创建TextInputView时直接传入保存闭包,无需单独维护onSave属性,解决了原代码中无法在onTapGesture赋值的问题。
  3. Core Data上下文操作:通过@Environment(\.managedObjectContext)获取上下文,修改对象属性后调用save()完成持久化。
  4. 简化闭包参数:将TextInputView的onSave改为非可选类型,减少空值判断的繁琐,同时保证调用时必有逻辑实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 15:15:03