Play video on UITableViewCell when it is completely visible

15,066

Solution 1

You can use

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    isScrolling = NO;
    [homeTabl reloadData];
}

-(void)scrollViewWillBeginDragging:(UIScrollView *)aScrollView
{
    isScrolling = YES;
    [homeTabl reloadData];
    index = -1;
}

And in cellForRowatindexpath

if (fullvisible && index == indexPath.row) {
        if (!isScrolling) {

            NSLog(@"video index---%d",indexPath.row);

            if (index == indexPath.row) {
                NSLog(@"video index---%d",indexPath.row);
                cell.videoActivity.hidden = NO;

                // if (index == indexPath.row) {
                NSURL *url = [NSURL URLWithString:[[responsearray objectAtIndex:indexPath.row]valueForKey:@"feed_video"]];

                dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

                dispatch_async(queue, ^{
                    cell.videoItem = [AVPlayerItem playerItemWithURL:url];

                    dispatch_sync(dispatch_get_main_queue(), ^{
                        cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
                        cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
                        cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
                        [cell.videoItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
                        [cell.videoItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];

                        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
                        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

                        cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
                        [cell.contentView.layer addSublayer:  cell.avLayer];
                        [ cell.videoPlayer play];
                        [cell.contentView addSubview:cell.videoActivity];
                    });
                });
                //                }
                //                else{
                //                    cell.videoActivity.hidden = YES;
                //                    cell.videoPlayer = nil;
                //                    [cell.avLayer removeFromSuperlayer];
                //                    cell.videoItem = nil;
                //                    [cell.videoPlayer pause];
                //                }
            }
        }}

    else{

        cell.videoActivity.hidden = YES;
        cell.videoPlayer = nil;
        [cell.avLayer removeFromSuperlayer];
        cell.videoItem = nil;
        [cell.videoPlayer pause];

    }

Solution 2

You're loading your video items synonymously in the main thread; that means your video items are being downloaded and loaded in table view cells at the same time the table view cells are being created. That causes the issue and make the scrolling very laggy and slow. To fix the problem you need to load your videos asynchronously, in a separate thread.

Therefore, you must avoid loading images synchronously, but asynchronously instead, using a different thread other than the main thread, so that the scrolling and loading + displaying images can be done dependently, avoiding any kind of lag.

This is possibly a duplicate question, since it's already been asked and there are tutorials out there, but since I myself always had a problem with this (but for pictures) on my first iOS app and found it very confusing, I'm posting the answer here, hoping it's helpful.

Try moving all the lines related to the video load/display, especially playerItemWithURL in your - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath: to a separate block. Also do search and read other post about "asynchronous video loading in table view cells". There are lots of in-depth tutorials and Q/As about this.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

dispatch_async(queue, ^{
cell.videoItem = [AVPlayerItem playerItemWithURL:url];

dispatch_sync(dispatch_get_main_queue(), ^{
    cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
        cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
        cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
        [cell.videoItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
        [cell.videoItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];

         cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
         [cell.contentView.layer addSublayer:  cell.avLayer];
         [ cell.videoPlayer play];
         [cell.contentView addSubview:cell.videoActivity];
    });
});

Solution 3

Avoid reloading the table view every time you want to play a video. You can perform your video playing code on the visible cell without having to reload the entire tableview.

Also, I suspect you don't really want to play the video until after the table view has stopped scrolling. See How to detect when a UIScrollView has finished scrolling (detecting when a UIScrollView is finished scrolling is surprisingly not intuitive).

If you do really want to start playing the video before the scrollview stops scrolling, at least modify your code to check if the visible cell is already playing, then do nothing. Right now your tableView:cellForRowAtIndexPath: code that plays a video is executed many, many times whenever a cell is fully visible (which I suspect happens a lot!).

Share:
15,066
Admin
Author by

Admin

Updated on June 26, 2022

Comments

  • Admin
    Admin almost 2 years

    I have an Array which contains video URLS, I want to play those videos on UITableviewcell when it is fully visible.

    I have tried this

    - (void)scrollViewDidScroll:(UIScrollView *)aScrollView 
    {
        NSArray* cells = homeTabl.visibleCells;
    
        for (HomeCell* cell in cells) 
        {
            if (cell.frame.origin.y > offset.y &&
            cell.frame.origin.y + cell.frame.size.height < offset.y + bounds.size.height)
            {
                NSIndexPath *path = [homeTabl indexPathForCell:cell] ;
                index = path.row;
                fullvisible = YES;
                [homeTabl reloadData];
            }
            else
            {
                fullvisible = NO;
            }
        }
    }
    
    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    {
         if (fullvisible) 
         {
              NSURL *url = [NSURL URLWithString:[[responsearray objectAtIndex:indexPath.row]valueForKey:@"feed_video"]];
    
              cell.videoItem = [AVPlayerItem playerItemWithURL:url];
              cell.videoPlayer = [AVPlayer playerWithPlayerItem:cell.videoItem];
              cell.avLayer = [AVPlayerLayer playerLayerWithPlayer:cell.videoPlayer];
              cell.videoPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
              [cell.videoItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];
              [cell.videoItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
    
              [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidBufferPlaying:) name:AVPlayerItemPlaybackStalledNotification object:nil];
              [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:nil];
    
              cell.avLayer.frame = CGRectMake(5, 9, 310, 310);
              [cell.contentView.layer addSublayer:cell.avLayer];
              [cell.videoPlayer play];
              [cell.contentView addSubview:cell.videoActivity];
         }
         else
         {
             cell.videoPlayer = nil;
             [cell.avLayer removeFromSuperlayer];
             cell.videoItem = nil;
             [cell.videoPlayer pause];
         }
    }
    

    This works fine - but it horrible slow down the scrolling of table view. Please suggest me some other better method.