'Modifying state during view update, this will cause undefined behavior.' 在文本字段上键入时出错 (SwiftUI)
'Modifying state during view update, this will cause undefined behavior.' error when typing on a textfield (SwiftUI)
我有两个文本字段,分配给:
@State private var emailAddress: String = ""
@State private var password: String = ""
现在,每当我在上面打字时,应用程序似乎卡住了并给我这个错误:
'Modifying state during view update, this will cause undefined behavior.'
我有一个 StartView()
:
class UserSettings: ObservableObject {
var didChange = PassthroughSubject<Void, Never>()
@Published var loggedIn : Bool = false {
didSet {
didChange.send(())
}
}
}
struct StartView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
if settings.loggedIn {
return AnyView(TabbarView())
}else {
return AnyView(ContentView())
}
}
}
我创建了一个 ObservableObject class 的 UserSettings,它具有 loggedIn
布尔值。当用户点击 LogInView()
中的 'Log In' 按钮时,此布尔值变为 true
并出现一个新视图 (TabbarView()
)
这是 LogInView():
struct LogInView: View {
@EnvironmentObject var settings: UserSettings
@State private var emailAddress: String = ""
@State private var password: String = ""
var body: some View {
GeometryReader { geometry in
VStack (alignment: .center){
HStack {
Image("2")
.resizable()
.frame(width: 20, height: 20)
Text("Social App")
.font(.system(size: 12))
}.padding(.top, 30)
.padding(.bottom, 10)
Text("Log In to Your Account")
.font(.title)
.font(.system(size: 14, weight: .bold, design: Font.Design.default))
.padding(.bottom, 50)
TextField("Email", text: self.$emailAddress)
.frame(width: geometry.size.width - 45, height: 50)
.textContentType(.emailAddress)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.accentColor(.red)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.cornerRadius(5)
TextField("Password", text: self.$password)
.frame(width: geometry.size.width - 45, height: 50)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.foregroundColor(.gray)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.textContentType(.password)
.cornerRadius(5)
Button(action: {
self.settings.loggedIn = true
}) {
HStack {
Text("Log In")
}
.padding()
.frame(width: geometry.size.width - 40, height: 40)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
}
.padding(.bottom, 40)
Divider()
Button(action: {
print("Take to forget password VC")
}) {
Text("Forgot your password?")
}
Spacer()
}
.padding(.bottom, 90)
}
}
}
我知道如果我在修改状态时更新视图(在文本字段中键入时),则会出现此错误。但我不会在登录屏幕的任何地方更新视图。那么为什么会出现这个错误。帮助将不胜感激!
这可能与您的问题无关,但在 Xcode 11 Beta 4 中,Apple 将 "didset" 更改为 "willset",将 "didChange" 更改为 "willChange"
在 Xcode 11 Beta 5 中,苹果将 "willChange" 更改为 "objectWillChange"。
因此 StartView() 应该是:
class UserSettings: ObservableObject {
var objectWillChange = PassthroughSubject<Void, Never>()
@Published var loggedIn : Bool = false {
willSet {
objectWillChange.send(())
}
}
}
这对我有用,您甚至不需要导入 Combine!当你使用 @Published
时,SwiftUI 将自动合成 objectWillChange
主题,并在 属性 发生变化时调用发送。如果需要,您仍然可以手动调用 .send()
,但在大多数情况下您不会。
class UserSettings: ObservableObject {
@Published var loggedIn : Bool = false
}
Beta 5 发行说明摘录:
You can manually conform to ObservableObject by defining an
objectWillChange publisher that emits before the object changes.
However, by default, ObservableObject automatically synthesizes
objectWillChange and emits before any @Published properties change.
这是对我来说工作正常的完整代码(iPhone Xr 和真实设备,iPad 第六代):
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(UserSettings()))
import SwiftUI
struct ContentView: View {
var body: some View {
StartView()
}
}
class UserSettings: ObservableObject {
@Published var loggedIn : Bool = false
}
struct StartView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
if settings.loggedIn {
return AnyView(Text("LOGGED IN"))
} else {
return AnyView(LogInView())
}
}
}
struct LogInView: View {
@EnvironmentObject var settings: UserSettings
@State private var emailAddress: String = ""
@State private var password: String = ""
var body: some View {
GeometryReader { geometry in
VStack (alignment: .center){
HStack {
Image(systemName: "2.circle.fill")
.resizable()
.frame(width: 20, height: 20)
Text("Social App")
.font(.system(size: 12))
}.padding(.top, 30)
.padding(.bottom, 10)
Text("Log In to Your Account")
.font(.title)
.font(.system(size: 14, weight: .bold, design: Font.Design.default))
.padding(.bottom, 50)
TextField("Email", text: self.$emailAddress)
.frame(width: geometry.size.width - 45, height: 50)
.textContentType(.emailAddress)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.accentColor(.red)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.cornerRadius(5)
TextField("Password", text: self.$password)
.frame(width: geometry.size.width - 45, height: 50)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.foregroundColor(.gray)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.textContentType(.password)
.cornerRadius(5)
Button(action: {
self.settings.loggedIn = true
}) {
HStack {
Text("Log In")
}
.padding()
.frame(width: geometry.size.width - 40, height: 40)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
}
.padding(.bottom, 40)
Divider()
Button(action: {
print("Take to forget password VC")
}) {
Text("Forgot your password?")
}
Spacer()
}
.padding(.bottom, 90)
}
}
}
我想这是一个错误。您收到的这条消息也发生在这个简单的视图上,该视图通过用户输入过滤掉列表条目。只是在文本字段中快速键入会导致此问题。如果您在文本字段中输入第一个字符,UI 会卡住一段时间。
struct ContentView: View {
@State private var list: [String] = (0..<500).map { "Text \([=10=])" }
@State private var searchText: String = ""
var filteredList: [String] {
guard !searchText.isEmpty else { return list }
return list.filter({ [=10=].contains(self.searchText) })
}
var body: some View {
VStack {
TextField("Search", text: $searchText)
List(filteredList, id: \String.self) { t in Text(t) }
}
.padding()
}
}
解决方法是将@State 变量移动到模型中。所以这似乎是@State 的问题:
class Model: ObservableObject {
@Published var list: [String] = (0..<500).map { "Text \([=11=])" }
@Published var searchText: String = ""
var filteredList: [String] {
guard !searchText.isEmpty else { return list }
return list.filter({ [=11=].contains(self.searchText) })
}
}
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
VStack {
TextField("Search", text: $model.searchText)
List(model.filteredList, id: \String.self) { t in Text(t) }
}
.padding()
}
}
不要用 if 分支,使用 .opacity(_:)
@ViewBuilder
var body: some View {
// if settings.loggedIn {
TabbarView().opacity(settings.loggedIn ? 1 : 0)
// } else {
ContentView().opacity(settings.loggedIn ? 0 : 1)
// }
}
我有两个文本字段,分配给:
@State private var emailAddress: String = ""
@State private var password: String = ""
现在,每当我在上面打字时,应用程序似乎卡住了并给我这个错误:
'Modifying state during view update, this will cause undefined behavior.'
我有一个 StartView()
:
class UserSettings: ObservableObject {
var didChange = PassthroughSubject<Void, Never>()
@Published var loggedIn : Bool = false {
didSet {
didChange.send(())
}
}
}
struct StartView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
if settings.loggedIn {
return AnyView(TabbarView())
}else {
return AnyView(ContentView())
}
}
}
我创建了一个 ObservableObject class 的 UserSettings,它具有 loggedIn
布尔值。当用户点击 LogInView()
中的 'Log In' 按钮时,此布尔值变为 true
并出现一个新视图 (TabbarView()
)
这是 LogInView():
struct LogInView: View {
@EnvironmentObject var settings: UserSettings
@State private var emailAddress: String = ""
@State private var password: String = ""
var body: some View {
GeometryReader { geometry in
VStack (alignment: .center){
HStack {
Image("2")
.resizable()
.frame(width: 20, height: 20)
Text("Social App")
.font(.system(size: 12))
}.padding(.top, 30)
.padding(.bottom, 10)
Text("Log In to Your Account")
.font(.title)
.font(.system(size: 14, weight: .bold, design: Font.Design.default))
.padding(.bottom, 50)
TextField("Email", text: self.$emailAddress)
.frame(width: geometry.size.width - 45, height: 50)
.textContentType(.emailAddress)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.accentColor(.red)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.cornerRadius(5)
TextField("Password", text: self.$password)
.frame(width: geometry.size.width - 45, height: 50)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.foregroundColor(.gray)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.textContentType(.password)
.cornerRadius(5)
Button(action: {
self.settings.loggedIn = true
}) {
HStack {
Text("Log In")
}
.padding()
.frame(width: geometry.size.width - 40, height: 40)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
}
.padding(.bottom, 40)
Divider()
Button(action: {
print("Take to forget password VC")
}) {
Text("Forgot your password?")
}
Spacer()
}
.padding(.bottom, 90)
}
}
}
我知道如果我在修改状态时更新视图(在文本字段中键入时),则会出现此错误。但我不会在登录屏幕的任何地方更新视图。那么为什么会出现这个错误。帮助将不胜感激!
这可能与您的问题无关,但在 Xcode 11 Beta 4 中,Apple 将 "didset" 更改为 "willset",将 "didChange" 更改为 "willChange" 在 Xcode 11 Beta 5 中,苹果将 "willChange" 更改为 "objectWillChange"。
因此 StartView() 应该是:
class UserSettings: ObservableObject {
var objectWillChange = PassthroughSubject<Void, Never>()
@Published var loggedIn : Bool = false {
willSet {
objectWillChange.send(())
}
}
}
这对我有用,您甚至不需要导入 Combine!当你使用 @Published
时,SwiftUI 将自动合成 objectWillChange
主题,并在 属性 发生变化时调用发送。如果需要,您仍然可以手动调用 .send()
,但在大多数情况下您不会。
class UserSettings: ObservableObject {
@Published var loggedIn : Bool = false
}
Beta 5 发行说明摘录:
You can manually conform to ObservableObject by defining an objectWillChange publisher that emits before the object changes. However, by default, ObservableObject automatically synthesizes objectWillChange and emits before any @Published properties change.
这是对我来说工作正常的完整代码(iPhone Xr 和真实设备,iPad 第六代):
window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(UserSettings()))
import SwiftUI
struct ContentView: View {
var body: some View {
StartView()
}
}
class UserSettings: ObservableObject {
@Published var loggedIn : Bool = false
}
struct StartView: View {
@EnvironmentObject var settings: UserSettings
var body: some View {
if settings.loggedIn {
return AnyView(Text("LOGGED IN"))
} else {
return AnyView(LogInView())
}
}
}
struct LogInView: View {
@EnvironmentObject var settings: UserSettings
@State private var emailAddress: String = ""
@State private var password: String = ""
var body: some View {
GeometryReader { geometry in
VStack (alignment: .center){
HStack {
Image(systemName: "2.circle.fill")
.resizable()
.frame(width: 20, height: 20)
Text("Social App")
.font(.system(size: 12))
}.padding(.top, 30)
.padding(.bottom, 10)
Text("Log In to Your Account")
.font(.title)
.font(.system(size: 14, weight: .bold, design: Font.Design.default))
.padding(.bottom, 50)
TextField("Email", text: self.$emailAddress)
.frame(width: geometry.size.width - 45, height: 50)
.textContentType(.emailAddress)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.accentColor(.red)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.cornerRadius(5)
TextField("Password", text: self.$password)
.frame(width: geometry.size.width - 45, height: 50)
.padding(EdgeInsets(top: 0, leading: 5, bottom: 0, trailing: 0))
.foregroundColor(.gray)
.background(Color(red: 242 / 255, green: 242 / 255, blue: 242 / 255))
.textContentType(.password)
.cornerRadius(5)
Button(action: {
self.settings.loggedIn = true
}) {
HStack {
Text("Log In")
}
.padding()
.frame(width: geometry.size.width - 40, height: 40)
.foregroundColor(Color.white)
.background(Color.blue)
.cornerRadius(5)
}
.padding(.bottom, 40)
Divider()
Button(action: {
print("Take to forget password VC")
}) {
Text("Forgot your password?")
}
Spacer()
}
.padding(.bottom, 90)
}
}
}
我想这是一个错误。您收到的这条消息也发生在这个简单的视图上,该视图通过用户输入过滤掉列表条目。只是在文本字段中快速键入会导致此问题。如果您在文本字段中输入第一个字符,UI 会卡住一段时间。
struct ContentView: View {
@State private var list: [String] = (0..<500).map { "Text \([=10=])" }
@State private var searchText: String = ""
var filteredList: [String] {
guard !searchText.isEmpty else { return list }
return list.filter({ [=10=].contains(self.searchText) })
}
var body: some View {
VStack {
TextField("Search", text: $searchText)
List(filteredList, id: \String.self) { t in Text(t) }
}
.padding()
}
}
解决方法是将@State 变量移动到模型中。所以这似乎是@State 的问题:
class Model: ObservableObject {
@Published var list: [String] = (0..<500).map { "Text \([=11=])" }
@Published var searchText: String = ""
var filteredList: [String] {
guard !searchText.isEmpty else { return list }
return list.filter({ [=11=].contains(self.searchText) })
}
}
struct ContentView: View {
@ObservedObject var model: Model
var body: some View {
VStack {
TextField("Search", text: $model.searchText)
List(model.filteredList, id: \String.self) { t in Text(t) }
}
.padding()
}
}
不要用 if 分支,使用 .opacity(_:)
@ViewBuilder
var body: some View {
// if settings.loggedIn {
TabbarView().opacity(settings.loggedIn ? 1 : 0)
// } else {
ContentView().opacity(settings.loggedIn ? 0 : 1)
// }
}