В SwiftUI, как вы ограничиваете, какие @Published свойства ObservableObject запускают обновление представления?

#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 свойство с наблюдаемым объектом в качестве свойства будет перестроено, если какое-либо опубликованное свойство этого объекта будет изменено, независимо от того, используется ли это свойство для создания представления. В больших настольных приложениях это может привести к перестроению множества представлений, когда в этом нет необходимости. Это вызывает заметное замедление при выполнении действий пользователя, таких как выбор и перетаскивание, которые могут обновлять модель.