Latest 0.0.5
Homepage https://github.com/kingly09/KYVedioPlayer
License MIT
Platforms ios 7.0, requires ARC
Dependencies Masonry
Frameworks MediaPlayer, AVFoundation, UIKit
Authors

KYVedioPlayer 是基于AVPlayer的封装视频播放器,支持播放mp4、m3u8、3gp、mov等格式;支持网络视频和本地视频播放;支持全屏和小屏幕播放;还在UITableViewCell中播放视频 ;支持横屏竖屏自动播放。

安装

要求

  • Xcode 7 +
  • iOS 7.0 +

KYVedioPlayer播放器的布局依赖Masonry框架,注意工程是否包含Masonry库,如果没有的话,可以使用CocoaPods安装Masonry库。

    pod 'Masonry', '~> 1.0.1'

手动安装

下载DEMO后,将子文件夹 KYVedioPlayerLib 拖入到项目中, 导入头文件KYVedioPlayer.h 开始使用.

CocoaPods安装

你可以在 Podfile 中加入下面一行代码来使用 KYAlertView

    pod 'KYVedioPlayer'

实现的功能

  1. 支持播放mp4、m3u8、3gp、mov等格式的视频播放;
  2. 支持网络视频和本地视频播放;
  3. 支持全屏和小屏幕播放;
  4. 支持UITableViewCell中播放视频;
  5. 支持横屏竖屏自动播放;

如何使用

基本功能

step 1 :创建控制器

在您需要使用KYVedioPlayer播放器功能的类中,import 头文件KYVedioPlayer.h即可 。
设置 KYVedioPlayerDelegate委托代理 代码示例如下:

#import "KYVedioPlayer.h"

@interface KYLocalVideoPlayVC ()<KYVedioPlayerDelegate>{
    KYVedioPlayer  *vedioPlayer;
    CGRect     playerFrame;
}

step 2 :初始化

初始化VedioPlayer 初始化需要几个步骤:

  • 准备需要视频播放的UIView;
  • 新建player;
  • 设置url;
  • 调用 [vedioPlayer play] 开始播放。还可以设置播放器进度条的颜色,关闭按钮是否显示,视频标题等。

新建一个 化VedioPlayer 播放器,代码示例如下:

    playerFrame = CGRectMake(0, 0, kScreenWidth, (kScreenWidth)*(0.75));
   vedioPlayer = [[KYVedioPlayer alloc]initWithFrame:playerFrame];
   vedioPlayer.delegate = self;
   vedioPlayer.URLString = _URLString;
   vedioPlayer.titleLabel.text = self.title;
   vedioPlayer.closeBtn.hidden = NO;
   vedioPlayer.progressColor = [UIColor orangeColor];
   [self.view addSubview:vedioPlayer];

step 3 :启动播放器和暂停

启动

[vedioPlayer play];    

暂停

[vedioPlayer pause];    

step 4 :设置监听,屏幕旋转的通知,代码示例如下:

[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(NotificationDeviceOrientationChange:)
                                             name:UIDeviceOrientationDidChangeNotification
                                           object:nil
 ];

根据屏幕旋转的通知,是否全屏,是否缩小,代码示例如下:

 -(void)NotificationDeviceOrientationChange:(NSNotification *)notification{

     if (vedioPlayer == nil|| vedioPlayer.superview==nil){
         return;
     }

     UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
     UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
     switch (interfaceOrientation) {
         case UIInterfaceOrientationPortraitUpsideDown:{
             NSLog(@"第3个旋转方向---电池栏在下");
         }
             break;
         case UIInterfaceOrientationPortrait:{
             NSLog(@"第0个旋转方向---电池栏在上");
             if (vedioPlayer.isFullscreen) {
                 [self setNeedsStatusBarAppearanceUpdate];
                 [vedioPlayer showSmallScreenWithPlayer:vedioPlayer withFatherView:self.view withFrame:playerFrame];

             }
         }
             break;
         case UIInterfaceOrientationLandscapeLeft:{
             NSLog(@"第2个旋转方向---电池栏在左");
             vedioPlayer.isFullscreen = YES;
             [self setNeedsStatusBarAppearanceUpdate];
             [vedioPlayer showFullScreenWithInterfaceOrientation:interfaceOrientation player:vedioPlayer withFatherView:self.view];
         }
             break;
         case UIInterfaceOrientationLandscapeRight:{
             NSLog(@"第1个旋转方向---电池栏在右");
             vedioPlayer.isFullscreen = YES;
             [self setNeedsStatusBarAppearanceUpdate];
             [vedioPlayer showFullScreenWithInterfaceOrientation:interfaceOrientation player:vedioPlayer withFatherView:self.view];
         }
             break;
         default:
             break;
     }

 }

step 5 :注销播放器,代码示例如下:

 /**
  *  注销播放器
  **/
 - (void)releasePlayer
 {
     [vedioPlayer resetKYVedioPlayer];
      vedioPlayer = nil;
 }

 - (void)dealloc
 {
     [self releasePlayer];
     [[NSNotificationCenter defaultCenter] removeObserver:self];
     NSLog(@"KYLocalVideoPlayVC deallco");
 }

step 6 :其它操作,如隐藏状态栏,设置播放器进度条的颜色,关闭按钮是否显示,获取正在播放的时间点等,代码示例如下:

  • 隐藏状态栏
/**
 *  隐藏状态栏
 **/
-(BOOL)prefersStatusBarHidden{
    return YES;
}
  • 设置播放器进度条的颜色
vedioPlayer.progressColor = [UIColor orangeColor];
  • 关闭按钮是否显示,NO为显示关闭按钮 ,YES为隐藏关闭按钮
 vedioPlayer.closeBtn.hidden = NO;
  • 获取正在播放的时间点
[vedioPlayer currentTime];

播放器事件

播放器的几种状态,为KYVedioPlayerState枚举类型

事件ID 含义说明
KYVedioPlayerStateFailed 播放失败
KYVedioPlayerStateBuffering 缓冲中
KYVedioPlayerStatusReadyToPlay 将要播放
KYVedioPlayerStatePlaying 播放中
KYVedioPlayerStateStopped 暂停播放
KYVedioPlayerStateFinished 播放完毕

播放器回调

播放器所有点击事件的回调都会通过这个KYVedioPlayerDelegate反馈给您的App.

//点击播放暂停按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedPlayOrPauseButton:(UIButton *)playOrPauseBtn{

    NSLog(@"[KYVedioPlayer] clickedPlayOrPauseButton ");
}
//点击关闭按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedCloseButton:(UIButton *)closeBtn{

    NSLog(@"[KYVedioPlayer] clickedCloseButton ");

}
//点击全屏按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedFullScreenButton:(UIButton *)fullScreenBtn{
    NSLog(@"[KYVedioPlayer] clickedFullScreenButton ");

}
//单击WMPlayer的代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer singleTaped:(UITapGestureRecognizer *)singleTap{

    NSLog(@"[KYVedioPlayer] singleTaped ");
}
//双击WMPlayer的代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer doubleTaped:(UITapGestureRecognizer *)doubleTap{

    NSLog(@"[KYVedioPlayer] doubleTaped ");
}

///播放状态
//播放失败的代理方法
-(void)kyvedioPlayerFailedPlay:(KYVedioPlayer *)kyvedioPlayer playerStatus:(KYVedioPlayerState)state{
    NSLog(@"[KYVedioPlayer] kyvedioPlayerFailedPlay  播放失败");
}
//准备播放的代理方法
-(void)kyvedioPlayerReadyToPlay:(KYVedioPlayer *)kyvedioPlayer playerStatus:(KYVedioPlayerState)state{

    NSLog(@"[KYVedioPlayer] kyvedioPlayerReadyToPlay  准备播放");
}
//播放完毕的代理方法
-(void)kyplayerFinishedPlay:(KYVedioPlayer *)kyvedioPlayer{

    NSLog(@"[KYVedioPlayer] kyvedioPlayerReadyToPlay  播放完毕");
}

更多播放器方法

  • 重置播放器
[vedioPlayer resetKYVedioPlayer];
  • 设置全屏显示播放
/**
 *  全屏显示播放
 * @param interfaceOrientation 方向
 * @param player 当前播放器
 * @param fatherView 当前父视图
 **/
-(void)showFullScreenWithInterfaceOrientation:(UIInterfaceOrientation )interfaceOrientation player:(KYVedioPlayer *)player withFatherView:(UIView *)fatherView;
  • 设置小屏幕显示播放
/**
 *  小屏幕显示播放
 * @param player 当前播放器
 * @param fatherView 当前父视图
 * @param playerFrame 小屏幕的Frame
 **/
-(void)showSmallScreenWithPlayer:(KYVedioPlayer *)player withFatherView:(UIView *)fatherView withFrame:(CGRect )playerFrame;

高级功能演示DEMO

1.记住上次播放的位置

每次记录播放器注销的时候的该视频的时间点,当下次在播放该视频的时候,先判断一下是否记录了该视频的时间点,如果记录了,就从记录的时间点开始播放,若没有,正常播放即可。

整个例子 在 DEMO 的KYRememberLastPlayedVC.m 文件代码实现如下:

//
//  KYRememberLastPlayedVC.m
//  KYVedioPlayer
//
//  Created by kingly on 16/9/9.
//  Copyright © 2016年 https://github.com/kingly09/KYVedioPlayer kingly  inc . All rights reserved.
//

#import "KYRememberLastPlayedVC.h"

#define TheUserDefaults [NSUserDefaults standardUserDefaults]

@interface KYRememberLastPlayedVC ()<KYVedioPlayerDelegate>{
    KYVedioPlayer  *vedioPlayer;
    CGRect     playerFrame;
    NSString *URLString;
}

@end

@implementation KYRememberLastPlayedVC

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    playerFrame = CGRectMake(0, 0, kScreenWidth, (kScreenWidth)*(0.75));
    vedioPlayer = [[KYVedioPlayer alloc]initWithFrame:playerFrame];
    vedioPlayer.delegate = self;

    URLString = @"http://static.tripbe.com/videofiles/20121214/9533522808.f4v.mp4";
    if ([TheUserDefaults doubleForKey:URLString]) {//如果有存上次播放的时间点记录,直接跳到上次纪录时间点播放
        double time = [TheUserDefaults doubleForKey:URLString];
        vedioPlayer.seekTime = time;
    }
    [vedioPlayer setURLString:URLString];

    vedioPlayer.titleLabel.text = self.title;
    vedioPlayer.closeBtn.hidden = NO;
    vedioPlayer.progressColor = [UIColor orangeColor];
    [self.view addSubview:vedioPlayer];
    [vedioPlayer play];

}

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];

}
-(void)viewDidDisappear:(BOOL)animated{

    [super viewDidAppear:animated];
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

/**
 *  注销播放器
 **/
- (void)releasePlayer
{
    [vedioPlayer resetKYVedioPlayer];
    vedioPlayer = nil;
}

- (void)dealloc
{
    //记录播放的时间
    double time = [vedioPlayer currentTime];
    [TheUserDefaults setDouble:time forKey:URLString];

    [self releasePlayer];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    NSLog(@"KYRememberLastPlayedVC dealloc");
}

/**
 *  隐藏状态栏
 **/
-(BOOL)prefersStatusBarHidden{
    return YES;
}

#pragma mark - KYVedioPlayerDelegate 播放器委托方法
//点击播放暂停按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedPlayOrPauseButton:(UIButton *)playOrPauseBtn{

    NSLog(@"[KYVedioPlayer] clickedPlayOrPauseButton ");
}
//点击关闭按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedCloseButton:(UIButton *)closeBtn{

    NSLog(@"[KYVedioPlayer] clickedCloseButton ");

}
//点击全屏按钮代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer clickedFullScreenButton:(UIButton *)fullScreenBtn{
    NSLog(@"[KYVedioPlayer] clickedFullScreenButton ");

}
//单击WMPlayer的代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer singleTaped:(UITapGestureRecognizer *)singleTap{

    NSLog(@"[KYVedioPlayer] singleTaped ");
}
//双击WMPlayer的代理方法
-(void)kyvedioPlayer:(KYVedioPlayer *)kyvedioPlayer doubleTaped:(UITapGestureRecognizer *)doubleTap{

    NSLog(@"[KYVedioPlayer] doubleTaped ");
}

///播放状态
//播放失败的代理方法
-(void)kyvedioPlayerFailedPlay:(KYVedioPlayer *)kyvedioPlayer playerStatus:(KYVedioPlayerState)state{
    NSLog(@"[KYVedioPlayer] kyvedioPlayerFailedPlay  播放失败");
}
//准备播放的代理方法
-(void)kyvedioPlayerReadyToPlay:(KYVedioPlayer *)kyvedioPlayer playerStatus:(KYVedioPlayerState)state{

    NSLog(@"[KYVedioPlayer] kyvedioPlayerReadyToPlay  准备播放");
}
//播放完毕的代理方法
-(void)kyplayerFinishedPlay:(KYVedioPlayer *)kyvedioPlayer{

    NSLog(@"[KYVedioPlayer] kyvedioPlayerReadyToPlay  播放完毕");
}

@end

效果如下:

  • 每次记录播放器注销的时候的该视频的时间点,代码实现如下:
- (void)dealloc
{
    //记录播放的时间
    double time = [vedioPlayer currentTime];
    [TheUserDefaults setDouble:time forKey:URLString];

    [self releasePlayer];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    NSLog(@"KYRememberLastPlayedVC dealloc");
}
  • 如果有存上次播放的时间点记录,直接跳到上次纪录时间点播放,代码实现如下:
if ([TheUserDefaults doubleForKey:URLString]) {//如果有存上次播放的时间点记录,直接跳到上次纪录时间点播放
        double time = [TheUserDefaults doubleForKey:URLString];
        vedioPlayer.seekTime = time;
}

2.支持UITableViewCell中播放视频

支持UITableViewCell中播放视频,当滑动视图的时候后,切换到小窗口播放,当滑到当前的cell视图时候,回来cell视图中播放,可以自由滑动切换视频连续播放。
还可以随时点击切换横屏播放,小屏幕播放。

step 1 :首先创建一个 KYNetworkVideoCell视图

KYNetworkVideoCell.h

#import <UIKit/UIKit.h>
#import "KYVideo.h"

@protocol KYNetworkVideoCellDelegate;

@interface KYNetworkVideoCell : UITableViewCell

@property (nonatomic,weak) id<KYNetworkVideoCellDelegate>mydelegate;

+(NSString *) cellReuseIdentifier;

@property (nonatomic, strong)  UIImageView *vedioBg;
@property (nonatomic, strong)   UIButton *playBtn;
@property (nonatomic, strong) NSIndexPath *indexPath;
@property (nonatomic,strong) KYVideo *video;

@end

@protocol KYNetworkVideoCellDelegate <NSObject>

-(void)networkVideoCellVedioBgTapGesture:(KYVideo *)video;

-(void)networkVideoCellOnClickVideoPlay:(KYVideo *)video withVideoPlayBtn:(UIButton *)videoPlayBtn;

@end

KYNetworkVideoCell.m


#import "KYNetworkVideoCell.h"
#define kVerticalSpace 10

@interface KYNetworkVideoCell(){

    UILabel *title;

}
@end

@implementation KYNetworkVideoCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        // Initialization code

        [self addCellView];
    }
    return self;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (void)addCellView
{

        title = [[UILabel alloc]init];
        title.backgroundColor = [UIColor whiteColor];
        title.textAlignment = NSTextAlignmentLeft;
        title.textColor = [UIColor blackColor];
        title.font = [UIFont systemFontOfSize:16];
        title.numberOfLines = 0;
        title.contentMode= UIViewContentModeTop;
        [self.contentView  addSubview:title];

        _vedioBg= [[UIImageView alloc]init];
        _vedioBg.contentMode = UIViewContentModeScaleToFill;
        _vedioBg.userInteractionEnabled = YES;
        UITapGestureRecognizer *panGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(vedioBgTapGesture:)];
        _vedioBg.userInteractionEnabled = YES;
        [_vedioBg addGestureRecognizer:panGesture];
        [self.contentView  addSubview:_vedioBg];

        _playBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_playBtn setImage:[UIImage imageNamed:@"video_cover_play_nor"]  forState:UIControlStateNormal];
        [_playBtn adjustsImageWhenHighlighted];
        [_playBtn adjustsImageWhenDisabled];
        _playBtn.backgroundColor = [UIColor clearColor];
        _playBtn.imageView.contentMode = UIViewContentModeCenter;
        [_playBtn addTarget:self action:@selector(onClickVideoPlay:) forControlEvents:UIControlEventTouchUpInside];
        [self.contentView  addSubview:_playBtn];

}

/**
 * 设置数据模型展示视图
 */
-(void)setVideo:(KYVideo *)video
{
    if (_video != video ) {
        _video = nil;
        _video = video;

        title.text  = _video.title;
        title.frame = CGRectMake(kVerticalSpace, 0 , kScreenWidth - kVerticalSpace*2, 30);
        _vedioBg.frame =  CGRectMake(0, title.frame.size.height , kScreenWidth,200);
        [_vedioBg sd_setImageWithURL:[NSURL URLWithString:video.image] placeholderImage:[UIImage imageNamed:@"PlayerBackground"]];
        _playBtn.frame = CGRectMake((kScreenWidth - 72)/2, title.frame.size.height+ (_vedioBg.frame.size.height - 72)/2  , 72, 72);
        _video.curCellHeight = 230;

    }
}

+(NSString *) cellReuseIdentifier{

    return @"KKYNetworkVideoCell";
}

-(void)vedioBgTapGesture:(id)sender{

    if (_mydelegate && [_mydelegate respondsToSelector:@selector(networkVideoCellVedioBgTapGesture:)]) {
        [_mydelegate networkVideoCellVedioBgTapGesture:_video];
    }

}

-(void)onClickVideoPlay:(UIButton *)sender{

    _video.indexPath = _indexPath;
    if (_mydelegate && [_mydelegate respondsToSelector:@selector(networkVideoCellOnClickVideoPlay:withVideoPlayBtn:)]) {
        [_mydelegate networkVideoCellOnClickVideoPlay:_video withVideoPlayBtn:sender];
    }
}

@end

step 2 : 设置 KYNetworkVideoCellDelegateKYVedioPlayerDelegate 委托代理 如 demo里面的 KYSwitchFreelyVC.m 所示

@interface KYSwitchFreelyVC ()<UITableViewDelegate, UITableViewDataSource,KYNetworkVideoCellDelegate,KYVedioPlayerDelegate>
@property (nonatomic, strong) UITableView       *tableView;
@property (nonatomic, strong) NSMutableArray    *dataSource;

@end

@implementation KYSwitchFreelyVC{

    KYVedioPlayer *vedioPlayer;
    KYVideo *currentVideo;
    NSIndexPath *currentIndexPath;
    BOOL isSmallScreen;
}

step 3 : 给播放器加监听以及屏幕旋转的通知

- (void)viewWillAppear:(BOOL)animated{
    [super viewWillAppear:animated];
    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
    self.navigationController.navigationBarHidden = NO;
    //旋转屏幕通知
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(NotificationDeviceOrientationChange:)
                                                 name:UIDeviceOrientationDidChangeNotification
                                               object:nil
     ];
}

接收屏幕旋转的通知

#pragma mark - NotificationDeviceOrientationChange
-(void)NotificationDeviceOrientationChange:(NSNotification *)notification{

    if (vedioPlayer == nil|| vedioPlayer.superview==nil){
        return;
    }

    UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
    UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
    switch (interfaceOrientation) {
        case UIInterfaceOrientationPortraitUpsideDown:{
            NSLog(@"第3个旋转方向---电池栏在下");
        }
            break;
        case UIInterfaceOrientationPortrait:{
            NSLog(@"第0个旋转方向---电池栏在上");

            if (vedioPlayer.isFullscreen) {
                if (isSmallScreen) {
                    //放widow上,小屏显示
                    [self showSmallScreen];
                }else{
                    [self showCellCurrentVedioPlayer];
                }
            }

        }
            break;
        case UIInterfaceOrientationLandscapeLeft:{
            NSLog(@"第2个旋转方向---电池栏在左");
            vedioPlayer.isFullscreen = YES;
            [self setNeedsStatusBarAppearanceUpdate];
            [vedioPlayer showFullScreenWithInterfaceOrientation:interfaceOrientation player:vedioPlayer withFatherView:self.view];
        }
            break;
        case UIInterfaceOrientationLandscapeRight:{
            NSLog(@"第1个旋转方向---电池栏在右");
            vedioPlayer.isFullscreen = YES;
            [self setNeedsStatusBarAppearanceUpdate];
            [vedioPlayer showFullScreenWithInterfaceOrientation:interfaceOrientation player:vedioPlayer withFatherView:self.view];
        }
            break;
        default:
            break;
    }

}

step 4 : 加载数据,显示视频列表,使用 MJRefresh实现下拉刷新的效果

-(void)loadDataList{
    [self addProgressHUDWithMessage:@"加载中..."];
    [self getVideoListWithURLString:@"http://c.m.163.com/nc/video/home/0-10.html"
                            success:^( NSArray *videoArray) {
                                _dataSource =[NSMutableArray arrayWithArray:videoArray];
                                dispatch_async(dispatch_get_main_queue(), ^{
                                    [self removeProgressHUD];
                                    [self.tableView reloadData];
                                    [self.tableView.mj_header endRefreshing];
                                });
                            }
                             failed:^(NSError *error) {
                                 [self removeProgressHUD];
                             }];

}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)addMJRefresh{
    WS(weakSelf)
    __unsafe_unretained UITableView *tableView = self.tableView;
    tableView.mj_header= [MJRefreshNormalHeader headerWithRefreshingBlock:^{
         [weakSelf addProgressHUDWithMessage:@"加载中..."];
        [self getVideoListWithURLString:@"http://c.m.163.com/nc/video/home/0-10.html"
                                success:^( NSArray *videoArray) {
                                    _dataSource =[NSMutableArray arrayWithArray:videoArray];
                                    dispatch_async(dispatch_get_main_queue(), ^{
                                        if (currentIndexPath.row> _dataSource.count) {
                                            [weakSelf releasePlayer];
                                        }
                                        [weakSelf removeProgressHUD];
                                        [tableView reloadData];
                                        [tableView.mj_header endRefreshing];
                                    });
                                }
                                 failed:^(NSError *error) {

                                     [weakSelf removeProgressHUD];
                                 }];

    }];

    // 设置自动切换透明度(在导航栏下面自动隐藏)
    tableView.mj_header.automaticallyChangeAlpha = YES;
    // 上拉刷新
    tableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
        NSString *URLString = [NSString stringWithFormat:@"http://c.m.163.com/nc/video/home/%ld-10.html",_dataSource.count - _dataSource.count%10];
        [weakSelf addProgressHUDWithMessage:@"加载中..."];
        [self getVideoListWithURLString:URLString
                                success:^(NSArray *videoArray) {
                                    [_dataSource addObjectsFromArray:videoArray];
                                    dispatch_async(dispatch_get_main_queue(), ^{
                                        [weakSelf removeProgressHUD];
                                        [tableView reloadData];
                                        [tableView.mj_header endRefreshing];
                                    });

                                }
                                 failed:^(NSError *error) {

                                     [weakSelf removeProgressHUD];
                                 }];
        // 结束刷新
        [tableView.mj_footer endRefreshing];
    }];

}

step 5 : 实现tableView,点击某个视频push到KYLocalVideoPlayVC界面播放

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    KYNetworkVideoCell *cell = [tableView dequeueReusableCellWithIdentifier:[KYNetworkVideoCell cellReuseIdentifier]];
    if (nil==cell)
    {
        cell = [[KYNetworkVideoCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:[KYNetworkVideoCell cellReuseIdentifier]];

    }
    KYVideo *kYVideo  = self.dataSource[indexPath.row];

    cell.selectionStyle = UITableViewCellSelectionStyleNone;
    cell.indexPath = indexPath;
    cell.video = kYVideo;
    cell.mydelegate = self;
    cell.playBtn.tag = indexPath.row;

    if (vedioPlayer && vedioPlayer.superview) {
        if (indexPath.row == currentIndexPath.row) {
            [cell.playBtn.superview sendSubviewToBack:cell.playBtn];    //隐藏播放按钮
        }else{
            [cell.playBtn.superview bringSubviewToFront:cell.playBtn];  //显示播放按钮
        }
        NSArray *indexpaths = [tableView indexPathsForVisibleRows];
        if (![indexpaths containsObject:currentIndexPath] && currentIndexPath!=nil) { //复用机制

            if ([[UIApplication sharedApplication].keyWindow.subviews containsObject:vedioPlayer]) {
                vedioPlayer.hidden = NO;
            }else{
                vedioPlayer.hidden = YES;
                [cell.playBtn.superview bringSubviewToFront:cell.playBtn];
            }
        }else{
            if ([cell.vedioBg.subviews containsObject:vedioPlayer]) {  //当滑倒所属当前视频的时候自动播放
                [cell.vedioBg addSubview:vedioPlayer];
                [vedioPlayer play];
                vedioPlayer.hidden = NO;
            }

        }
    }

    return cell;
}

#pragma mark - UITableViewDelegate

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {

    if (self.dataSource.count > 0) {
        KYVideo *kYVideo  = self.dataSource[indexPath.row];
        return kYVideo.curCellHeight;
    }
    return 0;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 0.1;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
    return 0.1;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

    KYLocalVideoPlayVC *localVideoPlayVC = [[KYLocalVideoPlayVC alloc] init];
    KYVideo *kYVideo  = self.dataSource[indexPath.row];
    localVideoPlayVC.title = kYVideo.title;
    localVideoPlayVC.URLString = kYVideo.video;
    UIBarButtonItem *backItem = [[UIBarButtonItem alloc] init];
    backItem.title = @"返回";
    self.navigationItem.backBarButtonItem = backItem;
    [self.navigationController pushViewController:localVideoPlayVC animated:YES];

}

step 6 : 从全屏来当前的cell视频

-(void)showCellCurrentVedioPlayer{

    if (currentVideo != nil &&  currentIndexPath != nil) {

        KYNetworkVideoCell *currentCell = [self currentCell];
        [vedioPlayer removeFromSuperview];

        [UIView animateWithDuration:0.5f animations:^{
            vedioPlayer.transform = CGAffineTransformIdentity;
            vedioPlayer.frame = currentCell.vedioBg.bounds;
            vedioPlayer.playerLayer.frame =  vedioPlayer.bounds;
            [currentCell.vedioBg addSubview:vedioPlayer];
            [currentCell.vedioBg bringSubviewToFront:vedioPlayer];
            [vedioPlayer.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(vedioPlayer).with.offset(0);
                make.right.equalTo(vedioPlayer).with.offset(0);
                make.height.mas_equalTo(40);
                make.bottom.equalTo(vedioPlayer).with.offset(0);
            }];
            [vedioPlayer.topView mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(vedioPlayer).with.offset(0);
                make.right.equalTo(vedioPlayer).with.offset(0);
                make.height.mas_equalTo(40);
                make.top.equalTo(vedioPlayer).with.offset(0);
            }];
            [vedioPlayer.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(vedioPlayer.topView).with.offset(45);
                make.right.equalTo(vedioPlayer.topView).with.offset(-45);
                make.center.equalTo(vedioPlayer.topView);
                make.top.equalTo(vedioPlayer.topView).with.offset(0);
            }];
            [vedioPlayer.closeBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(vedioPlayer).with.offset(5);
                make.height.mas_equalTo(30);
                make.width.mas_equalTo(30);
                make.top.equalTo(vedioPlayer).with.offset(5);
            }];
            [vedioPlayer.loadFailedLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
                make.center.equalTo(vedioPlayer);
                make.width.equalTo(vedioPlayer);
                make.height.equalTo(@30);
            }];
        }completion:^(BOOL finished) {
            vedioPlayer.isFullscreen = NO;
            [self setNeedsStatusBarAppearanceUpdate];
            isSmallScreen = NO;
            vedioPlayer.fullScreenBtn.selected = NO;

        }];
    }
}

step 7 : 显示小窗口视频

实际是删除vedioPlayer,然后放在keyWindow上

-(void)showSmallScreen{

    //放widow上
    [vedioPlayer removeFromSuperview];
    [UIView animateWithDuration:0.5f animations:^{
        vedioPlayer.transform = CGAffineTransformIdentity;
        vedioPlayer.frame = CGRectMake(kScreenWidth/2,kScreenHeight-kNavbarHeight-(kScreenWidth/2)*0.75, kScreenWidth/2, (kScreenWidth/2)*0.75);
        vedioPlayer.playerLayer.frame =  vedioPlayer.bounds;
        [[UIApplication sharedApplication].keyWindow addSubview:vedioPlayer];
        [vedioPlayer.bottomView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(vedioPlayer).with.offset(0);
            make.right.equalTo(vedioPlayer).with.offset(0);
            make.height.mas_equalTo(40);
            make.bottom.equalTo(vedioPlayer).with.offset(0);
        }];
        [vedioPlayer.topView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(vedioPlayer).with.offset(0);
            make.right.equalTo(vedioPlayer).with.offset(0);
            make.height.mas_equalTo(40);
            make.top.equalTo(vedioPlayer).with.offset(0);
        }];
        [vedioPlayer.titleLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(vedioPlayer.topView).with.offset(45);
            make.right.equalTo(vedioPlayer.topView).with.offset(-45);
            make.center.equalTo(vedioPlayer.topView);
            make.top.equalTo(vedioPlayer.topView).with.offset(0);
        }];
        [vedioPlayer.closeBtn mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(vedioPlayer).with.offset(5);
            make.height.mas_equalTo(30);
            make.width.mas_equalTo(30);
            make.top.equalTo(vedioPlayer).with.offset(5);

        }];
        [vedioPlayer.loadFailedLabel mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.center.equalTo(vedioPlayer);
            make.width.equalTo(vedioPlayer);
            make.height.equalTo(@30);
        }];

    }completion:^(BOOL finished) {
        vedioPlayer.isFullscreen = NO;
        [self setNeedsStatusBarAppearanceUpdate];
        vedioPlayer.fullScreenBtn.selected = NO;
        isSmallScreen = YES;
        [[UIApplication sharedApplication].keyWindow bringSubviewToFront:vedioPlayer];
    }];

}

step 8 : 点击cell视图上的播放按钮,在UITableViewCell中播放视频

cell播放:Layer是加载到cell上的背景图片区域的 滚动的时候要记录当前cell
全屏播放:Layer是加载到Window上的 frame全屏
小窗播放:它其实就是全屏播放的一个特例,也是加载到Window上的,frame自定义
其实不同状态的切换无非就是Layer所在View的位置不停切换

下面这个方法就是记录当前播放的cell下标

-(void)networkVideoCellOnClickVideoPlay:(KYVideo *)video withVideoPlayBtn:(UIButton *)videoPlayBtn;{

    [self closeCurrentCellVedioPlayer];

    currentVideo = video;
    currentIndexPath = [NSIndexPath indexPathForRow:videoPlayBtn.tag inSection:0];
    KYNetworkVideoCell *cell =nil;
    if ([UIDevice currentDevice].systemVersion.floatValue>=8||[UIDevice currentDevice].systemVersion.floatValue<7) {
        cell = (KYNetworkVideoCell *)videoPlayBtn.superview.superview;

    }else{//ios7系统 UITableViewCell上多了一个层级UITableViewCellScrollView
        cell = (KYNetworkVideoCell *)videoPlayBtn.superview.superview.subviews;

    }

    if (isSmallScreen) {
        [self releasePlayer];
        isSmallScreen = NO;
    }

    if (vedioPlayer) {
        [self releasePlayer];
        vedioPlayer = [[KYVedioPlayer alloc]initWithFrame:cell.vedioBg.bounds];
        vedioPlayer.delegate = self;
        vedioPlayer.closeBtnStyle = CloseBtnStyleClose;
        vedioPlayer.titleLabel.text = video.title;
        vedioPlayer.URLString = video.video;
    }else{

        vedioPlayer = [[KYVedioPlayer alloc]initWithFrame:cell.vedioBg.bounds];
        vedioPlayer.delegate = self;
        vedioPlayer.closeBtnStyle = CloseBtnStyleClose;
        vedioPlayer.titleLabel.text = video.title;
        vedioPlayer.URLString = video.video;
    }

    [cell.vedioBg addSubview:vedioPlayer];
    [cell.vedioBg bringSubviewToFront:vedioPlayer];
    [cell.playBtn.superview sendSubviewToBack:cell.playBtn];
    [self.tableView reloadData];

}

step 9 : 设置上下滚动的时候根据坐标切换cell显示还是小窗显示

#pragma mark -  scrollView delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if(scrollView ==self.tableView){
        if (vedioPlayer==nil) {
            return;
        }

        if (vedioPlayer.superview) {
            CGRect rectInTableView = [self.tableView rectForRowAtIndexPath:currentIndexPath];
            CGRect rectInSuperview = [self.tableView convertRect:rectInTableView toView:[self.tableView superview]];
            if (rectInSuperview.origin.y<-self.currentCell.vedioBg.frame.size.height||rectInSuperview.origin.y>kScreenHeight-kNavbarHeight-kTabBarHeight) {//往上拖动

                if ([[UIApplication sharedApplication].keyWindow.subviews containsObject:vedioPlayer]&&isSmallScreen) {
                    isSmallScreen = YES;
                }else{
                    //放widow上,小屏显示
                    [self showSmallScreen];
                }

            }else{
                if ([self.currentCell.vedioBg.subviews containsObject:vedioPlayer]) {

                }else{
                    [self showCellCurrentVedioPlayer];
                }
            }
        }

    }
}

step 10 : 当滑倒所属当前视频的时候自动播放,切换的时候就是把只之前的Layer移除,然后重新布局,加到KeyWindow中去,代码实现如下:

if (vedioPlayer && vedioPlayer.superview) {
    if (indexPath.row == currentIndexPath.row) {
        [cell.playBtn.superview sendSubviewToBack:cell.playBtn];    //隐藏播放按钮
    }else{
        [cell.playBtn.superview bringSubviewToFront:cell.playBtn];  //显示播放按钮
    }
    NSArray *indexpaths = [tableView indexPathsForVisibleRows];
    if (![indexpaths containsObject:currentIndexPath] && currentIndexPath!=nil) { //复用机制

        if ([[UIApplication sharedApplication].keyWindow.subviews containsObject:vedioPlayer]) {
            vedioPlayer.hidden = NO;
        }else{
            vedioPlayer.hidden = YES;
            [cell.playBtn.superview bringSubviewToFront:cell.playBtn];
        }
    }else{
        if ([cell.vedioBg.subviews containsObject:vedioPlayer]) {  //当滑倒所属当前视频的时候自动播放
            [cell.vedioBg addSubview:vedioPlayer];
            [vedioPlayer play];
            vedioPlayer.hidden = NO;
        }

    }
}

step 11 : 关闭当前cell 中的 视频,直接vedioPlayer 移除子视图即可。

/**
 * 关闭当前cell 中的 视频
 **/
-(void)closeCurrentCellVedioPlayer{

    if (currentVideo != nil &&  currentIndexPath != nil) {

        KYNetworkVideoCell *currentCell = [self currentCell];
        [currentCell.playBtn.superview bringSubviewToFront:currentCell.playBtn];
        [vedioPlayer removeFromSuperview];
        [self setNeedsStatusBarAppearanceUpdate];
    }
}

联系与建议反馈

weibo: http://weibo.com/balenn

QQ: 362108564

如果有任何你觉得不对的地方,或有更好的建议,以上联系都可以联系我。 十分感谢!

感谢

KYVedioPlayer播放器的布局依赖Masonry框架,十分感谢Masonry开发人员对开源事业作出的贡献!

鼓励

它若不慎给您帮助,请不吝啬给它点一个star,是对它的最好支持,非常感谢!🙏

LICENSE

KYVedioPlayer 被许可在 MIT 协议下使用。查阅 LICENSE 文件来获得更多信息。

Latest podspec

{
    "name": "KYVedioPlayer",
    "summary": "KYVedioPlayer u662fu57fau4e8eAVPlayeru7684u5c01u88c5u89c6u9891u64adu653eu5668,u652fu6301u64adu653emp4u3001m3u8u30013gpu3001movu7b49u683cu5f0fuff1bu652fu6301u7f51u7edcu89c6u9891u548cu672cu5730u89c6u9891u64adu653euff1bu652fu6301u5168u5c4fu548cu5c0fu5c4fu5e55u64adu653euff1bu8fd8u5728UITableViewCellu4e2du64adu653eu89c6u9891 uff1bu652fu6301u6a2au5c4fu7ad6u5c4fu81eau52a8u64adu653e",
    "version": "0.0.5",
    "homepage": "https://github.com/kingly09/KYVedioPlayer",
    "license": {
        "type": "MIT",
        "file": "LICENSE"
    },
    "authors": {
        "kingly": "[email protected]"
    },
    "platforms": {
        "ios": "7.0"
    },
    "source": {
        "git": "https://github.com/kingly09/KYVedioPlayer.git",
        "tag": "0.0.5"
    },
    "social_media_url": "https://github.com/kingly09",
    "source_files": "KYVedioPlayerLib/*.{h,m}",
    "resources": "KYVedioPlayerLib/KYVedioPlayer.bundle",
    "frameworks": [
        "MediaPlayer",
        "AVFoundation",
        "UIKit"
    ],
    "dependencies": {
        "Masonry": [
            "~> 1.0.1"
        ]
    },
    "requires_arc": true
}

Pin It on Pinterest

Share This