Как прослушать изменение данных с помощью переменной @Published, а затем перезагрузить tableView

Самая трудная задача, с которой я сталкиваюсь, это знать правильную терминологию для поиска. Я привык к SwiftUI как к простому способу создания приложения в кратчайшие сроки. В этом проекте я должен использовать UIKit и для этой конкретной задачи.

Внутри контроллера представления я создал tableView:

private let tableView: UITableView = {
    let table = UITableView()
    table.register(ProfileCell.self, forCellReuseIdentifier: ProfileCell.identifier)
    return table
}()

Позже перезагружаю данные внутри viewDidLoad


override func viewDidLoad() {
    super.viewDidLoad()
    
    Task {
        do {
            try await viewModel.getProfiles()

            // Here I reload the table when data comes in
            self.tableView.reloadData()
        } catch {
            print(error)
        }
    }
    
    view.addSubview(tableView)
    tableView.delegate = self
    tableView.dataSource = self
}

Итак, что такое viewModel? В SwiftUI я привык иметь это внутри структуры представления:


@ObservedObject var viewModel = ProfilesViewModel()

... и это то, что у меня есть внутри моего контроллера представления. Я искал:

  • наблюдаемый объект в uitableview
  • uitableview перезагружать данные при изменении данных

..и многое другое, но отмечу, что мне было полезно «собирать по кусочкам».

В том же контроллере я showMyViewControllerInACustomizedSheet, который теперь использует UIHostingController:


private func showMyViewControllerInACustomizedSheet() {
    // A SwiftUI view along with viewModel being passed in
    let view = ProfilesMenu(viewModel: viewModel)
    let viewControllerToPresent = UIHostingController(rootView: view)
    if let sheet = viewControllerToPresent.sheetPresentationController {
        sheet.detents = [.medium(), .large()]
        sheet.largestUndimmedDetentIdentifier = .medium
        sheet.prefersScrollingExpandsWhenScrolledToEdge = false
        sheet.prefersEdgeAttachedInCompactHeight = true
        sheet.widthFollowsPreferredContentSizeWhenEdgeAttached = true
    }
    present(viewControllerToPresent, animated: true, completion: nil)
}

Для ProfilesViewModel:


class ProfilesViewModel: ObservableObject {

  // ProfilesResponse is omitted
  @Published var profiles = [ProfilesResponse]()

  public func getProfiles(endpoint: String? = nil) async throws -> Void {
    
   // After getting the data, I set the profiles variable
   self.profiles = [..]
  }
}

Всякий раз, когда я звоню try await viewModel.getProfiles(endpoint: "...") из ProfileMenu, я хочу перезагрузить tableView. Какая дополнительная настройка требуется?

Только SwiftUI просмотры реагируют на @Published неявно. В UIKit вы можете использовать Combine и замыкание обратного вызова.

vadian 27.11.2022 08:49

Привет @vadian. Я уже видел это слово «Комбинировать». Спасибо. Я почитаю, как он используется.

Sylar 27.11.2022 08:51
@Published является частью Combine Framework. Вам нужен подписчик, чтобы иметь возможность прослушивать изменения.
vadian 27.11.2022 08:54

@vadian Нашел это, потому что вы упомянули Combine: swiftbysundell.com/articles/published-properties-in-swift Спасибо за подсказку.

Sylar 27.11.2022 09:03

Вы реализовали функции func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell и func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int?

Brianna Doubt 27.11.2022 09:17

Независимо от того, обернуты ли они Published, это не должно препятствовать отображению данных в представлении, они просто не используются UIKit так, как это происходит со SwiftUI. У меня такое чувство, что ваша проблема связана с реализацией источника данных.

Brianna Doubt 27.11.2022 09:20

Привет. Да, это нужно было реализовать, но мне нужен был способ перезагрузить таблицу при изменении viewModel. Я опубликовал ответ, который работает.

Sylar 27.11.2022 10:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
589
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В комментариях Вадиан упомянул «Комбайн», где я провел поиск в Google и нашел это. Что работает, для базовой демонстрации:


[..]
import Combine

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
  private let viewModel = ProfilesViewModel()
  private var cancellable: AnyCancellable?

  override func viewDidLoad() {
      super.viewDidLoad()
      
      Task {
          do {
              try await viewModel.getProfiles()

              // Remove this
              // self.tableView.reloadData()
          } catch {
              print(error)
          }


      }
      
      view.addSubview(tableView)
      tableView.delegate = self
      tableView.dataSource = self

      // Add this
      cancellable = viewModel.objectWillChange.sink(receiveValue: { [weak self] in
        self?.render()
      })
  }

  // Also add this
  private func render() {
    // TODO: Implement failures...
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
  }

  ...
}

objectWillChange был ключом к моей проблеме.

Другие вопросы по теме