#swift #swiftui
#swift #swiftui
Вопрос:
В SwiftUI
, есть ли способ контролировать, какие @Published
свойства в @ObservableObject
триггере вызывают обновление представления?
На iPad и macOS я считаю обычным разделять a model
между несколькими представлениями, которые представлены на экране одновременно. Это model
ObservableObject
с многочисленными свойствами, которые есть @Published
. Однако вероятность любого из этих свойств, по-видимому, приводит к тому, что SwiftUI перестраивает иерархию представлений, даже если это не совсем необходимо.
Рассмотрим пример ниже. Существует model
вызываемый Order
объект, который имеет несколько опубликованных свойств. Параллельно представлены два представления, от которых зависят оба Order
. Если вы вносите изменения в Order.name
или Order.address
из CustomerView
, это CartView
также запускает обновление, даже если это зависит только от Order.items
.
В идеале мне нужен способ указать CustomerView
игнорировать обновления Order.items
и указывать CartView
игнорировать обновления Order.name
и Order.address
. Но поскольку order
это @EnvironmentObject
, я не могу понять, как это сделать.
Альтернативное решение:
Один из способов, которым я мог бы обойти это, не использовать @EnvironmentObject
и вместо этого просто передать Order
через стандартный инициализатор свойств. Затем объявите отдельные @Binding
свойства для CustomerView
и CartView
для управления состоянием. Это работает, но быстро становится большим дублированием, если представления и модели имеют много свойств.
Не то чтобы это имело слишком большое значение, но это в основном для разработки iPadOS и macOS.
┌──────────────────────────────────────────┐
│ ContentView │
│ │
│┌──────────────────┐ ┌──────────────────┐ │
││ │ │ │ │
││ CustomerView │ │ CartView │ │
││ │ │ │ │
││ │ │ │ │
│└──────────────────┘ └──────────────────┘ │
└──────────────────────────────────────────┘
final class Order: ObservableObject {
@Published var name: String
@Published var items: [Item]
@Published var address: String
}
struct ContentView: View {
@StateObject var order = Order()
var body: some View {
HStack {
CustomerView()
CartView()
}.environmentObject(order)
}
}
struct CustomerView: View {
@EnvironmentObject var order: Order
// This view gets rebuilt when 'order.items' is changed.
var body: some View {
VStack {
TextField("Name", text: $order.name)
TextField("Address", text: $order.address)
}
}
}
struct CartView: View {
@EnvironmentObject var order: Order
// This view gets rebuilt when 'order.name' or 'order.address' is changed.
var body: some View {
List(order.items, id: .id) { item in
ItemView(item)
}
}
}
Комментарии:
1. Я думаю, тогда вам нужно будет реорганизовать свой код. Использование
@ObservedObject
or@EnvironmentObject
в основном означает, что всякий раз, когда объект уведомляет о каких-либо изменениях (черезobjectWillChange
), тело представления должно быть пересчитано. Если вы этого не хотите, выполните рефакторинг2. Согласен, но я чувствую, что упускаю что-то очевидное.
@EnvironmentObject
требуется@ObservableObject
so, если вы хотите передать модель с использованием среды, тогда модель должна быть наблюдаемой. В настольном приложении на основе документов модель может легко иметь большое количество свойств, а окно может легко иметь большое количество представлений, которые всем необходимы для доступа к «текущему документу». Но вы, конечно, не хотите, чтобы все представления обновлялись при изменении одного свойства документа.3. Вы слишком беспокоитесь об иерархии представлений, SwiftUI (должен быть) достаточно умен, чтобы повторно отображать только представления, подписанные на изменяющиеся данные, независимо от того, сколько опубликованных свойств имеет ваш наблюдаемый объект
4. Это похоже на ошибку, представление не должно обновляться, если в зависимых свойствах нет изменений. Я рекомендую отправить отзыв в Apple.
5. [Cristik] Может быть, и так, но, по крайней мере, в macOS поведение, которое я вижу, таково, что любое
View
свойство с наблюдаемым объектом в качестве свойства будет перестроено, если какое-либо опубликованное свойство этого объекта будет изменено, независимо от того, используется ли это свойство для создания представления. В больших настольных приложениях это может привести к перестроению множества представлений, когда в этом нет необходимости. Это вызывает заметное замедление при выполнении действий пользователя, таких как выбор и перетаскивание, которые могут обновлять модель.