//
// MZPlayView.m
// 视频播放器常规功能
//
// Created by yubo on 16/7/27.
//
#import "MZPlayView.h"
#import
#import
#import
/**
* Description
关于视频这一块的业务操作全部封装在这个里面。 对外就提供一个NSUrl属性。
*/
typedefNS_ENUM(NSInteger,PanDirection){
PanDirectionHorizontalMoved,//横向移动
PanDirectionVerticalMoved//纵向移动
};
@interfaceMZPlayView()
@property(nonatomic,strong)AVPlayer*player;
@property(nonatomic,strong)AVPlayerItem*playerItem;
@property(nonatomic,strong)AVPlayerLayer*playerLayer;
@property(weak,nonatomic) IBOutletUIButton*backBtn;
@property(weak,nonatomic) IBOutletUILabel*forwardLabel;
@property(weak,nonatomic) IBOutletUILabel*currentTimeLabel;
@property(weak,nonatomic) IBOutletUIProgressView*progressView;
@property(weak,nonatomic) IBOutletUILabel*totalTimeLabel;
@property(weak,nonatomic) IBOutletUIButton*fullScreanBtn;
@property(weak,nonatomic) IBOutletUISlider*sliderView;
@property(weak,nonatomic) IBOutletUIButton*startBtn;
@property(weak,nonatomic) IBOutletUIButton*lockBtn;
@property(nonatomic,assign)boolplayViewFlag;// 隐藏和显示标识
@property(nonatomic,strong)NSTimer*timer;
@property(nonatomic,assign)boollockFlag;// 锁标识
@property(nonatomic,assign) PanDirection panDirection;
@property(nonatomic, assign)BOOLisVolume;// 是否调节音量
@property(nonatomic,strong)UISlider*volumeViewSlider;
@property(nonatomic, assign) CGFloat sumTime;// 用来保存的总时长
@end
@implementationMZPlayView
- (void)awakeFromNib{
[superawakeFromNib];
[_sliderViewsetThumbImage:[UIImageimageNamed:@"slider"]forState:UIControlStateNormal];
[selfinsertSubview:_progressViewbelowSubview:_sliderView];
// 滑块左边的颜色
_sliderView.minimumTrackTintColor= [UIColororangeColor];
// 滑块右边的颜色
_sliderView.maximumTrackTintColor= [UIColorclearColor];
}
- (void)setUrl:(NSURL*)url{
_playerItem = [AVPlayerItemplayerItemWithURL:url];
_player = [AVPlayerplayerWithPlayerItem:_playerItem];
_playerLayer = [AVPlayerLayerplayerLayerWithPlayer:_player];
[self.layerinsertSublayer:_playerLayeratIndex:0];
[_playerplay];
_forwardLabel.hidden=YES;
// 添加播完通知
[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(moviePlayDidEnd:)name:AVPlayerItemDidPlayToEndTimeNotificationobject:_player.currentItem];
// 添加手势
[selfcreateGesture];
//获取系统音量
[selfconfigureVolume];
// 添加事件
[selfsliderAddEvent];
// 通过定时器获取 时间,
_timer = [NSTimerscheduledTimerWithTimeInterval:1.0ftarget:selfselector:@selector(playerTimerAction)userInfo:nilrepeats:YES];
// 计算缓冲
[_playerItemaddObserver:selfforKeyPath:@"loadedTimeRanges"options:NSKeyValueObservingOptionNewcontext:nil];
[_playerItemaddObserver:selfforKeyPath:@"status"options:NSKeyValueObservingOptionNewcontext:nil];
}
#pragma mark 滑动事件添加
- (void)sliderAddEvent{
// slider开始滑动事件
[_sliderViewaddTarget:selfaction:@selector(progressSliderTouchBegan:)forControlEvents:UIControlEventTouchDown];
// slider滑动中事件
[_sliderViewaddTarget:selfaction:@selector(progressSliderValueChanged:)forControlEvents:UIControlEventValueChanged];
// slider结束滑动事件
[_sliderViewaddTarget:selfaction:@selector(progressSliderTouchEnded:)forControlEvents:UIControlEventTouchUpInside];
}
// 开始滑动
- (void)progressSliderTouchBegan:(UISlider*)slider{
// 取消延迟
// [NSObject cancelPreviousPerformRequestsWithTarget:self];
}
// 开始变化
- (void)progressSliderValueChanged:(UISlider*)slider{
// 拖动改变视频播放进度
if(_player.status== AVPlayerItemStatusReadyToPlay) {
// 总秒数
CGFloat total = (CGFloat)_playerItem.duration.value/ _playerItem.duration.timescale;
// 已经拖动到了现在的秒数了。
NSInteger dragedSeconds = floorf(total* slider.value);
//转换成CMTime才能给player来控制播放进度---这个类用来控制时间的。
CMTime dragedCMTime = CMTimeMake(dragedSeconds,1);
[_playerpause];
[_playerseekToTime:dragedCMTimecompletionHandler:^(BOOLfinished) {
[_playerplay];
}];
}
}
#pragma mark slider事件结束
- (void)progressSliderTouchEnded:(UISlider*)slider{
}
#pragma mark 重新布局 frame
- (void)layoutSubviews{
_playerLayer.frame=self.bounds;
};
#pragma mark 添加手势, 点击隐藏视频所有按钮
- (void)createGesture{
UITapGestureRecognizer*tap = [[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(playViewHanlder:)];
tap.numberOfTapsRequired=1;
[selfaddGestureRecognizer:tap];
}
#pragma mark 手势回调处理
- (void)playViewHanlder:(UITapGestureRecognizer*)recognizer{
if(!_lockFlag) {
if(recognizer.state== UIGestureRecognizerStateRecognized) {
if(!_playViewFlag) {
[selfhiddlePlayView];
}else{
[selfshowPlayView];
}
}
}
}
#pragma mark 隐藏组件
- (void)hiddlePlayView{
[UIViewanimateWithDuration:0.5animations:^{
_backBtn.alpha=0;
_currentTimeLabel.alpha=0;
_progressView.alpha=0;
_totalTimeLabel.alpha=0;
_fullScreanBtn.alpha=0;
_sliderView.alpha=0;
_startBtn.alpha=0;
}completion:^(BOOLfinished) {
_playViewFlag = !_playViewFlag;
}];
}
#pragma mark 显示组件
- (void)showPlayView{
[UIViewanimateWithDuration:0.5animations:^{
_backBtn.alpha=1;
_currentTimeLabel.alpha=1;
_progressView.alpha=1;
_totalTimeLabel.alpha=1;
_fullScreanBtn.alpha=1;
_sliderView.alpha=1;
_startBtn.alpha=1;
}completion:^(BOOLfinished) {
_playViewFlag = !_playViewFlag;
}];
}
#pragma mark 播放完后---通知处理
- (void)moviePlayDidEnd:(NSNotification*)notification{
}
#pragma mark 播放或者暂停
- (IBAction)startAction:(id)sender {
UIButton*btn = (UIButton*)sender;
if(btn.selected) {
[_playerplay];
}else{
[_playerpause];
}
btn.selected= !btn.selected;
}
#pragma mark 全屏按钮事件
- (IBAction)fullScreanAction:(id)sender {
UIDeviceOrientation orientation = [UIDevicecurrentDevice].orientation;
UIInterfaceOrientation interfaceOrientation = (UIInterfaceOrientation)orientation;
switch(interfaceOrientation) {
caseUIInterfaceOrientationPortraitUpsideDown:// 电池栏在下
[selfinterfaceOrientation:UIInterfaceOrientationLandscapeRight];
break;
caseUIInterfaceOrientationPortrait:// 电池栏在上
[selfinterfaceOrientation:UIInterfaceOrientationLandscapeRight];
break;
caseUIInterfaceOrientationLandscapeLeft:// 电池栏在右边
[selfinterfaceOrientation:UIInterfaceOrientationPortrait];
break;
caseUIInterfaceOrientationLandscapeRight:// 电池栏左边
[selfinterfaceOrientation:UIInterfaceOrientationPortrait];
break;
default:
break;
}
}
#pragma mark 强制转屏相关
- (void)interfaceOrientation:(UIInterfaceOrientation)orientation{
// arc下
if([[UIDevicecurrentDevice]respondsToSelector:@selector(setOrientation:)]) {
SELselector = NSSelectorFromString(@"setOrientation:");
NSInvocation*invocation = [NSInvocationinvocationWithMethodSignature:[UIDeviceinstanceMethodSignatureForSelector:selector]];
[invocationsetSelector:selector];
[invocationsetTarget:[UIDevicecurrentDevice]];
intval = orientation;
[invocationsetArgument:&valatIndex:2];
[invocationinvoke];
}
}
#pragma mark 通过定时器获取视频的时间问题
- (void)playerTimerAction{
if(_playerItem.duration.timescale!=0) {
_sliderView.maximumValue=1;//音乐总共时长
_sliderView.value= CMTimeGetSeconds([_playerItemcurrentTime]) / (_playerItem.duration.value/ _playerItem.duration.timescale);//当前进度
//duration 总时长
NSInteger durMin = (NSInteger)_playerItem.duration.value/ _playerItem.duration.timescale;//总秒
NSInteger durSec = (NSInteger)_playerItem.duration.value/ _playerItem.duration.timescale/60;//总分钟
_totalTimeLabel.text= [NSStringstringWithFormat:@"%02ld:%02ld",durSec,durMin];
//当前时长进度progress
NSInteger proMin = (NSInteger)CMTimeGetSeconds([_playerItemcurrentTime]);//当前秒
NSInteger proSec = (NSInteger)CMTimeGetSeconds([_playerItemcurrentTime]) /60;//当前分
_currentTimeLabel.text= [NSStringstringWithFormat:@"%02ld:%02ld", proSec,proMin];
}
}
#pragma mark 计算出总时间
- (void)observeValueForKeyPath:(NSString*)keyPathofObject:(id)objectchange:(NSDictionary *)changecontext:(voidvoid*)context{
if(object == _playerItem) {
if([keyPathisEqualToString:@"status"]) {// 这里代表添加完成
if(_playerItem.status== AVPlayerItemStatusReadyToPlay) {
// 添加滑动手势
UIPanGestureRecognizer*pan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(panDirection:)];
[selfaddGestureRecognizer:pan];
}
}elseif([keyPathisEqualToString:@"loadedTimeRanges"]){
NSTimeInterval timeInterval = [selfavailableDuration];// 计算缓冲进度
CMTime duration = _playerItem.duration;
CGFloat totalDuration = CMTimeGetSeconds(duration);
[_progressViewsetProgress:timeInterval / totalDurationanimated:NO];
}
}
}
#pragma mark 计算缓存
- (NSTimeInterval)availableDuration {
NSArray*loadedTimeRanges = [[_playercurrentItem]loadedTimeRanges];
CMTimeRange timeRange = [loadedTimeRanges.firstObjectCMTimeRangeValue];// 获取缓冲区域
floatstartSeconds = CMTimeGetSeconds(timeRange.start);
floatdurationSeconds = CMTimeGetSeconds(timeRange.duration);
NSTimeInterval result = startSeconds + durationSeconds;// 计算缓冲总进度
returnresult;
}
#pragma mark 锁屏
- (IBAction)lockAction:(id)sender {
UIButton*btn = (UIButton*)sender;
btn.selected= !btn.selected;
_lockFlag = btn.selected;
if(btn.selected) {
[btnsetImage:[UIImageimageNamed:@"lock-nor"]forState:UIControlStateNormal];
}else{
[btnsetImage:[UIImageimageNamed:@"unlock-nor"]forState:UIControlStateNormal];
}
}
#pragma mark 平移手势
- (void)panDirection:(UIPanGestureRecognizer*)pan{
// 根据在Uiview的位置,调节音量或者亮度
CGPoint locationPoint = [panlocationInView:self];
NSLog(@"当前点的位置--->%@",NSStringFromCGPoint(locationPoint));
// 拖动的速度---->返回值就是你拖动时X和Y轴上的速度,速度是矢量,有方向
CGPoint veloctyPoint = [panvelocityInView:self];
NSLog(@"拖动的速度--->%@",NSStringFromCGPoint(veloctyPoint));
// 判断是垂直移动还是水平移动
switch(pan.state) {
caseUIGestureRecognizerStateBegan:{// 开始移动
CGFloat x = fabs(veloctyPoint.x);
CGFloat y = fabs(veloctyPoint.y);
if(x > y) {// 水平移动
_panDirection = PanDirectionHorizontalMoved;
CMTime time = _player.currentTime;
_sumTime = time.value/time.timescale;
NSLog(@"===%f",_sumTime);
}elseif(x < y){// 垂直移动
_panDirection = PanDirectionVerticalMoved;
if(locationPoint.x>self.bounds.size.width/2) {// 右边是控制音量
_isVolume =true;
}else{// 左边是控制亮度
_isVolume =false;
}
}
}
break;
caseUIGestureRecognizerStateChanged:{// 正在移动
if(self.panDirection== PanDirectionHorizontalMoved) {// 水平
[selfhorizontalMoved:veloctyPoint.x];// 水平移动的方法只要x方向的值
}else{// 垂直
[selfverticalMoved:veloctyPoint.y];// 垂直移动方法只要y方向的值
}
}
break;
caseUIGestureRecognizerStateEnded:{
NSLog(@"移动停止l......");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(11* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 隐藏视图
_forwardLabel.hidden=YES;
});
if(self.panDirection== PanDirectionHorizontalMoved) {
//转换成CMTime才能给player来控制播放进度
CMTime dragedCMTime = CMTimeMake(self.sumTime,1);
[_playerpause];
[_playerseekToTime:dragedCMTimecompletionHandler:^(BOOLfinish){
[_playerplay];
}];
// 把sumTime滞空,不然会越加越多
self.sumTime=0;
}else{
}
}
break;
default:
break;
}
}
#pragma mark 垂直移动处理。
- (void)verticalMoved:(CGFloat)value{
if(_isVolume) {// 控制音量
// 更改系统的音量
self.volumeViewSlider.value-= value /10000;// 越小幅度越小
// 这个就对手机有效果, 最大的值为 1 。
NSLog(@"声音----->%lf",self.volumeViewSlider.value);
}else{// 调节亮度
[UIScreenmainScreen].brightness-= value /10000;
NSString*brightness = [NSStringstringWithFormat:@"亮度%.0f%%",[UIScreenmainScreen].brightness/1.0*100];
_forwardLabel.hidden=NO;
_forwardLabel.text= brightness;
}
}
//获取系统音量
- (void)configureVolume
{
// 系统声音控制
MPVolumeView*volumeView = [[MPVolumeViewalloc]init];
_volumeViewSlider =nil;
for(UIView*view in [volumeViewsubviews]){
if([view.class.descriptionisEqualToString:@"MPVolumeSlider"]){
_volumeViewSlider = (UISlider*)view;
break;
}
}
// 使用这个category的应用不会随着手机静音键打开而静音,可在手机静音下播放声音
NSError*setCategoryError =nil;
BOOLsuccess = [[AVAudioSessionsharedInstance]
setCategory: AVAudioSessionCategoryPlayback
error: &setCategoryError];
if(!success) {/* handle the error in setCategoryError */}
}
#pragma mark - pan水平移动的方法
- (void)horizontalMoved:(CGFloat)value{
// 快进快退的方法
NSString*style =@"";
if(value <0) {
style =@"<<";
}
elseif(value >0){
style =@">>";
}
// 每次滑动需要叠加时间
self.sumTime+= value /200;
// 需要限定sumTime的范围
CMTime totalTime =self.playerItem.duration;
CGFloat totalMovieDuration = (CGFloat)totalTime.value/totalTime.timescale;
if(self.sumTime> totalMovieDuration) {
self.sumTime= totalMovieDuration;
}elseif(self.sumTime<0){
self.sumTime=0;
}
// 当前快进的时间
NSString*nowTime = [selfdurationStringWithTime:(int)self.sumTime];
// 总时间
NSString*durationTime = [selfdurationStringWithTime:(int)totalMovieDuration];
// 给label赋值
_forwardLabel.text= [NSStringstringWithFormat:@"%@ %@ / %@",style, nowTime,durationTime];
_forwardLabel.hidden=NO;
}
#pragma mark - 根据时长求出字符串
- (NSString*)durationStringWithTime:(int)time
{
// 获取分钟
NSString*min = [NSStringstringWithFormat:@"%02d",time /60];
// 获取秒数
NSString*sec = [NSStringstringWithFormat:@"%02d",time %60];
return[NSStringstringWithFormat:@"%@:%@", min,sec];
}
@end
视图只设置了 6+ 的适配, 其他的没做处理。
调用
self.view.backgroundColor = [UIColor blackColor];
[objc]view plaincopy
NSURL*url = [NSURLURLWithString:@"http://baobab.wdjcdn.com/14562919706254.mp4"];
NSURL*url = [[NSBundlemainBundle]URLForResource:@"minion_01"withExtension:@"mp4"];
[_playViewsetUrl:url];
效果:
实现了, 暂停, 拖放,声音,亮度, 锁屏。 这一些基本简单操作。