SwiftUI硬币翻转应用界面更新异常,求正确实现代码
问题解决:SwiftUI硬币翻转应用UI更新滞后修复
合适的提问标题推荐
- SwiftUI + Core Data 数据更新后UI统计值滞后一步如何解决?
- 基于Core Data的SwiftUI应用中,如何让UI实时同步数据统计结果?
- SwiftUI中FetchRequest数据变更后,@State变量无法实时更新的问题处理
修复后的完整代码
import SwiftUI struct CoinFlip: View { @State private var saveFailed: Bool = false @State private var flipResult: Bool = false @State private var justFlipped: Bool = false @Environment(\.managedObjectContext) private var coinFlipContext @FetchRequest( sortDescriptors: [NSSortDescriptor(keyPath: \FlippedCoin.timeStamp, ascending: true)], animation: .default) private var flippedCoins: FetchedResults<FlippedCoin> // 从Core Data直接计算统计值,自动同步UI private var flipCount: Int { flippedCoins.count } private var headsCount: Int { flippedCoins.filter { $0.wasHeads }.count } private var percentHeads: Float { guard flipCount > 0 else { return 0 } return (Float(headsCount) / Float(flipCount)) * 100 } var body: some View { VStack { Spacer() Button(action: addItem) { Label("Flip", systemImage: "plus") } Button(action: eraseHistory) { Label("Erase History", systemImage: "minus") } Spacer() if justFlipped { Text(flipResult ? "You flipped heads!" : "You flipped tails!") .onAppear { // 3秒后自动隐藏翻转结果提示 DispatchQueue.main.asyncAfter(deadline: .now() + 3) { justFlipped = false } } } Spacer() switch flipCount { case 0: Text("No coin has been flipped.") case 1: Text("You have flipped a coin only one time.") Text("") Text(headsCount > 0 ? "It was heads." : "It was tails.") default: Text("Coins have been flipped \(flipCount) times.") Text("") Text("\(Int(percentHeads)) % heads.") } if saveFailed { VStack { Text("HA HA!") Text("Something went wrong.") Text("") Text("Your flips were not saved! Too bad.") } .onAppear { // 3秒后自动隐藏错误提示 DispatchQueue.main.asyncAfter(deadline: .now() + 3) { saveFailed = false } } } } } private func addItem() { let rndMin = 0 let rndMax = 1000 let randRoll = Int.random(in: rndMin...rndMax) flipResult = randRoll >= 500 let newItem = FlippedCoin(context: coinFlipContext) newItem.timeStamp = Date() newItem.wasHeads = flipResult newItem.rndMinimun = Int16(rndMin) newItem.rndMaximum = Int16(rndMax) newItem.rndValue = Int16(randRoll) do { try coinFlipContext.save() justFlipped = true } catch { saveFailed = true } } private func eraseHistory() { flippedCoins.forEach(coinFlipContext.delete) justFlipped = false do { try coinFlipContext.save() } catch { saveFailed = true } } } private let itemFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateStyle = .short formatter.timeStyle = .medium return formatter }() struct CoinFlip_Previews: PreviewProvider { static var previews: some View { CoinFlip() } }
问题原因与修复说明
- 核心问题:原代码在保存Core Data上下文之前调用
test1()统计数据,此时flippedCoins还未包含新添加的记录,导致统计值滞后一步;同时用@State变量存储统计值,无法自动响应Core Data的数据变化。 - 修复逻辑:
- 用计算属性替代
@State变量,直接从FetchedResults中计算统计值,Core Data数据更新时计算属性会自动重新计算,UI实时同步。 - 调整
addItem()顺序:先保存上下文,再设置justFlipped,确保新数据已经写入后再触发UI更新。 - 移除了冗余的
test1()方法和隐藏的占位视图,符合SwiftUI响应式设计思想。
- 用计算属性替代
- 额外优化:给翻转结果和错误提示添加自动消失逻辑,提升用户体验。
内容的提问来源于stack exchange,提问作者Fejj
相关产品推荐
相关产品推荐

