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

如何在SwiftUI的TableView中为TextField实现自动补全功能?

解决SwiftUI TableView中绑定数组对象的TextField自动补全失效问题

问题背景

需要给SwiftUI TableView中作为数据列的TextField添加自动补全功能,允许输入自定义值(不强制匹配建议项)。独立TextField的自动补全已实现,但当TextField绑定到ObservableObject数组中的对象属性时,封装的CompletingTextField内的.onChange闭包从未触发,建议项无法随输入更新。

问题根源

  1. 自定义Binding未正确关联到Thing的@Published属性,导致输入变化无法被@Binding检测到
  2. 使用@State存储建议项并依赖.onChange更新的方式,在TableView的cell环境下无法正常响应实时输入

解决方案

1. 简化自定义Binding,直接绑定到ObservableObject属性

在TableView的cell中,将Thing实例声明为@ObservedObject,直接使用其@Published属性的绑定,无需手动通过数组索引查找更新。

2. 重构CompletingTextField,移除.onChange依赖

不需要用@State存储建议项,直接在textInputSuggestions闭包中实时计算匹配的建议,避免依赖.onChange触发更新。

完整修正代码

数据模型(保持不变)

enum CommonLocation: String, CaseIterable, Identifiable {
    var id: Self { self }

    case Bathroom, Bedroom, DiningRoom, Foyer, Kitchen, LivingRoom
}

class Thing: Identifiable, Hashable, ObservableObject {
    static func == (lhs: Thing, rhs: Thing) -> Bool {
        lhs.id == rhs.id
    }

    @Published var name: String
    @Published var location: String
    let id: UUID = UUID()

    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }

    init(title: String, location: String) {
        self.name = title
        self.location = location
    }
}

class ThingSet: ObservableObject, Identifiable {
    @Published var things: [Thing] = []
    let id = UUID()

    init(things: [Thing]) {
        self.things = things
    }
}

重构后的CompletingTextField

struct CompletingTextField: View {
    struct Choice: Identifiable {
        let value: String
        let id = UUID()
        init(_ value: String) {
            self.value = value
        }
    }

    @Binding var value: String
    private let allChoices: [String]

    init(value: Binding<String>, choices: [String]) {
        self._value = value
        self.allChoices = choices
    }

    private func matchingChoices() -> [Choice] {
        do {
            let filtered = try allChoices.filter(#Predicate<String> { $0.contains(value) })
            return filtered.map(Choice.init)
        } catch {
            return []
        }
    }

    var body: some View {
        TextField("", text: $value)
            .textInputSuggestions {
                ForEach(matchingChoices()) { choice in
                    Text(choice.value)
                        .textInputCompletion(choice.value)
                }
            }
    }
}

修正后的ThingSetView

struct ThingSetView: View {
    @ObservedObject private var thingSet: ThingSet

    init(thingSet: ThingSet) {
        self.thingSet = thingSet
    }

    var body: some View {
        Table(of: Thing.self) {
            TableColumn("Name") { thing in
                @ObservedObject var observedThing = thing
                TextField("", text: $observedThing.name)
            }
            TableColumn("Location") { thing in
                @ObservedObject var observedThing = thing
                CompletingTextField(
                    value: $observedThing.location,
                    choices: CommonLocation.allCases.map(\.rawValue)
                )
            }
        } rows: {
            ForEach(thingSet.things) { thing in
                TableRow(thing)
            }
        }
    }
}

关键改动说明

  • 绑定优化:在TableColumn的cell内部将thing声明为@ObservedObject,确保Thing的@Published属性变化能实时传递到Binding,触发视图更新
  • 移除.onChange:直接在textInputSuggestions中实时计算匹配建议,无需依赖状态变量和.onChange闭包,避免了绑定更新不及时的问题
  • 简化逻辑:去掉了冗余的@State存储建议项的逻辑,让建议计算与输入值实时同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 13:44:56