SwiftUI:如何在父视图中初始化新的 StateObject?

Posted

技术标签:

【中文标题】SwiftUI:如何在父视图中初始化新的 StateObject?【英文标题】:SwiftUI: How to initialize a new StateObject in a parent view? 【发布时间】:2021-11-07 23:56:14 【问题描述】:

我有一个类似于以下(简化)代码的应用架构。我使用了WorkoutManagerStateObject,我在设置视图中对其进行了初始化,然后通过EnvironmentObject 传递给它的孩子。问题是,在关闭.sheet 时,没有任何生命周期事件可以初始化一个新的WorkoutManager,我需要它才能连续开始新的锻炼。在下面的这个例子中,我怎样才能让WorkoutView 重新初始化 WorkoutManager 使其成为一个干净的对象?

import SwiftUI
import HealthKit

class WorkoutManager: ObservableObject 
    
    var workout: HKWorkout?


struct ContentView: View 
    
    @StateObject var workoutManager = WorkoutManager()
    
    @State var showingWorkoutView = false
    
    var body: some View 
      
        Button 
            showingWorkoutView.toggle()
         label: 
            Text("Start Workout")
        
        .sheet(isPresented: $showingWorkoutView) 
            WorkoutView(showingWorkoutView: $showingWorkoutView)
        
        
    


struct WorkoutView: View 
    
    @EnvironmentObject var workoutManager:  WorkoutManager
    @Binding var showingWorkoutView: Bool
    
    var body: some View 
        Text("Workout Started")
            .padding()
        Button 
            showingWorkoutView.toggle()
            //Here I want to initialize a new WorkoutManager to clear out the previous workout's state, how? 
         label: 
            Text("End Workout")
        

    

【问题讨论】:

愚蠢的问题,但你不能在 WorkoutManager 中创建一个方法来重置它的状态而不创建新实例吗? @Pastre 一点也不笨,但是是的,我现在的解决方案是我在锻炼管理器中有一个 resetWorkoutManager 函数,它将所有变量设置回它们的初始化状态。只是感觉有点 hacky,我想知道是否有更 SwiftUI 的方式来做到这一点。 @GarySabo 重置状态函数对我来说似乎一点也不 hacky @GarySabo “将其所有变量设置回其初始化状态”——与其设置所有变量,不如将状态封装在一个可以在 @987654329 上重置的 struct 中@? 【参考方案1】:

正如 cmets 中已经提到的,您可能想要采取的方法是在同一 WorkoutManager 内重置状态。无论如何,您将无法将 new 对象分配给 @StateObject - 由于 View 的不可变 self,您最终会遇到编译器错误。

其次,我建议您可能不想依赖 WorkoutView 中的 Button 来执行此操作。例如,如果用户通过滑动关闭sheet,则不会被调用。相反,您可以在onChange 中侦听sheet 的状态(另一种方法是使用sheetonDismiss 参数):

class WorkoutManager: ObservableObject 
    var workout: HKWorkout?
    
    func resetState() 
        //do whatever you need to do to reset the state
        print("Reset state")
    


struct ContentView: View 
    
    @StateObject var workoutManager = WorkoutManager()
    
    @State var showingWorkoutView = false
    
    var body: some View 
      
        Button 
            showingWorkoutView.toggle()
         label: 
            Text("Start Workout")
        
        .sheet(isPresented: $showingWorkoutView) 
            WorkoutView(showingWorkoutView: $showingWorkoutView)
        
        .onChange(of: showingWorkoutView)  newValue in
            if !newValue 
                workoutManager.resetState()
            
        
    


struct WorkoutView: View 
    
    @EnvironmentObject var workoutManager:  WorkoutManager
    @Binding var showingWorkoutView: Bool
    
    var body: some View 
        Text("Workout Started")
            .padding()
        Button 
            showingWorkoutView.toggle()
         label: 
            Text("End Workout")
        

    

【讨论】:

谢谢,我应该澄清这是一个 WatchOS 应用程序,所以除了按钮之外没有其他方法可以关闭工作表。

以上是关于SwiftUI:如何在父视图中初始化新的 StateObject?的主要内容,如果未能解决你的问题,请参考以下文章

如何从结构视图数组中提取状态? SwiftUI

SwiftUI 在视图之间切换

SwiftUI - 更新父视图的状态时如何保留子视图的状态?

在 SwiftUI MVVM 中的子视图和父视图之间共享值

如何在 SwiftUI 中再次初始化 View?

如何在 SwiftUI 中为 WKWebView 创建浏览器选项卡