【问题标题】:How Set Model Data Before TableView Reload如何在 TableView 重新加载之前设置模型数据
【发布时间】:2020-11-21 00:49:49
【问题描述】:

TableView 在设置模型数据(topRatedModel)之前重新加载,因此模型保持为零。我使用了主胎面,但它不起作用。我怎样才能解决这个问题 ?您可以在以下位置查看我的代码:https://github.com/melisaozturk/MVVM (主分支是最新的)

    class MovieViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    let viewModel = MovieViewModel()
    var topRatedModel: MovieTopRatedModel!
    var nowPlayingModel: MovieNowPlayingModel!
    var popularModel: MoviePopularModel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.tableRegister()
        self.viewModel.getTopRatedData(completion: { [weak self] response in
            if let _ = self {return}
            self!.topRatedModel = response
        }, completionHandler: { [weak self] error in
             if let _ = self {return}
        })
        
        
        self.viewModel.getNowPlayingData(completion: { [weak self] response in
            if let _ = self {return}
            self!.nowPlayingModel = response
        }, completionHandler: { [weak self] error in
            if let _ = self {return}
        })
        
        self.viewModel.getPopularData(completion: { [weak self] response in
            if let _ = self {return}
            self!.popularModel = response
        }, completionHandler: { [weak self] error in
            if let _ = self {return}
        })
        
        DispatchQueue.main.async {
            self.tableView.reloadData()
        }
       
    }

     private func tableRegister() {
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.separatorStyle = .none
        self.tableView.tableFooterView = UIView()
        self.tableView.separatorStyle = .none
        
        self.tableView.register(UINib(nibName: "MovieCell", bundle: nil), forCellReuseIdentifier: "MovieCell")
    }
}

【问题讨论】:

    标签: ios swift closures tableview codable


    【解决方案1】:

    所有数据加载完成后,您需要重新加载表格视图。您的代码现在会在这些网络操作仍在运行时重新加载表格视图。

    现在,您有三个网络操作,您不想重新加载,直到它们全部完成并且您不知道它们将按什么顺序完成。

    您可以在这里使用DispatchGroup 为您提供帮助。

    在DispatchGroup 上使用notify,您可以在调度组为空时执行一些代码。

    您在处理 weak self 时也存在一些错误 - 如果 self 不为零,您的代码将返回。你想要相反的结果。

    override func viewDidLoad() {
            super.viewDidLoad()
            
            self.tableRegister()
    
            let dispatchGroup = DispatchGroup()
            
    
            dispatchGroup.enter()
            self.viewModel.getTopRatedData(completion: { [weak self] response in
                if let self = self else {
                    self.topRatedModel = response               
                }
                dispatchGroup.leave()
            }, completionHandler: { [weak self] error in
                 dispatchGroup.leave()
            })
            
            dispatchGroup.enter()
            self.viewModel.getNowPlayingData(completion: { [weak self] response in
                if let self = self {
                    self.nowPlayingModel = response
                }
                dispatchGroup.leave()
            }, completionHandler: { [weak self] error in
                dispatchGroup.leave()
            })
            
            self.viewModel.getPopularData(completion: { [weak self] response in
                if let self = self {
                    self.popularModel = response
                }
                dispatchGroup.leave()
            }, completionHandler: { [weak self] error in
                dispatchGroup.leave()
            })
            
            dispatchGroup.notify {
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
           
        }
    

    从风格的角度来看,我会使用 Result 类型或至少一个接受 (Response?,Error?) 的闭包,而不是使用两个闭包。

    【讨论】:

      【解决方案2】:

      编辑:我犯了一个错误,建议将异步更改为同步。这是错误的,因为它可能导致主线程被阻塞,正如@Paulw11 指出的那样。

      我能想到的解决方案是使用 DispatchGroups 或调度信号量。我个人对 DispatchGroups 更满意,所以我会给你一些伪代码,说明你可能希望如何实现它:

      override func viewDidLoad() {
          super.viewDidLoad()
          
          self.tableRegister()
      
          let dispatcher = DispatchGroup()
      
          //for every function that you want to wait for you make an dispatcher.enter()
          dispatcher.enter()
          self.viewModel.getTopRatedData(completion: { [weak self] response in
              if let _ = self {return}
              self!.topRatedModel = response
          }, completionHandler: { [weak self] error in
               if let _ = self {
                   // when each of the functions has finished running you can write a dispatcher.leave()
                   dispatcher.leave()
                   return
               }
          })
          
          // This code will wait for all dispatcher.leaves() to run in order to execute the following code.
          dispatcher.notify(.main){
              self.tableView.reloadData()
          }
         
      }
      

      我希望我能提供一点帮助。如果您需要更多说明,我很乐意提供帮助。

      【讨论】:

      • 你绝对不想使用sync() - 你有阻塞主线程的风险。它也不起作用,因为异步网络任务没有同步
      • 哦,谢谢!我不知道。我会尝试更加了解未来的回复,并将编辑此回复。另外我的意思是我不太确定,而不是说我是,对不起。
      猜你喜欢
      • 2023-03-05
      • 1970-01-01
      • 2018-06-07
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多