web-dev-qa-db-fra.com

'La modification de l'état lors de la mise à jour de la vue entraînera un comportement indéfini.' erreur lors de la saisie sur un champ de texte (SwiftUI)

J'ai deux champs de texte, affectés à:

@State  private var emailAddress: String = ""
@State  private var password: String = ""

Maintenant, chaque fois que je tape dessus, l'application semble bloquée et me donne cette erreur:

'La modification de l'état lors de la mise à jour de la vue entraînera un comportement indéfini.'

J'ai une 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())
    }
}

}

J'ai créé une classe ObservableObject de UserSettings qui a une valeur booléenne loggedIn. Lorsque l'utilisateur appuie sur le bouton "Connexion" dans LogInView(), cette valeur booléenne devient true et une nouvelle vue apparaît (TabbarView())

C'est 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)
        }
}
}

Je sais que cette erreur apparaît si je mets à jour la vue pendant la modification de l'état (lors de la saisie dans le champ de texte). Mais je ne mets à jour la vue nulle part dans l'écran de connexion. Alors pourquoi cette erreur se produit. Votre aide sera appréciée!

7
Osama Naeem

Je suppose que c'est un bug. Ce message que vous avez reçu se produit également sur cette vue simple qui filtre les entrées de liste par entrée utilisateur. Une simple saisie rapide dans le champ de texte provoque ce problème. Si vous entrez le premier caractère dans le champ de texte, l'interface utilisateur est restée bloquée pendant un certain temps.

struct ContentView: View {
    @State private var list: [String] = (0..<500).map { "Text \($0)" }
    @State private var searchText: String = ""

    var filteredList: [String] {
        guard !searchText.isEmpty else { return list }
        return list.filter({ $0.contains(self.searchText) })
    }

    var body: some View {
        VStack {
            TextField("Search", text: $searchText)
            List(filteredList, id: \String.self) { t in Text(t) }
        }
        .padding()
    }
}

Une solution de contournement consiste à déplacer les variables @State dans un modèle. Cela semble donc être un problème avec @State:

class Model: ObservableObject {
    @Published var list: [String] = (0..<500).map { "Text \($0)" }
    @Published var searchText: String = ""

    var filteredList: [String] {
        guard !searchText.isEmpty else { return list }
        return list.filter({ $0.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()
    }
}
1
Patrick Sturm

Cela peut ne pas être lié à votre problème, mais dans Xcode 11 Beta 4, Apple a changé "didset" en "willset" et "didChange" en "willChange" Dans Xcode 11 Beta 5, Apple a changé " willChange "en" objectWillChange ".

Ainsi, StartView () devrait être:

class UserSettings: ObservableObject {

var objectWillChange = PassthroughSubject<Void, Never>()

@Published var loggedIn : Bool = false {
     willSet {
         objectWillChange.send(())
     }
 }
}
0
Gloorfindel