Изменение свойства «Published» ObservableObject на вычисляемую переменную.
У меня есть свойства:
class SettingsViewState: ObservableObject {
@Published var viewData: SettingsViewData = .init()
…
Я хотел бы перейти на вычисляемую переменную, основанную на других источниках истины, вместо того, чтобы позволять ее напрямую изменять. Тем не менее, я все еще хочу, чтобы просмотренные представления автоматически обновлялись по мере их изменения. Они должны обновляться, когда свойства вычисляются из изменения.
Хотя я действительно не очень уверен в том, как на самом деле это работает. Я думаю, что это имеет своеwillSet
выполнить вложение до того, как произойдет изменение, но я не уверен!
Если мои подозрения верны, похоже, я мог бы вручную вызвать включающий объект, если что-то от него изменится.
Альтернативно, если свойстваviewData
вычисляется из самих себя, когда я меняю один, предположительно эквивалентobjectWillChange.send()
произойдет автоматически, и мне не нужно будет делать ничего особенного? Это должно работать, даже если эти свойстваprivate
и наблюдающее представление не имеет к ним доступа: оно все равно должно видетьobjectWillChange
излучается?
Однако вполне возможно, что я написал это ужасно искаженно или в основном наоборот! Например, возможно,@Published
свойства имеют собственного независимого издателя изменений, а не просто используют прилагаемый файлObservableObject
х? Или они оба публикуются до изменения?
Разъяснения будут приняты с благодарностью. Спасибо!
3 ответа
Я действительно не очень уверен в том, как на самом деле работает @Published. Я думаю, что у него есть метод willSet, выполняющий objectWillChange.send() для окружающего ObservableObject до того, как произойдет изменение, но я не уверен!
Вы правы, именно так и работают вместе, однако это 2 независимые вещи.
— это оболочка свойства, которая добавляетPublisher
к обернутому свойству, которое выдает новое значение изwillSet
собственности. ИтакPublished.Publisher
выдает значение, которое должно быть установлено, прежде чем оно будет фактически установлено.
— это протокол, у которого есть издатель, значение которого автоматически синтезируется компилятором. Синтезированная реализация выдает значение, когда выдает его любой из издателей свойства. Таким образом, выдается значение всякий раз, когда свойство соответствующего типа собирается измениться.
Если вы сохраняете соответствующий тип в SwiftUIView
как@StateObject
,@ObservedObject
или@EnvironmentObject
, представление обновляется (и, следовательно, пересчитывает своеbody
) всякий раз, когдаobjectWillChange
принадлежащийObservableObject
излучает.
Если у вас есть свойство, которое необходимо пересчитывать при каждом обновлении других свойств, и вы хотите обновить свое представление с учетом этих изменений, у вас есть несколько вариантов добиться этого.
- Объявите все свойства как хранимые свойства и настройте зависимое свойство на обновление при каждом обновлении любого из свойств, от которых оно зависит.
Это 100% гарантирует, что ваше представление всегда будет обновляться правильными значениями, и вам не нужно будет вызыватьobjectWillChange.send()
вручную.
Это решение также работает, даже если свойства, от которых зависит ваше «вычисляемое» свойство, объявлены в других типах, поскольку ваше «вычисляемое» свойство таково, когда оно обновляется, оно запускает обновление представления.
Однако вам необходимо настроить наблюдение за зависимыми свойствами, чтобы обновлять свойство, которое от них зависит.
@Published var height: Int
@Published var width: Int
@Published private(set) var size: Int
init(height: Int, width: Int) {
self.height = height
self.width = width
self.size = height * width
$height.combineLatest($width).map { height, width in
height * width
}.assign(to: &$size)
}
- Если все свойства, от которых зависит ваше вычисляемое свойство, объявлены в том же объекте, что и ваше вычисляемое свойство, простое объявление свойства, которое зависит от них, как вычисленное, должно работать, поскольку свойства, от которых вы зависите, сами будут запускать обновление представления всякий раз, когда они обновляются.
Однако это не работает, если ваш@Published
свойства объявляются для другого объекта, поскольку этот объект не вызывает обновление представления.
Используйте для просмотра данных (значений или структур с изменяющимися функциями).ObservableObject
изначально был разработан для срока службы данных модели.
В структуре View используйте вычисляемое свойство для преобразования данных при их передаче дочернему элементу.View
структуры. Обычно мы преобразуем расширенные типы моделей в простые значения, которые передаются в доступные для предварительного просмотра представления.
Более позднее добавление@StateObject
предназначен для случаев, когда вам нужен ссылочный тип в@State
что необычно и, похоже, здесь не требуется.
Я пытался ответить на этот же вопрос для себя и попал сюда. Я знаю, что проблема уже «решена» уже пару месяцев, но я нашел другой способ, который работает, по крайней мере, для моего сценария.
Вместо того, чтобы вычислять общедоступное свойство, вы можете возложить работу на частные свойства, чтобы обновлять общедоступные свойства всякий раз, когда они обновляются с использованием их файлов .
import Foundation
class ThemeList: ObservableObject {
// this is the property I wanted to be computed
@Published public private(set) var themes: [String]
// these are the "sources of truth"
private let internalThemes: [String]
private var customThemes: [String] {
// this is where the magic happens!
didSet {
bundleThemes()
}
}
init(themes: [String], customThemes: [String] = []) {
self.internalThemes = themes
self.customThemes = customThemes
self.themes = []
bundleThemes()
}
// set the (no-longer computed) @Published property
// triggering the
func bundleThemes() {
var allThemes: [String]
allThemes = internalThemes
allThemes.append(contentsOf: customThemes)
allThemes.sort()
themes = allThemes
}
func addTheme(_ theme: String) {
customThemes.append(theme)
}
}
let themeList = ThemeList(themes: ["Hi", "Hello"])
let cancellable = themeList.$themes
.sink() {
print ($0)
}
themeList.addTheme("Howdy!")
Вы можете запустить это как есть на игровой площадке и увидеть, что новое значение
Изначально я пытался сделать