#swift #swiftui #combine #observableobject
Вопрос:
У меня в приложении SwiftUI есть модель данных, которая выглядит примерно так:
struct Entry: Identifiable{
var id = UUID().uuidString
var name = ""
var duration: Int{
//An SQLite query that returns the total of the "duration" column
let total = try! dbc.scalar(tableFlight.filter(db.entry == id).select(db.duration.total))
return Int(total)
}
}
struct Flight: Identifiable{
var id = UUID().uuidString
var duration = 0
var entry: String?
}
У меня есть ObservableObject
модель представления, которая создает записи, подобные этой:
class EntryModel: ObservableObject{
static let shared = EntryModel()
@Published var entries = [Entry]()
init(){
get()
}
func get(){
//Stuff to fetch the entries
entries = //SQLite query that returns an array of Entry objects
}
}
Затем, наконец , в моем View
, я перечисляю все имена записей и связанные duration
с ними, как это:
ForEach(modelEntry.entries){ entry in
VStack{
Text(entry.name) //<-- Updates fine
Text(entry.duration) //<-- Gets set initially, but no updates
}
}
Проблема , с которой я сталкиваюсь, заключается в том, что, когда я обновляю a Flight
для этого Entry
, duration
на мой взгляд, он не обновляется. Я знаю, что этого не произойдет, потому что только entries
они будут перерисовываться, когда они будут изменены.
Но даже если я вручную вызову get()
функцию в моем EntryModel
, связанная функция все duration
равно не обновляется.
Есть ли лучший способ сделать это? Как заставить вычисленные свойства родителя пересчитываться при обновлении его дочернего элемента?
Комментарии:
1. Как насчет того, чтобы позвонить
objectWillChange.send()
послеget()
? Я не уверен, сработает это или нет, но попробовать стоит-по крайней мере, это даст сигнал представлению попытаться обновиться.2. Чего я не понимаю, так это того, что ваша продолжительность даже не зависит от чего-либо в вашей модели входа. Итак, что именно вы обновляете в своей модели входа?. Потому что dbc нет в вашей модели. Я могу только предположить (и опасаться), что это какая-то глобальная переменная (или вы использовали нижний регистр для класса, чтобы запутать читателя).
3. Как указано в комментарии под
duration
свойством, это запрос SQLite, который возвращает связанные данные.dbc
представляет собой синглтон базы данных. Однако на самом деле это не имеет отношения к рассматриваемому вопросу.4. Извините, но это просто кодирование против типов значений. Тип значения не должен иметь скрытых зависимостей, подобных этому, и особенно иметь возможность вести себя по-разному, если он используется в разные моменты времени. Эта функция должна быть глобальной функцией или, что еще лучше, частью класса, который раскрывает
dbc
зависимость.5. Я понимаю. Не делай этого. Это причинит тебе боль. Поместите этот запрос в инициализатор. Ваш запрос будет выполняться каждый раз, когда представление будет повторно отображено не только для описания, но и для чего угодно.
Ответ №1:
Я все понял. В моем реальном коде использовался дочерний View
элемент внутри ForEach
where the VStack
is. Я просто передавал entry
ему значение, так что значения устанавливались только изначально и, следовательно, не реагировали.
Изменив это entry
на Binding
«а», это работает:
ForEach($modelEntry.entries){ $entry in
ChildView(entry: $entry)
}
Обратите внимание, что $
на $modelEntry
и возврат $entry
-это функция Xcode 13 (но обратно совместимая с iOS 14 и macOS 11.0).