Изменения TableView работают некорректно в swiftUI

Я использую UITableView в SwiftUI. Я получаю данные из WebSocket и хочу обновить свое табличное представление в реальном времени. Я успешно получаю данные из сети, обновляю их в viewModel и передаю в QuotesTableView.

TableView не обновляет мои данные правильно, т.е. первое значение tableViewData — Symbol1, но оно показывает данные Symbol1 в первом IndexPath. Я отлаживаю его в функции cellForRowAt и вижу, что получаю правильные данные, но не отображаю правильный элемент в ячейке. Он случайным образом меняет порядок данных в моем табличном представлении, но порядок tableviewData не меняется.

   @StateObject private var viewModel = QuotesViewModel()

   var body: some View {
      ZStack {
         Color.init(hex: "#293c54").edgesIgnoringSafeArea(.all)

         QuotesTableView(tableViewData: $viewModel.list)
            .background(Color.clear)
      }
   }
import Combine

class QuotesViewModel: ObservableObject {
   var cancellables = Set<AnyCancellable>()
   
   // MARK: - Input
   var selectedSymbols: String
   
   /// Symbols list
   @Published var list: [SymbolsInDataModel] = .init()
   
   // MARK: - Output
   
   // MARK: - Init
   init() {
      
      socket = SocketManager.shared
      
      observeSocketValues()
      bindView()
   }
   
   // MARK: - Business Logic
   let socket: SocketManager
   
   // MARK: - Config
}

// MARK: - Bind View
extension QuotesViewModel {
   /// observe view actions in here...
   func bindView() {
      
   }
}


// MARK: - Observation Socket Data
extension QuotesViewModel {
   func observeSocketValues() {
      socket.$symbolsList.sink(receiveValue: { newSymbols in
         self.list = newSymbols
      })
      .store(in: &cancellables)

      
      socket.$symbolsList
         .filter { !$0.isEmpty }
         .first { _ in
            self.list = self.socket.symbolsList
            return true
         }
         .sink(receiveValue: {_ in})
         .store(in: &cancellables)
   }
}

struct QuotesTableView: UIViewRepresentable {   
   // The data source for the table view
   @Binding var tableViewData: [SymbolsInDataModel]
   var selectClicked: ((_ item: SymbolsInDataModel) -> Void)?
   
   func makeUIView(context: Context) -> UITableView {
      let tableView = UITableView()
      tableView.translatesAutoresizingMaskIntoConstraints = false
      tableView.backgroundColor = .clear
      tableView.dataSource = context.coordinator
      tableView.showsVerticalScrollIndicator = false
      tableView.delegate = context.coordinator
      tableView.register(HostingCell.self, forCellReuseIdentifier: "Cell")
      
      return tableView
   }
   
   func updateUIView(_ uiView: UITableView, context: Context) {
      // Reload the table view data whenever the data changes
      
      uiView.reloadData()

   }
   
   func makeCoordinator() -> Coordinator {
      Coordinator(self)
   }
 
}



// MARK: - CompetitionsTableView -> Coordinator
extension QuotesTableView {
   class Coordinator: NSObject, UITableViewDataSource, UITableViewDelegate {
      
      var parent: QuotesTableView
      
      init(_ tableView: QuotesTableView) {
         parent = tableView
      }
      
      func numberOfSections(in tableView: UITableView) -> Int {
         return 1
      }
      
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
         parent.tableViewData.count
      }
      
      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
         tableView.deselectRow(at: indexPath, animated: false)
      }
      
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! HostingCell
         tableViewCell.backgroundColor = .clear
         
         print("indexpath: \(indexPath.row) item: \(parent.tableViewData[indexPath.row])")
         
         // Set the root view of the hosting controller to the view for this cell
         let row = parent.tableViewData[indexPath.row]
         let hostingController = UIHostingController(
            rootView: AnyView(
               QuotesCellView(item: row, selectAction: self.parent.selectClicked)
            )
         )
         
         hostingController.view.backgroundColor = .clear
         
         // create & setup hosting controller only once
         if tableViewCell.host == nil {
            tableViewCell.host = hostingController
            
            let tableCellViewContent = hostingController.view!
            tableCellViewContent.translatesAutoresizingMaskIntoConstraints = false
            tableViewCell.contentView.addSubview(tableCellViewContent)
            tableCellViewContent.topAnchor.constraint(equalTo: tableViewCell.contentView.topAnchor).isActive = true
            tableCellViewContent.leftAnchor.constraint(equalTo: tableViewCell.contentView.leftAnchor).isActive = true
            tableCellViewContent.bottomAnchor.constraint(equalTo: tableViewCell.contentView.bottomAnchor).isActive = true
            tableCellViewContent.rightAnchor.constraint(equalTo: tableViewCell.contentView.rightAnchor).isActive = true
         } else {
            // reused cell, so just set other SwiftUI root view
            tableViewCell.host = hostingController
         }
         tableViewCell.setNeedsLayout()
         return tableViewCell
         
      }
   }
}

Когда ячейка представления таблицы используется повторно, вы назначаете новый hostingController, но представление не изменяется. Таким образом, он покажет данные последнего раза, когда эта ячейка использовалась для другой строки.

Geoff Hackworth 04.04.2023 09:36

как я могу это исправить? Можете ли вы показать мне пример кода, пожалуйста?

Kenan 04.04.2023 10:47
Coordinator(self) — это ошибка, если QuotesTableView перезапускается, то self — это старое значение, makeCoordinator вызывается только один раз.
malhal 04.04.2023 16:02
Стоит ли изучать 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
3
52
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Чтобы решить проблему с перезапуском ячеек, вам нужно либо изменить данные в представлении хостинга, либо создать новое. Ваш старый код, кажется, хочет создать новый (который менее эффективен), поэтому небольшое изменение для удаления старого представления хостинга должно решить эту проблему.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         let tableViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! HostingCell
         tableViewCell.backgroundColor = .clear
         
         print("indexpath: \(indexPath.row) item: \(parent.tableViewData[indexPath.row])")
         
         // Set the root view of the hosting controller to the view for this cell
         let row = parent.tableViewData[indexPath.row]
         let hostingController = UIHostingController(
            rootView: AnyView(
               QuotesCellView(item: row, selectAction: self.parent.selectClicked)
            )
         )
         
         hostingController.view.backgroundColor = .clear

         // remove any previous view if the cell is being reused
         tableViewCell.host?.view.removeFromSuperView()
         
         // create & setup hosting controller
         tableViewCell.host = hostingController
            
         let tableCellViewContent = hostingController.view!
         tableCellViewContent.translatesAutoresizingMaskIntoConstraints = false
         tableViewCell.contentView.addSubview(tableCellViewContent)
         tableCellViewContent.topAnchor.constraint(equalTo: tableViewCell.contentView.topAnchor).isActive = true
         tableCellViewContent.leftAnchor.constraint(equalTo: tableViewCell.contentView.leftAnchor).isActive = true
         tableCellViewContent.bottomAnchor.constraint(equalTo: tableViewCell.contentView.bottomAnchor).isActive = true
         tableCellViewContent.rightAnchor.constraint(equalTo: tableViewCell.contentView.rightAnchor).isActive = true
       
         tableViewCell.setNeedsLayout()
         return tableViewCell
         
      }

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