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
}
}