Swift视图无法刷新:点击按钮重建视图未生效问题求助
解决方案
问题根源
- 用
@State存储视图实例无效:SwiftUI视图是值类型,重新赋值@State中的视图实例不会触发视图树更新,SwiftUI依赖状态变量变化而非实例引用对比来刷新视图。 - 子视图各自持有独立数据源:
RingView和RingNumbersView分别创建main实例,导致数据不共享,refresh操作无法同步通知所有子视图。 RingView未实现更新逻辑:updateWKInterfaceObject方法为空,即使数据源变化,底层WKInterfaceActivityRing也不会更新。RingNumbersView仅加载一次数据:仅在onAppear时获取数据,后续数据源变化无法触发UI更新。
1. 统一数据管理,提升状态到父视图
将数据源实例提升到ContentView,作为共享数据源传递给子视图,避免子视图各自创建独立实例:
struct ContentView: View { @State var WeekElevation : FlightsClimbed = FlightsClimbed() // 共享的数据源实例 @StateObject private var fitness = main() var body: some View { VStack { Button("refresh") { // 触发重新查询数据,而非重建视图 fitness.makeQuery() { summary in fitness.currentSummary = summary } } RingView(fitness: fitness).frame(width: 45, height: 45) RingNumbersView(fitness: fitness) VStack { Text("\(Int(WeekElevation.getFlights())) ") + Text(Image(systemName: "figure.stairs")) + Text(" in past week") Text("\(WeekElevation.calculatePercentEverest())% of Mt. Everest") } } } }
2. 改造数据源类,支持状态发布
确保main类为ObservableObject,并添加可发布的HKActivitySummary属性,让数据变化能通知所有关联视图:
import Foundation import HealthKit class main: ObservableObject { @Published var currentSummary: HKActivitySummary? func authorizeHealthkit() { // 保留原有的健康权限授权逻辑 } func makeQuery(completion: @escaping (HKActivitySummary) -> Void) { // 保留原有的HKActivitySummary查询逻辑 // 查询完成后更新可发布属性 let fetchedSummary = HKActivitySummary() // 替换为实际查询结果 currentSummary = fetchedSummary completion(fetchedSummary) } }
3. 修改RingView,实现数据更新逻辑
通过@ObservedObject接收共享数据源,在updateWKInterfaceObject中更新环形图:
import Foundation import HealthKit import SwiftUI struct RingView : WKInterfaceObjectRepresentable { @ObservedObject var fitness: main func makeWKInterfaceObject(context: Context) -> WKInterfaceActivityRing { let ringObject = WKInterfaceActivityRing() // 初始化时加载已有数据 if let summary = fitness.currentSummary { ringObject.setActivitySummary(summary, animated: true) } return ringObject } func updateWKInterfaceObject(_ wkInterfaceObject: WKInterfaceActivityRing, context: Context) { // 数据变化时更新环形图 if let summary = fitness.currentSummary { wkInterfaceObject.setActivitySummary(summary, animated: true) } } }
4. 修改RingNumbersView,响应数据变化
去掉子视图独立的数据源和状态,直接使用共享数据源的currentSummary,自动响应数据更新:
struct RingNumbersView: View { @ObservedObject var fitness: main var body: some View { HStack { if let summary = fitness.currentSummary { let red = Int(summary.activeEnergyBurned.doubleValue(for: .largeCalorie())) let green = Int(summary.appleExerciseTime.doubleValue(for: .minute())) let blue = Int(summary.appleStandHours.doubleValue(for: .count())) Text("\(red)").foregroundColor(.red) Text("\(green)").foregroundColor(.green) Text("\(blue)").foregroundColor(.blue) } else { // 数据未加载时的占位显示 Text("--").foregroundColor(.gray) Text("--").foregroundColor(.gray) Text("--").foregroundColor(.gray) } } .padding() .onAppear(){ fitness.authorizeHealthkit() // 初始化时无数据则触发查询 if fitness.currentSummary == nil { fitness.makeQuery() { _ in } } } } }
核心逻辑说明
- 状态提升:将数据源集中在父视图,确保所有子视图共享同一数据,避免数据不一致。
- 可发布属性绑定:通过
@Published和@ObservedObject实现数据变化自动通知UI刷新,无需手动重建视图。 - WatchKit组件更新:必须实现
WKInterfaceObjectRepresentable的updateWKInterfaceObject方法,才能让底层WatchKit组件响应数据变化。 - Refresh触发:点击按钮仅触发数据重新查询,通过更新共享数据源的可发布属性,自动同步所有关联子视图的UI。
内容的提问来源于stack exchange,提问作者Eddie Gabriel
相关产品推荐
相关产品推荐

