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

SwiftUI中遍历@AppStorage变量创建TextField的报错排查请求

遍历@AppStorage变量创建TextField的问题分析与解决

问题描述

尝试遍历@AppStorage变量并生成带预设值的TextField,调整$符号后仍无法正常运行,简化后的代码如下:

import SwiftUI

struct ContentView: View {
    static let store = UserDefaults(suiteName: "group.not-important.etc")
    
    @AppStorage("variable1", store: store) var variable1 = "a"
    @AppStorage("variable2", store: store) var variable2 = "b"
    @AppStorage("variable3", store: store) var variable3 = "c"
    @AppStorage("variable4", store: store) var variable4 = "d"
    @AppStorage("variable5", store: store) var variable5 = "e"
    
    var body: some View {
        NavigationStack {
            Form { 
                Section(header: Text("Variables")) {                    
                    HStack (alignment: .center) {
                        ForEach([
                            $variable1, // 也试过直接写variable1等
                            $variable2,
                            $variable3,
                            $variable4,
                            $variable5
                        ], id: \.self) { variable in // 也试过$variable
                            TextField(
                                "",
                                text: variable // 也试过$variable、variable.wrappedName等
                            )
                        }
                    }
                }             
            }
        }
    }
}

错误原因拆解

1. Generic struct 'ForEach' requires that 'Binding<String>' conform to 'Hashable'

ForEach使用id: \.self时,要求数组元素必须遵循Hashable协议,但Binding<String>本身并未实现该协议,直接把$variable1这类Binding对象放进数组遍历会触发类型不匹配错误。

2. Inferred projection type 'String' is not a property wrapper

把闭包参数写成$variable in是错误写法——闭包传入的参数已经是Binding<String>类型,$符号仅用于属性包装器的投影操作,普通变量(即使是Binding类型)不能加$做投影,因此触发语法错误。

3. Cannot convert value of type 'String' to expected argument type 'Binding<String>'

如果去掉$直接传variable1这类值,数组元素就是普通String,而TextField的text参数要求的是Binding<String>;此时写$variable相当于试图对普通String值做投影,既无对应语法,类型也完全不匹配。

解决方案

方案1:通过键名动态生成Binding(推荐)

把变量的键名存进数组,遍历键名时动态创建对应UserDefaults的Binding,无需单独定义多个@AppStorage变量:

import SwiftUI

struct ContentView: View {
    static let store = UserDefaults(suiteName: "group.not-important.etc")!
    // 存储每个键对应的默认值
    private let defaultValues = [
        "variable1": "a",
        "variable2": "b",
        "variable3": "c",
        "variable4": "d",
        "variable5": "e"
    ]
    // 所有变量的键名数组
    private let variableKeys = ["variable1", "variable2", "variable3", "variable4", "variable5"]
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Variables")) {
                    HStack(alignment: .center) {
                        ForEach(variableKeys, id: \.self) { key in
                            TextField(
                                "",
                                text: Binding(
                                    get: { Self.store.string(forKey: key) ?? self.defaultValues[key]! },
                                    set: { Self.store.set($0, forKey: key) }
                                )
                            )
                        }
                    }
                }
            }
        }
    }
}

方案2:给Binding做Hashable包装

如果不想修改原有@AppStorage变量的定义,可以创建一个遵循Hashable的包装结构体,把Binding对象包装后再遍历:

import SwiftUI

// 自定义可Hashable的Binding包装器
struct HashableBinding<T: Hashable>: Hashable {
    let binding: Binding<T>
    private let id: T
    
    init(_ binding: Binding<T>) {
        self.binding = binding
        self.id = binding.wrappedValue
    }
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
    }
    
    static func == (lhs: HashableBinding<T>, rhs: HashableBinding<T>) -> Bool {
        lhs.id == rhs.id
    }
}

struct ContentView: View {
    static let store = UserDefaults(suiteName: "group.not-important.etc")
    
    @AppStorage("variable1", store: store) var variable1 = "a"
    @AppStorage("variable2", store: store) var variable2 = "b"
    @AppStorage("variable3", store: store) var variable3 = "c"
    @AppStorage("variable4", store: store) var variable4 = "d"
    @AppStorage("variable5", store: store) var variable5 = "e"
    
    var body: some View {
        NavigationStack {
            Form {
                Section(header: Text("Variables")) {
                    HStack(alignment: .center) {
                        ForEach([
                            HashableBinding($variable1),
                            HashableBinding($variable2),
                            HashableBinding($variable3),
                            HashableBinding($variable4),
                            HashableBinding($variable5)
                        ], id: \.self) { wrapper in
                            TextField("", text: wrapper.binding)
                        }
                    }
                }
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 03:45:55