iOS开发-视频播放AVPlayer(AVPlayer的封装、tableviewcell上视频播放以及滑动播放暂停)

写在前面、感谢作者

使用AVPlayer自定义支持全屏的播放器http://www.jianshu.com/p/11e05d684c05

仿微博视频边下边播之滑动TableView自动播放

说明:本文使用的是第一篇文章作者的框架,加入第二篇文章作者的思路,实现了 在tabbleViewCell上播放视频、滑动自动播放暂停以及对播放器的定制等等。注意:此处并不包含播放完成cell自动滑动的功能,这一点也不难,相信你学会了滑动自动播放,这一功能也就水到渠成了。

项目效果图

关于制作GIF图的一种方法请跳转

videoPlay.gif

非常遗憾,不满足苹果审核要求,项目迟迟未能上线、不能被大家所下载

2017年7月11日 下午11:37
发件人 Apple
Guideline 3.2.2 - Business - Other Business Model Issues - Unacceptable

The feature in your app displays or promotes third-party apps, which is not appropriate for the App Store.

Please see attached screenshots for details.

Next Steps

To resolve this issue, please remove the feature from your app.

隐藏好像都已经无可救药了

2017年7月13日 下午8:37
发件人 Apple

  1. 2.2 Business: Other Business Model Issues - Unacceptable
    Guideline 3.2.2 - Business - Other Business Model Issues - Unacceptable

Your app displays or promotes third-party apps, which is not appropriate for the App Store.

Next Steps

We encourage you to review your app concept and incorporate different content and features that are in compliance with the App Store Review Guidelines.

对比 作者与我修改后的界面(我只是单纯的修改了一下界面)

作者的界面:


author1.png
author2.png

我修改后的界面:


modify1.png
modify2.png

修改地方:
将原来播放暂停的按钮移动到屏幕正中央,在左下角播放暂停按钮处添加一个静音按钮,也就是要么静音要么以某一音量播放(我添加的为0.3)

回到正题:AVPlayer的封装、tableviewcell上视频播放以及滑动播放暂停

实现原理:关于tableviewcell上播放视频的一些说明:首先、我们并不是让每一个cell都拥有一个播放器,而是让tableview所在的控制器拥有一个播放器,然后根据计算来决定哪个cell此时应该播放视频,进而将控制器的播放器 添加 到cell的contentView上。在滑动过程中,我们会不断的判断正在播放的cell是否滑出屏幕或者正在播放的cell需要被另一个cell所替代,通过判断来对播放器的销毁以及重新初始化并添加到相应的cell上。

让控制器拥有一个播放器是我在项目实践过程中想到的一个方案并付诸实践,效果还可以,至少从内存的角度可以看到变化。由原先的 130M左右降到现在的65M左右。

准备工作:下载作者或我的playerView代码
作者GitHubhttps://github.com/JmoVxia/CLPlayer

个人修改后的代码

说明:我会直接使用我项目中的截图来给大家分享,没有一个完整的demo。但是我会教你怎么使用。

1.0关于cell中对于播放器view的布局 以及 cell需要添加一个videoFrame的属性

playercell.png

播放器所在的视图结构大致就是这样。将播放暂停按钮的操作传到tableview所在的控制器,这里有一点需要注意:那就是 计算出 播放器所在的视图也就是上面videoView的frame,传递给控制器以便初始化播放器的时候将其添加到cell上。所以,给每个需要播放视频的cell 添加一个属性,videoFrame,来记录videoView的frame

+ (instancetype)cellWithTableView:(UITableView *)tableView;

@property (nonatomic,strong)ConventionsModel *convention;

//用来记录播放器在cell的frame
@property (nonatomic,assign)CGRect videoFrame;

@property (nonatomic,assign)id<DiscoverVideoCellDelegate> delegate;

@end

在给cell设置数据后并强制布局后再计算videoFrame

- (void)setConvention:(ConventionsModel *)convention
{
    _convention = convention;

// 这里给cell上的控件赋值


//如果cell的高度需要自适应,这里一定要强制布局,然后再计算videoFrame,否则、播放器播放时可能不会在你想要的地方
[self layoutIfNeeded];

// convention.cellHeight = CGRectGetMaxY(self.gameIcon.frame) + CGRectGetMinY(self.titleLabel.frame);
//    }
    
    self.videoFrame = self.videoView.frame;
}

2.0在控制器中点击播放暂停按钮进行播放 或者 进入控制器后自动播放

理论说明:如果不需要自动播放,那么通过代理将cell上的播放暂停按钮点击事件传递给控制器后,我们只需要 初始化 控制器拥有的那个播放器属性,并将其添加到点击的cell上即可。

在需要自动播放时,我们就需要进行分类判断,当进入该控制器,请求到数据后,播放当前tableview上的第一个(最上面的)视频,当滑动tableview时,播放cell中心(更准确的说:应该是cell上videoView的中心)距离屏幕中心最近的cell上的视频。

下面我直接分享自动播放的情况,在控制器中添加两个属性、一个是播放器(view)一个是正在播放的cell。倘若你的tableview中只有一种cell,你可以直接使用该类型的cell,因为我的项目中并不是每个cell上都有视频播放,所有我直接用UITableViewCell,模态属性,父类指向子类。

@property (nonatomic,strong)CLPlayerView *clPlayerView;

@property (nonatomic,strong)UITableViewCell *playingCell;

3.0请求到数据后播放第一个视频

我们可以在请求到数据的回调里面刷新列表后播放第一个视频

[self.tableView reloadData];
            
//进入界面播放
[self playVideoInVisiableCells];

3.1进入这个界面就自动播放

//进入这个界面就自动播放
-(void)playVideoInVisiableCells{
    
//下面是我实际项目中代码。在这里你需要做的就是获得第一个有视频的cell,并进行播放和记录。播放视频可以抽到一个方法中,需要的就是播放视频所在的cell以及视频播放所需要的地址URL等。可以仿照我的进行写代码,当然,如果你的每个cell类型相同且都有视频,你可以直接获取可见的cell  NSArray *visiableCells = [self.tableView visibleCells]; 判断 visiableCells.count是否大于0,正在播放的cell就是第一个了。接下来我们说一说播放视频- (void)initPlayerView:(DiscoverVideoCell *)cell playClick:(ConventionsModel *)convention


    // 找到下一个要播放的cell(最在屏幕中心的)
    DiscoverVideoCell *firstCell = nil;
    NSArray *visiableCells = [self.tableView visibleCells];
    
    //存放大框播放视频
    for (int i = 0; i < visiableCells.count; i++) {
        UITableViewCell *cell = visiableCells[i];
        
        if ([cell isKindOfClass:[DiscoverVideoCell class]]) {
            firstCell = (DiscoverVideoCell *)cell;
            break;
        }
    }
    
    //播放第一个视频
    [self initPlayerView:firstCell playClick:firstCell.convention];

}

4.0播放视频

初始化视频播放器,并将其添加到正在播放的cell上

- (void)initPlayerView:(DiscoverVideoCell *)cell playClick:(ConventionsModel *)convention
{
  self.playingCell = cell;
//    self.playingIndexPath = [self.tableView indexPathForCell:cell];
    
    [_clPlayerView destroyPlayer];
    _clPlayerView = nil;
    
    CLPlayerView *playerView = [[CLPlayerView alloc] initWithFrame:cell.videoFrame];
    _clPlayerView = playerView;
    
    [cell.contentView addSubview:_clPlayerView];
    
    //视频地址
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        _clPlayerView.url = convention.url;
        //播放
        [_clPlayerView playVideo];
        
    });
    
    //返回按钮点击事件回调
    [_clPlayerView backButton:^(UIButton *button) {
        NSLog(@"返回按钮被点击");
    }];
    
    //播放完成回调
    [_clPlayerView endPlay:^{
        
        //销毁播放器
        [_clPlayerView destroyPlayer];
        _clPlayerView = nil;
        NSLog(@"播放完成");
    }];
}

5.0滚动播放暂停

//滚动播放暂停说明:对于正在播放的视频、我们需要时刻监听cell是否移除界面;而对于正在播放的cell被替代时,我们可以在滚动停止时进行播放器的初始化并播放。
所以、只需要在以下三个代理方法中实现即可。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
   
//我发现作者对于内存的释放有一点问题,播放器销毁后内存并没有下降
//    [_clPlayerView calculateScrollOffset:self.tableView cell:self.playingCell];

//我尝试去修改作者的,似乎也没有起作用,所有自己在改方法中进行销毁
    NSArray *cells = [self.tableView visibleCells];
    if (![cells containsObject:self.playingCell]) {
        
        if (_clPlayerView) {
            //销毁播放器
            [_clPlayerView destroyPlayer];
            _clPlayerView = nil;
        }
        
    }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    //滑动播放
    [self handleScrollPlaying:scrollView];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    
    if (!decelerate){
        //滑动播放
        [self handleScrollPlaying:scrollView];
    }
    
}

//这里参考第二篇文章的计算方式,更准确的计算方式应该是:cell中videoView的中心距屏幕的中心最近的cell为正在播放的cell。
//滑动播放
- (void)handleScrollPlaying:(UIScrollView *)scrollView
{
    // 找到下一个要播放的cell(最在屏幕中心的)
    DiscoverVideoCell *finnalCell = nil;
    NSArray *visiableCells = [self.tableView visibleCells];
    
    //存放大框播放视频
    NSMutableArray *tempVideoCells = [NSMutableArray array];
    for (int i = 0; i < visiableCells.count; i++) {
        UITableViewCell *cell = visiableCells[i];
        
        if ([cell isKindOfClass:[DiscoverVideoCell class]]) {
            [tempVideoCells addObject:cell];
        }
    }
    
    
    NSMutableArray *indexPaths = [NSMutableArray array];
    CGFloat gap = MAXFLOAT;
    for (DiscoverVideoCell *cell in tempVideoCells) {
        
        [indexPaths addObject:[self.tableView indexPathForCell:cell]];
        
//计算距离屏幕中心最近的cell
        CGPoint coorCentre = [cell.superview convertPoint:cell.center toView:nil];
        CGFloat delta = fabs(coorCentre.y-[UIScreen mainScreen].bounds.size.height*0.5);
        if (delta < gap) {
            gap = delta;
            finnalCell = cell;
        }
        
    }
    
    // 注意, 如果正在播放的cell和finnalCell是同一个cell, 不应该在播放
    if (finnalCell != nil && self.playingCell != finnalCell)  {  
        if (_clPlayerView) {
            [_clPlayerView destroyPlayer];
            _clPlayerView = nil;
        }
        
        [self initPlayerView:finnalCell playClick:finnalCell.convention];
        
        self.playingCell = finnalCell;
        return;
    }
}


在tableviewcell上播放视频,在这里基本上就结束了。目前在项目中使用良好。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,631评论 4 59
  • Summary: This distinction between knowledge and skills li...
    不是猫阅读 208评论 0 0
  • 我们都是宇宙间小小的尘埃 有着平凡的梦想和爱 每天穿梭在城市的缝隙里 错过了日出和日落 扛起行囊 背井离乡 这个世...
    微笑的百合花阅读 287评论 7 7
  • 尊敬的各位长辈、亲朋好友、小朋友们: 大家中午好! 今天是笑笑12岁生日庆典!作为妈妈的我格外激动!在这...
    王敏11阅读 680评论 0 1
  • 6月2日 星期五 晴 今晚在外边吃完饭后,他们还在喝着,聊着,我和如梦便在夜市逛了一圈。 走出门后,很远...
    XHF_6de4阅读 158评论 0 0