自从swiftUI 2.0出现以来,我一直无法根据在另一个模式显示视图(设置视图(中所做的更改来更新视图。
我在主ContentView上显示一个字符串,该字符串从SettingsView上的分段选取器值派生其内容。问题是,在用户更改设置并放弃SettingsView后,ContentView中的字符串不会更新。正文未重新绘制。
我正在使用@ObservableObject和@StateObject,所以对它的每一次更改都应该触发重绘,但我无法使它工作。。。
我创建了一个符合ObservableObject协议的类:AppState我正在使用该类尝试传递数据,更重要的是,数据在视图之间更改,以便根据用户的设置重新绘制我的ContentView。为了实例化这个类,我在AppDelegate文件中注册了一个UserDefaults。
我还将Combine Framework导入到我的项目中,并在每个文件中添加了导入Combine行!
为了说明这个问题,我已经尽可能简化了我的代码,所以下面的内容可能看起来有点绕口,但它是从一个更复杂的应用程序中派生出来的,很抱歉。
这是我的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("nn*********** 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())
}
}
这是我的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 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())
}
}
如果有人有时间检查这个"工作项目";live":
https://github.com/Esowes/dataFun
谢谢你的指点。
谨致问候。
嗯。。。它是。。。简而言之,许多更改,所以这里是完整的ContentView.swift与修复。
注意:您只需要一个StateObject,并在其中设置一个实例,并且您需要在视图中发布可观察对象的属性,否则它不会刷新,并且在使用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("nn*********** 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())
}
}