如何在SwiftUI应用中持久化保存用户输入内容?
SwiftUI实现用户输入内容持久化(重启/返回后保留)
你的当前代码里,@State变量仅存在于内存中,应用重启或视图销毁后数据就会丢失,必须添加磁盘持久化逻辑才能让内容留存。下面给你两种实用的实现方案:
方案一:用@AppStorage直接处理数组持久化
@AppStorage是SwiftUI自带的轻量持久化工具,支持存储Data类型,我们可以通过JSON编解码把字符串数组转成Data存储:
修改原代码中的landings变量部分,替换成以下代码:
// 用Data类型存储数组的JSON编码结果 @AppStorage("landings") private var landingsData: Data = Data() // 计算属性,负责编解码数组 private var landings: [String] { get { (try? JSONDecoder().decode([String].self, from: landingsData)) ?? [] } set { landingsData = (try? JSONEncoder().encode(newValue)) ?? Data() } }
然后调整添加和更新逻辑的代码:
- 添加按钮的逻辑:
Button("Add") { if !newLandingName.isEmpty { // 因为landings是计算属性,需要重新赋值触发存储 landings = landings + [newLandingName] newLandingName = "" } isAddingLanding = false }
- 更新按钮的逻辑(顺便修复原代码里的冗余写法):
Button("Update") { if !newLandingName.isEmpty { if let idx = landings.firstIndex(where: { $0 == oldLandingName }) { var updatedLandings = landings updatedLandings[idx] = newLandingName landings = updatedLandings } } isEditing = false }
方案二:用ViewModel管理持久化(更适合扩展)
如果以后需要添加更多复杂逻辑,建议把数据管理抽成ViewModel,代码结构更清晰:
首先创建ViewModel类:
class LandingViewModel: ObservableObject { @Published var landings: [String] = [] private let defaultsKey = "saved_landings" init() { // 初始化时读取本地存储的数据 loadSavedLandings() } // 从UserDefaults读取数据 private func loadSavedLandings() { guard let data = UserDefaults.standard.data(forKey: defaultsKey), let savedLandings = try? JSONDecoder().decode([String].self, from: data) else { return } landings = savedLandings } // 保存数据到UserDefaults private func saveLandings() { guard let data = try? JSONEncoder().encode(landings) else { return } UserDefaults.standard.set(data, forKey: defaultsKey) } // 添加新条目 func addNewLanding(_ name: String) { landings.append(name) saveLandings() } // 更新条目 func updateLanding(oldName: String, newName: String) { guard let index = landings.firstIndex(of: oldName) else { return } landings[index] = newName saveLandings() } }
然后在View中使用ViewModel:
struct LandingView: View { @State private var oldLandingName: String = "" @State private var newLandingName: String = "" @State private var isAddingLanding = false @State private var isEditing = false // 使用@StateObject持有ViewModel @StateObject private var viewModel = LandingViewModel() var body: some View { NavigationView { // ... 其他界面代码不变,只修改列表和按钮逻辑 ... List(viewModel.landings, id: \.self) { landing in Text(landing).bold() .foregroundColor(.white) .listRowBackground(Color.clear) .listRowSeparatorTint(.blue) .onTapGesture { self.isEditing = true self.oldLandingName = landing self.newLandingName = landing } } // 添加界面的按钮 Button("Add") { if !newLandingName.isEmpty { viewModel.addNewLanding(newLandingName) newLandingName = "" } isAddingLanding = false } // 更新界面的按钮 Button("Update") { if !newLandingName.isEmpty { viewModel.updateLanding(oldName: oldLandingName, newName: newLandingName) } isEditing = false } } } }
注意点
- 这两种方案都用了
UserDefaults,适合存储少量数据(比如这种字符串数组);如果以后需要存储大量复杂数据,建议改用Core Data或Realm。 - 原代码中更新按钮里的
$oldLandingName.wrappedValue是冗余写法,直接用oldLandingName即可。
内容的提问来源于stack exchange,提问作者allanrulezdood
相关产品推荐
相关产品推荐

