停止在 UITableView 的嵌套 UICollectionView 中播放视频或仅在嵌套结构中的可见单元格上播放

Posted

技术标签:

【中文标题】停止在 UITableView 的嵌套 UICollectionView 中播放视频或仅在嵌套结构中的可见单元格上播放【英文标题】:Stop playing video in nested UICollectionView in UITableView or Play only on the visible cell in nested structure 【发布时间】:2020-12-02 13:48:26 【问题描述】:

我已经为一个问题苦苦挣扎了很多天。我正在使用嵌套结构来显示多张照片和视频,例如 Instagram 提要。但问题是,当我启动 viewController 时,它开始播放视频并且在滚动时不会停止播放视频。如何仅播放嵌套结构中可见单元格的视频并停止所有其他视频?

在这里简单解释一下我的结构, UIViewController -> UITableView -> UITableViewCell -> UICollectioinView -> UICollectionViewCell -> section = 0(对于照片数组)+ section = 1(对于多个视频)。

extension HomeVC: UITableViewDelegate, UITableViewDataSource
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        postsArray.count
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "HomeTVC", for: indexPath) as! HomeTVC
        let objPost = postsArray[indexPath.row]
  
        cell.postImagesArray = []
        cell.postImagesArray = objPost.images ?? [Images].init()
        cell.videos = []
        cell.videos = objPost.videos ?? []
        
        return cell
    
    

    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
        
        guard let tableViewCell = cell as? HomeTVC else  return 
        tableViewCell.setCollectionViewDataSourceDelegate(dataSourceDelegate: self, forRow: indexPath.row)
    
    

UICollectionView 委托方法

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
        
        let  cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCVC", for: indexPath) as! HomeCVC
        
        if indexPath.section == 0 
            let images = postsArray[collectionView.tag].images?[indexPath.row]
            let imagePath = APIConstants.BasePathImage + (images?.post_merge_image ?? "")
            cell.imgPropertyView.sd_setImage(with: URL(string: imagePath), placeholderImage: UIImage(named: "placeholder"))
            return cell
         else 
            let videos = postsArray[collectionView.tag].videos?[indexPath.row]
            cell.videoView.isHidden = false
            let videoUrl = APIConstants.BasePathImage + (videos?.post_video ?? "")
            cell.playVideo(videoUrl)
            return cell
        

    

   func collectionView(_ collectionView: UICollectionView, 
     didEndDisplaying cell: 
        UICollectionViewCell, forItemAt indexPath: IndexPath) 
        guard let cell = cell as? HomeCVC else return
        cell.stopVideo()
    

【问题讨论】:

为什么你的didEndDisplaying方法在cellForItemAt里面? 您能帮我解决问题吗? func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath)UICollectionViewDelegate 中定义,因此您应该确保将委托以及数据源设置为您的tableViewCell,例如collectionView.dataSource = selfcollectionView.delegate = self,在这样做之后didEndDisplaying你的 tableViewCell 中的方法将被调用,你的逻辑应该工作 【参考方案1】:

因此,在为此困扰了很多天并付出了很多努力之后,我自己解决了这个问题。我会在这里为你们发布答案。

了解结构

UITableView > UITableViewCell > UICollectionView > UICollectionViewCell > MultipleSections > VideoLayer > 播放/暂停(对于可见单元格)

解决方案 - Swift 5

您将使用外部 UITableView 的 WillDisplay 方法将标签分配给内部 UICollectionView,并将类似地分配 cellForRowat 中的数据。所以在 UICollectionView DataSource/Delegates 中,你会用到这些方法。

CollectionView cellForItemAt 方法

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

            let  cell = collectionView.dequeueReusableCell(withReuseIdentifier: "HomeCVC", for: indexPath) as! HomeCVC
            let images = postsArray[collectionView.tag].images?[indexPath.row]
            if indexPath.section == 0  //if multiple sections for image and videos
                let imagePath = APIConstants.BasePathImage + (images?.post_merge_image ?? "")
                cell.imgPropertyView.sd_setImage(with: URL(string: imagePath), placeholderImage: UIImage(named: "placeholder"))
                return cell
             else 
                let videos = postsArray[collectionView.tag].videos?[indexPath.row]
                cell.videoView.isHidden = false
                let videoUrl = APIConstants.BasePathImage + (videos?.post_video ?? "") //your video Url
                cell.playVideo(videoUrl: videoUrl) // to load the video here and will play in willDisplay
                return cell

            
//        


    

CollectionView didEndDisplaying 方法

func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
        if let homeCVC = cell as? HomeCVC 
            homeCVC.stopVideo()
            print("Playing Stopped:")
            
        
    

CollectionView willDisplay 方法

您将加载 UICollectionView 的 cellForItemAt 方法,并仅在可见单元格分别位于视图边界和超出视图边界时使用这些方法播放和停止视频。

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) 
        
        if let homeCVC = cell as? HomeCVC 
            
            if indexPath.section == 1 
                homeCVC.player.play()
                print("Playing Started:")
            
            
        
    

【讨论】:

以上是关于停止在 UITableView 的嵌套 UICollectionView 中播放视频或仅在嵌套结构中的可见单元格上播放的主要内容,如果未能解决你的问题,请参考以下文章

具有共享滚动手势的嵌套 UITableView

嵌套 UITableView 动态高度

在 UIScrollView 中快速嵌套 UITableView

多层ScrollView嵌套UITableView、UICollectionView联动

UITableView 在滚动期间停止渲染

将标题添加到 UICollectionView,而不是 UICollectionView 的部分