Swiftui 未触发从modalView中的更改重新绘制ContentView

Swiftui 未触发从modalView中的更改重新绘制ContentView,swiftui,modal-view,Swiftui,Modal View,自从Swiftui2.0问世以来,我一直无法根据另一个模式显示视图(设置视图)中所做的更改来更新视图 我在主ContentView上显示一个字符串,该字符串从SettingsView上的分段选择器值派生其内容。 问题是,在用户更改设置并放弃设置视图后,ContentView中的字符串不会更新。主体未重新绘制 我正在使用@observeObject和@StateObject,所以对它的每一次更改都应该触发一次重画,但我无法让它工作 我创建了一个符合ObserveObject协议的类:AppStat

自从Swiftui2.0问世以来,我一直无法根据另一个模式显示视图(设置视图)中所做的更改来更新视图

我在主ContentView上显示一个字符串,该字符串从SettingsView上的分段选择器值派生其内容。 问题是,在用户更改设置并放弃设置视图后,ContentView中的字符串不会更新。主体未重新绘制

我正在使用@observeObject和@StateObject,所以对它的每一次更改都应该触发一次重画,但我无法让它工作

我创建了一个符合ObserveObject协议的类:AppState 我正在使用该类尝试传递数据,更重要的是,在视图之间传递数据更改,以便根据用户的设置重新绘制ContentView。 为了实例化这个类,我在AppDelegate文件中注册了一个UserDefaults

我还将合并框架导入到我的项目中,并在每个文件中添加了导入合并行

为了说明这个问题,我已经尽可能地简化了我的代码,因此下面的内容可能有点迂回,但它是从一个更复杂的应用程序中派生出来的,对此我深表歉意

以下是我的ContentView代码:

import SwiftUI
import Combine


struct ContentView: View {
   
   @StateObject var appState: AppState
   
   @State var modalViewCaller = 0 // used to present correct modalView
   @State var modalIsPresented = false // to present the modal views
   
   var body: some View {
       
       let stringArray = generateString() // func to generate string according to user's pref
       let recapString = stringArray[0]
       
       return ZStack {
               NavigationView {
                   VStack {
 // MARK: -  texts :
                   VStack {
                   Text(recapString)
                       .bold()
                       .multilineTextAlignment(/*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)

                   } // end of VStack
                       .padding()
                       .overlay(RoundedRectangle(cornerRadius: 10)
                       .stroke(Color(UIColor.systemBlue), lineWidth: 4))
                       .padding()
                           } // END of VStack
           .onAppear() {
               self.modalViewCaller = 0
               print("\n\n*********** Content View onAppear triggered ! ************\n")
           }
           .navigationBarTitle("DataFun", displayMode: .inline)
           .navigationBarItems(leading: (
               Button(action: {
                   self.modalViewCaller = 1 // SettingsView
                   self.modalIsPresented = true
               }
                   ) {
                       Image(systemName: "gear")
                           .imageScale(.large)
                    }
           ))
       } // END of NavigationView
               .onAppear() {
                   self.appState.updateValues()
               }
       } // End of ZStack
       .sheet(isPresented: $modalIsPresented) {
           sheetContent(modalViewCaller: $modalViewCaller, appState: AppState())
               }
       .navigationViewStyle(StackNavigationViewStyle())
   }
   
   // MARK: - struct sheetContent() :
     
     struct sheetContent: View {
         @Binding var modalViewCaller: Int // Binding to the @State modalViewCaller variable from ContentView
         @StateObject var appState: AppState
         
         var body: some View {
           if modalViewCaller == 1 { // The settings view is called
               SettingsView(appState: AppState())
                     .navigationViewStyle(StackNavigationViewStyle())
                     .onDisappear { self.modalViewCaller = 0 }
             } else if modalViewCaller == 2 { // the "other view" is called
                     OtherView()
                     .navigationViewStyle(StackNavigationViewStyle())
                     .onDisappear { self.modalViewCaller = 0 }
             }
         }
     } // END of func sheetContent
   
   // MARK: - generateString()
 func generateString() -> [String] {
       
       var recapString = "" // The recap string
       var myArray = [""]
   
       // We create the recap string :
   if UserDefaults.standard.integer(forKey: "rules selection") == 0 { // ICAO
             recapString = "User chose LEFT"
   } else if UserDefaults.standard.integer(forKey: "rules selection") == 1 { // AF Rules
           recapString = "User chose RIGHT"
       }
   
   myArray = [recapString]
   
           return myArray
   } // End of func generateString()
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView(appState: AppState())
   }
}
import SwiftUI
import Combine


struct SettingsView: View {
   
   @Environment(\.presentationMode) var presentationMode // in order to dismiss the Sheet

   @StateObject var appState: AppState
   @State private var rulesSelection = UserDefaults.standard.integer(forKey: "rules selection") // 0 is LEFT, 1 is RIGHT

   var body: some View {
       NavigationView {
           VStack {
               Spacer()
       Text("Choose a setting below")
           .padding()
       Picker("", selection: $rulesSelection) {
                   Text("LEFT").tag(0)
                   Text("RIGHT").tag(1)
               }
       .pickerStyle(SegmentedPickerStyle())
       .padding()
               Spacer()
           }
           .navigationBarItems(
               leading:
               Button("Done") {
                       self.saveDefaults() // We set the UserDefaults
                       self.presentationMode.wrappedValue.dismiss() // This dismisses the view
                //   self.modalViewCaller = 0
               }
           ) // END of NavBarItems
       } // END of NavigationBiew
   } // END of body
   
   func saveDefaults() {
       
       UserDefaults.standard.set(rulesSelection, forKey: "rules selection")
       
       self.appState.updateValues() // This is a func from the AppState class that will align the appState's value to the UserDefaults
       
   }
}

struct SettingsView_Previews: PreviewProvider {
   static var previews: some View {
       SettingsView(appState: AppState())
   }
}
以下是我的AppState代码:

import Foundation
import SwiftUI
import Combine

class AppState: ObservableObject {

   @Published var rulesSelection: Int = UserDefaults.standard.integer(forKey: "rules selection")
   
   func updateValues() { // When the user changes a setting, the UserDefault is updated. Here, we align the AppState's value with what is now in the UserDefaults
       self.rulesSelection = UserDefaults.standard.integer(forKey: "rules selection")
       print("\nappState value (ruleSelection) updated from Appstate class func \"updateValues")
   }
}
这是我的设置视图代码:

import SwiftUI
import Combine


struct ContentView: View {
   
   @StateObject var appState: AppState
   
   @State var modalViewCaller = 0 // used to present correct modalView
   @State var modalIsPresented = false // to present the modal views
   
   var body: some View {
       
       let stringArray = generateString() // func to generate string according to user's pref
       let recapString = stringArray[0]
       
       return ZStack {
               NavigationView {
                   VStack {
 // MARK: -  texts :
                   VStack {
                   Text(recapString)
                       .bold()
                       .multilineTextAlignment(/*@START_MENU_TOKEN@*/.center/*@END_MENU_TOKEN@*/)

                   } // end of VStack
                       .padding()
                       .overlay(RoundedRectangle(cornerRadius: 10)
                       .stroke(Color(UIColor.systemBlue), lineWidth: 4))
                       .padding()
                           } // END of VStack
           .onAppear() {
               self.modalViewCaller = 0
               print("\n\n*********** Content View onAppear triggered ! ************\n")
           }
           .navigationBarTitle("DataFun", displayMode: .inline)
           .navigationBarItems(leading: (
               Button(action: {
                   self.modalViewCaller = 1 // SettingsView
                   self.modalIsPresented = true
               }
                   ) {
                       Image(systemName: "gear")
                           .imageScale(.large)
                    }
           ))
       } // END of NavigationView
               .onAppear() {
                   self.appState.updateValues()
               }
       } // End of ZStack
       .sheet(isPresented: $modalIsPresented) {
           sheetContent(modalViewCaller: $modalViewCaller, appState: AppState())
               }
       .navigationViewStyle(StackNavigationViewStyle())
   }
   
   // MARK: - struct sheetContent() :
     
     struct sheetContent: View {
         @Binding var modalViewCaller: Int // Binding to the @State modalViewCaller variable from ContentView
         @StateObject var appState: AppState
         
         var body: some View {
           if modalViewCaller == 1 { // The settings view is called
               SettingsView(appState: AppState())
                     .navigationViewStyle(StackNavigationViewStyle())
                     .onDisappear { self.modalViewCaller = 0 }
             } else if modalViewCaller == 2 { // the "other view" is called
                     OtherView()
                     .navigationViewStyle(StackNavigationViewStyle())
                     .onDisappear { self.modalViewCaller = 0 }
             }
         }
     } // END of func sheetContent
   
   // MARK: - generateString()
 func generateString() -> [String] {
       
       var recapString = "" // The recap string
       var myArray = [""]
   
       // We create the recap string :
   if UserDefaults.standard.integer(forKey: "rules selection") == 0 { // ICAO
             recapString = "User chose LEFT"
   } else if UserDefaults.standard.integer(forKey: "rules selection") == 1 { // AF Rules
           recapString = "User chose RIGHT"
       }
   
   myArray = [recapString]
   
           return myArray
   } // End of func generateString()
}

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
       ContentView(appState: AppState())
   }
}
import SwiftUI
import Combine


struct SettingsView: View {
   
   @Environment(\.presentationMode) var presentationMode // in order to dismiss the Sheet

   @StateObject var appState: AppState
   @State private var rulesSelection = UserDefaults.standard.integer(forKey: "rules selection") // 0 is LEFT, 1 is RIGHT

   var body: some View {
       NavigationView {
           VStack {
               Spacer()
       Text("Choose a setting below")
           .padding()
       Picker("", selection: $rulesSelection) {
                   Text("LEFT").tag(0)
                   Text("RIGHT").tag(1)
               }
       .pickerStyle(SegmentedPickerStyle())
       .padding()
               Spacer()
           }
           .navigationBarItems(
               leading:
               Button("Done") {
                       self.saveDefaults() // We set the UserDefaults
                       self.presentationMode.wrappedValue.dismiss() // This dismisses the view
                //   self.modalViewCaller = 0
               }
           ) // END of NavBarItems
       } // END of NavigationBiew
   } // END of body
   
   func saveDefaults() {
       
       UserDefaults.standard.set(rulesSelection, forKey: "rules selection")
       
       self.appState.updateValues() // This is a func from the AppState class that will align the appState's value to the UserDefaults
       
   }
}

struct SettingsView_Previews: PreviewProvider {
   static var previews: some View {
       SettingsView(appState: AppState())
   }
}
如果有人有时间查看此“活动”,则为正在工作的项目:

谢谢你的指点


问候。

好吧。。。那是。。。简而言之,这里有许多更改,所以这里是完整的ContentView.swift和修复

注意:您只需要一个StateObject,并在其中设置一个实例,并且您需要在视图中具有observable对象的已发布属性,否则它不会被刷新,并且在使用AppStorage等之前,UserDefaults中的更改不会刷新视图

使用Xcode 12.1/iOS 14.1进行验证

import SwiftUI
import Combine


struct ContentView: View {
    
    @StateObject var appState: AppState
    
    @State var modalViewCaller = 0 // used to present correct modalView
    @State var modalIsPresented = false // to present the modal views
    
    var body: some View {
        
        return ZStack {
                NavigationView {
                    VStack {
  // MARK: -  texts :
                    VStack {
                            RecapStringView(appState: appState)

                    } // end of VStack
                        .padding()
                        .overlay(RoundedRectangle(cornerRadius: 10)
                        .stroke(Color(UIColor.systemBlue), lineWidth: 4))
                        .padding()
                            } // END of VStack
            .onAppear() {
                self.modalViewCaller = 0
                print("\n\n*********** Content View onAppear triggered ! ************\n")
            }
            .navigationBarTitle("DataFun", displayMode: .inline)
            .navigationBarItems(leading: (
                Button(action: {
                    self.modalViewCaller = 1 // SettingsView
                    self.modalIsPresented = true
                }
                    ) {
                        Image(systemName: "gear")
                            .imageScale(.large)
                     }
            ))
        } // END of NavigationView
                .onAppear() {
                    self.appState.updateValues()
                }
        } // End of ZStack
        .sheet(isPresented: $modalIsPresented) {
            sheetContent(modalViewCaller: $modalViewCaller, appState: appState)
                }
        .navigationViewStyle(StackNavigationViewStyle())
    }
    
    // MARK: - struct sheetContent() :
      
      struct sheetContent: View {
          @Binding var modalViewCaller: Int // Binding to the @State modalViewCaller variable from ContentView
          @ObservedObject var appState: AppState
          
          var body: some View {
            if modalViewCaller == 1 { // The settings view is called
                SettingsView(appState: appState)
                      .navigationViewStyle(StackNavigationViewStyle())
                      .onDisappear { self.modalViewCaller = 0 }
              } else if modalViewCaller == 2 { // the "other view" is called
                      OtherView()
                      .navigationViewStyle(StackNavigationViewStyle())
                      .onDisappear { self.modalViewCaller = 0 }
              }
          }
      } // END of func sheetContent
}

struct RecapStringView: View {
    @ObservedObject var appState: AppState
    var body: some View {
        Text("User chose " + "\(appState.rulesSelection == 0 ? "LEFT" : "RIGHT")")
        .bold()
        .multilineTextAlignment(.center)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView(appState: AppState())
    }
}