如何仅从Web提取数据后才显示tableView? (迅速)

时间:2019-02-18 23:11:51

标签: ios swift multithreading uitableview uicollectionview

在加载嵌套在表视图单元格中的Collection视图时遇到了困难。单元格内部的内容仅在滚动表几次后才会显示。我的方法是使用DispatchGroup()以便在后台线程中获取数据,但是没有用。要在不滚动表的情况下一次显示所有信息怎么办?

ViewDidLoad

override func viewDidLoad() {

    super.viewDidLoad()

    _tableView.isHidden = true
    _tableView.dataSource = nil
    _tableView.delegate = nil
    SVProgressHUD.show()
    view.backgroundColor = UIColor.flatBlack()

    getData()

    dispatchGroup.notify(queue: .main) {
        SVProgressHUD.dismiss()
        self._tableView.isHidden = false
        self._tableView.dataSource = self
        self._tableView.delegate = self
        self._tableView.reloadData()

    }
}

UICollectionView和UITableView数据源/ OtherMethods

func getData(){

        dispatchGroup.enter()

        backend.movieDelegate = self

        backend.actorDelegate = self

        backend.getMoviePopularList()

        backend.getMovieTopRatedList()

        backend.getMovieUpcomingList()

        backend.getPopularActors()

        backend.getMovieNowPlayingList()

        dispatchGroup.leave()
    }


    func transferMovies(data: [String:[MovieModel]]) {
        dispatchGroup.enter()
        popularMovies = data
        dispatchGroup.leave()
    }

    func transferActors(data: [ActorModel]) {
        dispatchGroup.enter()
        popularActors = data
        dispatchGroup.leave()

    }



    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        guard let cell = tableView.dequeueReusableCell(withIdentifier: "DiscoverCell") as? DiscoverViewCell else { return UITableViewCell()}

        cell.categoryLabel.text = cell.categories[indexPath.item]
        //categories[indexPath.item]
        cell._collectionView.delegate = self
        cell._collectionView.dataSource = self
        cell._collectionView.tag = indexPath.row
        cell._collectionView.reloadData()

        self.setUpCell(cell)

        return cell
    }


    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MovieCell", for: indexPath) as? MovieCollectionViewCell else { return UICollectionViewCell()}

        if collectionView.tag == 0{

            if let movieDetails = popularMovies["Popular"]?[indexPath.item] {
                cell.updateMovieCollectionCell(movie: movieDetails)
            }
        } else if collectionView.tag == 1{

            if let movieDetails = popularMovies["Top rated"]?[indexPath.item] {

                cell.updateMovieCollectionCell(movie: movieDetails)
            }
        } else if collectionView.tag == 2{

            if let movieDetails = popularMovies["Upcoming"]?[indexPath.item] {

                cell.updateMovieCollectionCell(movie: movieDetails)

            } else if collectionView.tag == 3{

                cell.movieTitleLabel.text = popularActors?[indexPath.item].name ?? ""
                cell.moviePicture.image = popularActors?[indexPath.item].poster

            }
        } else if collectionView.tag == 4{

            if let movieDetails = popularMovies["Now playing"]?[indexPath.item] {
                cell.updateMovieCollectionCell(movie: movieDetails)
            }

        }

        return cell
    }

MovieCollectionViewCell

class MovieCollectionViewCell: UICollectionViewCell {

        @IBOutlet weak var moviePicture: UIImageView!
        @IBOutlet weak var movieTitleLabel: UILabel!

        func updateMovieCollectionCell(movie: MovieModel){
            moviePicture.image = movie.poster
            movieTitleLabel.text = movie.name
        }

    }

DiscoverViewCell

class DiscoverViewCell: UITableViewCell {

        @IBOutlet weak var categoryLabel: UILabel!
        @IBOutlet weak var _collectionView: UICollectionView!

        let categories = ["Popular", "Top Rated", "Upcoming", "Popular People", "Now playing"]

        @IBAction func seeMoreAction(_ sender: Any) {

        }
    }

我的意图是显示一个加载动画,直到获取所有数据为止,并显示包含从Web获取数据的集合视图的表视图单元格。

打开应用程序时,所需的结果应如下所示

The desired result should look like this when opening the app

3 个答案:

答案 0 :(得分:0)

尝试一下...从后端获取数据并分配给moviedetails之后,将委托和数据源设置为self并重新加载表。

答案 1 :(得分:0)

据我所知,您使用的DispatchGroup不正确。

总结notify()

  • 当组中所有当前排队的块完成时,它将运行关联的块
  • 该块仅运行一次然后释放
  • 如果组的队列为空,则该块将立即运行

我看到的问题是,通过编写提取代码的方式,当您调用notify时,该组认为其队列为空。因此,notify()块将立即运行,然后您只会在滚动期间重新加载单元格时看到填充的单元格。

有两种方法可以填充dispatchGroup的队列:

  • 调用DispatchQueue.async()并传递组以直接排队一个块并将其与该组关联
  • 在块开始时手动调用enter(),在块结束时手动调用leave(),这会增加/减少组上的内部计数器

第一种方法更安全,因为您不必自己跟踪块,但是第二种方法则更加灵活,例如,如果您无法控制块在哪个队列上运行。

由于您使用的是enter/leave,因此需要确保为每个单独的工作项目调用enter()(在您的情况下,是对backend的异步调用),并且仅每个工作项完成时,请致电leave()。我不确定您如何使用委托方法,但似乎每个backend调用都没有,因为有5个不同的调用,只有2个委托方法。如果在后端调用中发生错误,看起来也不会调用委托方法。

我建议将backend调用更改为使用完成块,但是,如果您要坚持使用委托模式,可以采用以下方法:

func getData(){
    backend.movieDelegate = self
    backend.actorDelegate = self

    dispatchGroup.enter()
    backend.getMoviePopularList()

    dispatchGroup.enter()
    backend.getMovieTopRatedList()

    dispatchGroup.enter()
    backend.getMovieUpcomingList()

    dispatchGroup.enter()
    backend.getPopularActors()

    dispatchGroup.enter()
    backend.getMovieNowPlayingList()

    dispatchGroup.notify(queue: .main) {
        SVProgressHUD.dismiss()
        self._tableView.isHidden = false
        self._tableView.dataSource = self
        self._tableView.delegate = self
        self._tableView.reloadData()
    }
}

func transferPopularMovies(data: [MovieModel]) {
    popularMovies = data
    dispatchGroup.leave()
}

func transferTopRatedMovies(data: [MovieModel]) {
    topRatedMovies = data
    dispatchGroup.leave()
}

func transferUpcomingMovies(data: [MovieModel]) {
    upcomingMovies = data
    dispatchGroup.leave()
}

func transferActors(data: [ActorModel]) {
    popularActors = data
    dispatchGroup.leave()
}

func transferNowPlayingMovies(data: [MovieModel]) {
    nowPlayingMovies = data
    dispatchGroup.leave()
}

即使存在错误以确保enter/leave调用是平衡的,也不要忘记调用委托方法。如果您调用enter的次数多于leave,则notify块将永远不会运行。如果您更频繁地致电leave,则会崩溃。

答案 2 :(得分:0)

将此行设置在getData()函数的底部并运行

    self._tableView.isHidden = false
    self._tableView.dataSource = self
    self._tableView.delegate = self
    self._tableView.reloadData()

并从viewdidload()中删除