如何在 SwiftUI 中绑定 TextField,同时仍使用它来存储收藏夹?

问题描述 投票:0回答:1

我有一个文本字段,用户可以在其中手动输入位置字符串。这与我观察到的对象

AppState
绑定,我已将其重新标记为 (
app
),其中有一个名为
@Published var
destination
。这工作正常,我可以在文本字段中绑定手动输入的文本,如下所示:

TextField("Destination", text: $app.destination)

我已经开始尝试实现一个收藏夹系统,用户可以保存和更改最多 4 个目的地,以便快速输入而无需重新键入(一次只能在文本字段中输入 1 个目的地)。

当用户按下他们最喜欢的内容之一时,最喜欢的内容会出现在文本字段中,他们可以通过输入新内容并保存来更改它,或者按新的最爱来替换文本字段中的内容。用户还必须可以选择根本不输入收藏夹,并且也能够输入随机收藏夹。

到目前为止,我已经让这部分工作得相当好,并且对功能感到满意。

我的问题是,我不知道如何将文本字段中的任何内容绑定到我的

ObservedObject
app.destination
。经过一番故障排除后,我发现只有手动输入的文本才会存储在
app.destination
中。在文本字段中出现最喜欢的内容不会绑定。

我尝试过

.onChange()
.onTapGesture()
.focused()
的变体来运行 if/else 块,该块会简单地将文本字段中的文本分配给
app.destination
,但它仍然有错误,您无法保存新的最爱...

我正在绕圈试图解决这个问题,非常感谢一些帮助。

我有一个最小的可复制示例供您复制和粘贴。谢谢你

主视图

struct ContentView: View {
    @StateObject private var favoritesViewModel = FavoritesViewModel()
    @ObservedObject var app = AppState()
    @State private var newLocation = ""
    @State private var selectedLocationIndex = 0
    @State private var userSelectedFavourite: String = ""
    @State private var favouritePressed: Bool = false
    @FocusState private var isFocused: Bool
    
    var body: some View {
        VStack {
            HStack {
                //.........THIS IS THE ISSUE TEXTFIELD
                TextField("Destination", text: (favouritePressed && !isFocused) ? $favoritesViewModel.favoriteLocations[selectedLocationIndex] : $newLocation)
                //.....................................
                .focused($isFocused)
                .contentShape(.rect)
                .textInputAutocapitalization(.characters)
                .disableAutocorrection(true)
                .padding()
                .frame(width: 280, height: 60)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.gray, lineWidth: 2)
                )
                
                Button("Save") {
                    saveLocation(locationIndex: selectedLocationIndex)
                }
            }
            
            Picker("Favorite Locations", selection: $selectedLocationIndex) {
                ForEach(favoritesViewModel.favoriteLocations.indices, id: \.self) { index in
                    Text(favoritesViewModel.favoriteLocations[index])
                        .tag(index)
                }
            }
            .onChange(of: selectedLocationIndex) {
                favouritePressed = true
                isFocused = false
                        }
            .pickerStyle(.palette)

            Spacer()
        }
        
        .navigationTitle("Favorite Locations")
    }
    
    
    func saveLocation(locationIndex: Int) {
        print("Saving...")
        if !newLocation.isEmpty {
            favoritesViewModel.addFavoriteLocation(newLocation, locationIndex: locationIndex)
            newLocation = ""
        }
    }
}

这个只存储收藏夹:

class FavoritesViewModel: ObservableObject {
    // Key for UserDefaults
    let favoritesKey = "favoriteLocations"

    // UserDefaults instance
    var defaults: UserDefaults {
        UserDefaults.standard
    }

    // Published property to manage favorite locations
    @Published var favoriteLocations: [String] = []

    // Initialize with default values
    init() {
        self.favoriteLocations = loadFavorites()
    }

    // Load favorite locations from UserDefaults
    func loadFavorites() -> [String] {
        return defaults.stringArray(forKey: favoritesKey) ?? []
    }

    // Save favorite locations to UserDefaults
    func saveFavorites() {
        defaults.set(favoriteLocations, forKey: favoritesKey)
    }

    // Add a new favorite location
    func addFavoriteLocation(_ location: String, locationIndex: Int) {
        // Check if location is already in favorites and limit to 4 locations
        if !favoriteLocations.contains(location) {
            if favoriteLocations.count < 4 {
                favoriteLocations.insert(location, at: locationIndex)
            } else {
                favoriteLocations.remove(at: locationIndex)
                favoriteLocations.insert(location, at: locationIndex)
            }
            saveFavorites() // Save after adding or updating favorites
        }
    }
}

还有 Observed 类:

class AppState: ObservableObject {
    
    // THIS IS THE VARIABLE IM TRYING TO SAVE TEXT TO
    @Published var destination: String = ""
    
}
ios swift swiftui observable textfield
1个回答
0
投票

您仍应使用

$app.destination
作为文本字段的绑定。请记住,单一事实来源

当用户在选择器中选择某些内容时,只需将

app.destination
设置为所选位置即可。

您只需要这些状态:

@StateObject private var favoritesViewModel = FavoritesViewModel()
@StateObject private var app = AppState()
@State private var selectedLocationIndex: Int?
@FocusState private var isFocused: Bool

我认为

selectedLocationIndex
是可选的更有意义,代表不选择任何东西的状态。

其余代码很简单。请注意如何在

app.destination
中设置
.onChange(of: selectedLocationIndex)

var body: some View {
    VStack {
        HStack {
            TextField("Destination", text: $app.destination)
                .focused($isFocused)
                .contentShape(.rect)
                .textInputAutocapitalization(.characters)
                .disableAutocorrection(true)
                .padding()
                .frame(width: 280, height: 60)
                .overlay(
                    RoundedRectangle(cornerRadius: 10)
                        .stroke(Color.gray, lineWidth: 2)
                )
            
            Button("Save") {
                saveLocation(locationIndex: selectedLocationIndex ?? 0)
            }
        }
        
        Picker("Favorite Locations", selection: $selectedLocationIndex) {
            ForEach(favoritesViewModel.favoriteLocations.indices, id: \.self) { index in
                Text(favoritesViewModel.favoriteLocations[index]).tag(Optional.some(index))
            }
        }
        .onChange(of: selectedLocationIndex) { _, newValue in
            if let newValue {
                isFocused = false
                app.destination = favoritesViewModel.favoriteLocations[newValue]
            }
        }
        .pickerStyle(.palette)

        Spacer()
    }
}


func saveLocation(locationIndex: Int) {
    print("Saving...")
    favoritesViewModel.addFavoriteLocation(app.destination, locationIndex: locationIndex)
    app.destination = ""
}
© www.soinside.com 2019 - 2024. All rights reserved.