SwiftUI View с @State, который инициализируется в конструкторе init()

У меня такая простая галочка SwiftUI view. Он отображается в списке, и его можно переключить, нажав на него, или можно переключить при обновлении из источника данных (например, Core Data)

Моя первая реализация была

struct RoundedCheckmark: View {

    @State var isChecked : Bool
    let onCheck: (Bool) -> Void

    func toggle() { isChecked = !isChecked; onCheck(isChecked) }

    init(isChecked: Bool, onCheck: @escaping (Bool) -> Void = { _ in }) {
        self._isChecked = State(initialValue: isChecked)
        self.onCheck = onCheck
    }

    var body: some View {

        Button(action: toggle) {

            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")
        }
    }
}

Казалось, что я могу переключить его, и он загружается правильно. При переключении я сохранил изменения с помощью закрытия / обратного вызова onCheck, и он обновился нормально.

Но после внешних обновлений, таких как обновление push-уведомлений, базовая модель в Core Data обновление этого представления неправильно изменяет свойство @State isChecked.

В init() я получаю новое значение isChecked и повторно инициализирую @State с состоянием (initialValue:). Но в теле я получаю старое значение isChecked из старого свойства @State. так что представление действительно имеет неправильное изображение.

Вот обходной путь, то есть полагаться только на свойство let isChecked и обратный вызов onCheck. Но теперь у меня не может быть состояния в моем представлении, и мне приходится полагаться только на изменение внешнего источника данных. Возможно, это более уместно, так как у меня есть единственный источник истины без локального хранилища @State.

struct RoundedCheckmark: View {

    let isChecked: Bool
    let onCheck: (Bool) -> Void

    func toggle() { onCheck(isChecked) }

    init(isChecked: Bool, onCheck: @escaping (Bool) -> Void = { _ in }) {

        self.isChecked = isChecked
        self.onCheck = onCheck
    }

    var body: some View {

        Button(action: toggle) {

            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")

        }

    }
}

1 ответ

isChecked как @Stateпеременная - ваш источник истины. Это означает, что изменение какой-либо базовой модели данных (например, CoreData) не имеет значения. Ваш взгляд смотрит только на местных@State версия isChecked.

Но посмотри на свой взгляд. Для меня он не должен владеть своим собственным состоянием. Почему? Потому что это представление в виде галочки не имеет семантического значения. Похоже, что его родительский объект владеет состоянием (поэтому существуетonCheckПерезвоните). Вместо этого следует использовать@Bindingс не обратного вызова:

struct RoundedCheckmark: View {
    @Binding var isChecked: Bool

    var body: some View {
        Button(action: { isChecked.toggle() }) {
            Image(isChecked ? "CheckedCheckmark" : "UncheckedCheckmark")
        }
    }

Теперь ваш родитель владеет состоянием, и вы можете определить его семантическое значение:

struct CheckmarkOwner: View {
    @State var showFavoritesOnly = false

    var body: some View {
        // content

        RoundedCheckmark(isChecked: $showFavoritesOnly)

        // and now something else will get notified when `showFavoritesOnly` gets toggled
        if showFavoritesOnly {
             // toggleable content
        }

        // more content
    }
}
Другие вопросы по тегам