Сообщение в консоли iOS 15: «Действие привязки пыталось обновляться несколько раз за кадр».

#xcode #swiftui #ios15

Вопрос:

У меня есть следующий код:

 struct Credential: Equatable {
    var username = ""
    var password = ""
}

enum Database {
    static var credential = Credential()
}

struct UsernamePasswordInput: View {
    @Binding var credential: Credential

    var body: some View {
        Group {
            TextField("Username", text: self.$credential.username)
            SecureField("password", text: self.$credential.password)
        }
        .textFieldStyle(RoundedBorderTextFieldStyle())
    }
}

struct Login: View {
    private var credential: Binding<Credential>

    var body: some View {
        UsernamePasswordInput(credential: self.credential)
    }

    init() {
        self.credential = Binding<Credential>(
            get: {
                Database.credential
            }, set: { newValue in
                Database.credential = newValue
            }
        )
    }
}

struct ContentView: View {
    var body: some View {
        Login()
    }
}
 

При запуске этого кода из Xcode 13.0 на устройстве iOS 15 Binding<String> action tried to update multiple times per frame. в консоли Xcode появляется сообщение. Это происходит всякий раз, когда вы вводите символ либо в TextField , либо в SecureField . Сообщение не отображается на устройствах iOS 13 или 14.

Я знаю, что могу решить эту проблему, просто используя a @StateObject , но приведенный выше код отражает структуру более крупного проекта. Мой вопрос в следующем: сообщение Xcode каким-то образом указывает на проблему? Является ли приведенный выше код каким-то принципиально неверным?

Комментарии:

1. Вполне вероятно, что обычай инициализирован. SwiftUI не очень хорошо справляется с ними. Он может решить перезагрузить весь вид, когда захочет. Вместо пользовательской привязки просто используйте обычную привязку. Это похоже на оксюморон, когда частная переменная хакерски обновляется извне.

2. @loremipsum К вашему сведению: я переместил создание привязки в представлении входа в систему в экземпляр представления UsernamePasswordInput, и это не удалило сообщение.

Ответ №1:

Вот возможный безопасный обходной путь — используйте встроенную разделенную привязку для каждого текстового поля:

 struct UsernamePasswordInput: View {
    @Binding var credential: Credential

    var body: some View {
        Group {
            TextField("Username", text: Binding(
                get: { credential.username },
                set: { credential.username = $0 }
                ))
            SecureField("password", text: Binding(
                get: { credential.password },
                set: { credential.password = $0 }
                ))
        }
        .textFieldStyle(RoundedBorderTextFieldStyle())
    }
}
 

Протестировано с Xcode 13 / iOS 15

Комментарии:

1. Да, так оно и было. Очевидно, разница в том, что мой код привязывается к структуре, а ваш код привязывается к строке этой структуры. Можете ли вы объяснить, почему было неправильно привязываться к структуре вне представления?