I have a table view cell for vertical listing and have added a collection view in a horizontal playlist. I want to implement video playback similar to the YouTube app. Specifically, I aim to have the video play when the cell is properly visible and pause when it becomes invisible in the collection view. I'm using AVPlayer. I've almost finished, but I'm facing one issue. Please check the code.
For example, the collection view array:
Table array:- [Test:[video.mp4, image.jpg, image.jpg, image.jpg, image.jpg, image.jpg, video.mp4],Test2:[video.mp4, image.jpg, image.jpg, image.jpg, image.jpg, image.jpg, video.mp4],Test3:[video.mp4, image.jpg, image.jpg, image.jpg, image.jpg, image.jpg, video.mp4]]
Collection array:- [video.mp4, image.jpg, image.jpg, image.jpg, image.jpg, image.jpg, video.mp4]
When the app runs, it will automatically display and check if it contains an mp4 file, then play the video automatically and pause when the video duration finishes. The issue I'm facing is that when we scroll vertical(Scroll Tablview) through the screen and reach index 2 or 3, the last video automatically plays.
Code:-
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
stringArrayUrl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "VideoCell", for: indexPath) as! VideoCell
if stringArrayUrl[indexPath.item].contains(".mp4") || stringArrayUrl[indexPath.item].contains(".MP4") {
cell.videoMainView.isHidden = false
cell.videoThumbImg.getThumbnailImageFromVideoUrl(url: URL(string: self.stringArrayUrl[indexPath.item])!) { (thumbNailImage) in
cell.videoThumbImg.image = thumbNailImage
}
cell.imgPlayBtn.actionBlock {
self.playVideoWhileScrlling(cell: cell, indexPath: indexPath)
}
}
return cell
}
func playVideoWhileScrlling(cell:StoryTableImageCell,indexPath:IndexPath){
if self.stringArrayUrl.indices.contains(indexPath.item+1) {
self.checkNextVideoUrlIsAvalibleOrNot(indexPath: indexPath.item)
}
cell.videoPlayerView.isHidden = false
self.returnVideoPlayer(inputUrl: self.stringArrayUrl[indexPath.item])
}
func returnVideoPlayer(inputUrl:String) {
let playerItem = AVPlayerItem(url: URL(string: inputUrl)!)
videoPlayer.replaceCurrentItem(with: playerItem)
videoPlayer.play()
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let cell = cell as? VideoCell {
if stringArrayUrl[indexPath.item].contains(".mp4") || stringArrayUrl[indexPath.item].contains(".MP4") {
playVideoWhileScrlling(cell: cell, indexPath: indexPath)
}
}
}
Question: How to pause the video when scrolling and Play a video when cell is visible on the screen. ?
Can someone please explain to me how to do this, I've tried with the above code but have no results yet. Please Correct me if I'm doing wrong.
Any help would be greatly appreciated
Table View Code
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
scrollToMostVisibleCell()
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate{
scrollToMostVisibleCell()
}
}
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stopvideo"), object: nil)
}
func scrollToMostVisibleCell() {
let visibleRect = CGRect(origin: tableVW.contentOffset, size: tableVW.bounds.size)
let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
guard let visibleIndexPath = tableVW.indexPathForRow(at: visiblePoint) else { return }
if let cell = tableVW.cellForRow(at: visibleIndexPath) as? FeedBookTVC {
let convertedRect = tableVW.convert(cell.frame, to: cell.collectionVW)
if let visibleCollectionViewIndexPath = cell.collectionVW.indexPathForItem(at: CGPoint(x: convertedRect.midX, y: convertedRect.midY)) {
let collectionViewIndex = visibleCollectionViewIndexPath.item
if collectionViewIndex < cell.imagesData.count {
if let cell2 = cell.collectionVW.cellForItem(at: visibleCollectionViewIndexPath) as? FeedBookCVC {
if cell.imagesData[collectionViewIndex].video != "" {
cell.configureVideoPlayer(view: cell2.videoData, videoURL: URL(string: "\(imageBaseURL)\(cell.imagesData[collectionViewIndex].video ?? "")")!, placeholderImageURL: "\(imageBaseURL)\(cell.imagesData[collectionViewIndex].thumnails ?? "")")
cell.playVideo()
}
}
}
}
}
print(visibleIndexPath,"indxxxxxxxx")
for indexPath in tableVW.indexPathsForVisibleRows ?? [] {
if indexPath != visibleIndexPath {
if let cell = tableVW.cellForRow(at: indexPath) as? FeedBookTVC {
cell.stopVideo()
}
}
}
}
Collection View Cell
var selectIndex = 0
var visibleIndex = 0
var imagesData = [AllFeedsModelFeedsImage]()
var player: AVPlayer?
var playerLayer: AVPlayerLayer?
override func awakeFromNib() {
super.awakeFromNib()
NotificationCenter.default.addObserver(self, selector: #selector(stopvideo), name: NSNotification.Name(rawValue: "stopvideo"), object: nil)
}
@objc func stopvideo(){
self.stopVideo()
}
func configureVideoPlayer(view: UIView, videoURL: URL, placeholderImageURL: String) {
player = AVPlayer(url: videoURL)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = view.bounds
playerLayer?.videoGravity = .resizeAspectFill
view.layer.addSublayer(playerLayer!)
player?.play()
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying(_:)), name: .AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
}
@objc func playerDidFinishPlaying(_ notification: Notification) {
player?.seek(to: .zero)
player?.play()
}
func playVideo() {
guard let player = self.player else { return }
player.play()
}
func stopVideo() {
guard let player = self.player else { return }
player.pause()
}
func isVisible() -> Bool {
if self.window == nil {
return false
}
let displayBounds = UIScreen.main.bounds
let selfFrame = self.convert(self.bounds, to: UIApplication.shared.keyWindow)
let intersection = displayBounds.intersection(selfFrame)
let visibility = (intersection.width * intersection.height) / (frame.width * frame.height)
return visibility >= 0.9
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return imagesData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "FeedBookCVC", for: indexPath) as! FeedBookCVC
cell.btnPlay.isHidden = true
cell.setData(imagesDataa: imagesData[indexPath.item])
return cell
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let firstVisibleCellIndexPath = collectionVW.indexPathsForVisibleItems.first
if let firstVisibleCellIndexPath = firstVisibleCellIndexPath {
visibleIndex = firstVisibleCellIndexPath.item
collectionVW.reloadItems(at: [firstVisibleCellIndexPath])
}
}
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if let cell = cell as? FeedBookCVC{
if imagesData[indexPath.item].thumnails != "" {
if visibleIndex == indexPath.item {
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stopvideo"), object: nil)
cell.videoData.isHidden = false
if imagesData[indexPath.item].video != "" {
configureVideoPlayer(view: cell.videoData, videoURL: URL(string: "\(imageBaseURL)\(imagesData[indexPath.item].video ?? "")")!, placeholderImageURL: "\(imageBaseURL)\(imagesData[indexPath.item].thumnails ?? "")")
}
}else{
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "stopvideo"), object: nil)
}
}else{
cell.videoData.isHidden = true
player?.pause()
}
}
}