```
主要用到的控件有 AVPlayer
该控件的初始化方法有许多种
- (instancetype)initWithURL:(NSURL *)URL
+ (instancetype)playerWithURL:(NSURL *)URL
- (instancetype)initWithPlayerItem:(AVPlayerItem *)item
+ (instancetype)playerWithPlayerItem:(AVPlayerItem *)item(最常用)
实现思路:
此处,我们将AVPlayer封装在一个单例的工具类中,开放了这两个接口.
+ (instancetype)sharePlayer;
- (void)playWithUrl:(NSURL *)url showView:(UIView *)showView;
1.随意传入一个播放的音频网址,便可以对其进行播放,相关的布局,还可以通过属性在外层VC内进行赋值.
2.上下曲的逻辑部分通过通知中心传到外层VC中,外层VC接受两部分内容,"点击的音频网址存入到一个数组当中"以及"点击当前音频的下标index",当前的下标是判断上下曲的关键.
.h
//// PlayerSingle.h// 喜马拉雅FM//// Created by dllo on 16/3/7.// Copyright © 2016年 lanou.com. All rights reserved.//#import#import#import@interface PlayerSingle : NSObject
// 视图层面.
@property (nonatomic, retain) UIImageView *imageBackGroundView;
@property (nonatomic, retain) UILabel *labelForTitle;
@property (nonatomic, retain) UIImageView *imageCoverLarge;
// 功能层面.
@property (nonatomic, retain) UIView *visualView;
@property (nonatomic, retain) AVPlayer *player;
@property (nonatomic, retain) AVPlayerItem *playerItem;
@property (nonatomic, retain) UIButton *buttonPlayOrPause;
@property (nonatomic, retain) UIButton *buttonRight;
@property (nonatomic, retain) UIButton *buttonLeft;
@property (nonatomic, retain) UIProgressView *progress;
+ (instancetype)sharePlayer;
- (void)playWithUrl:(NSURL *)url showView:(UIView *)showView;
@end
.m
//// PlayerSingle.m// 喜马拉雅FM//// Created by dllo on 16/3/7.// Copyright © 2016年 lanou.com. All rights reserved.//#define WIDTH [UIScreen mainScreen].bounds.size.width#define HEIGHT [UIScreen mainScreen].bounds.size.height#import "PlayerSingle.h"#import#import@implementation PlayerSingle
- (void)dealloc { [_imageBackGroundView release]; [_labelForTitle release]; [_imageCoverLarge release]; [_visualView release]; [_player release]; [_playerItem release]; [_buttonPlayOrPause release]; [_buttonRight release]; [_buttonLeft release]; [_progress release]; [super dealloc];}
+ (instancetype)sharePlayer { // 在静态区,只初始化一次 创建ItemVC3对象. static PlayerSingle *player = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
player = [[PlayerSingle alloc]init]; });
return player;}
- (instancetype)init {
self = [super init];
if (self) { }
return self;}
- (void)playWithUrl:(NSURL *)url showView:(UIView *)showView { self.imageBackGroundView = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"2.jpg"]];
self.imageBackGroundView.frame = showView.bounds; [showView addSubview:self.imageBackGroundView]; self.imageBackGroundView.userInteractionEnabled = YES; [self.imageBackGroundView release];
UIBlurEffect *blur = [UIBlurEffect effectWithStyle:2];
self.visualView = [[UIVisualEffectView alloc]initWithEffect:blur]; self.visualView.frame = showView.bounds;
[self.imageBackGroundView addSubview:self.visualView];
[self.visualView release];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom]; backButton.frame = CGRectMake(10, 24, 32, 32);
[backButton setBackgroundImage:[UIImage imageNamed:@"fanhui"] forState:UIControlStateNormal];
[showView addSubview:backButton];
[backButton addTarget:self action:@selector(backButton:) forControlEvents:UIControlEventTouchUpInside];
UIButton *naozhongButton = [UIButton buttonWithType:UIButtonTypeCustom]; naozhongButton.frame = CGRectMake(WIDTH - 40, 24, 32, 32); [naozhongButton setBackgroundImage:[UIImage imageNamed:@"naozhong"] forState:UIControlStateNormal];
[showView addSubview:naozhongButton];
self.labelForTitle = [[UILabel alloc]initWithFrame:CGRectMake(50, 20, WIDTH - 100, 60)];
[showView addSubview:self.labelForTitle];
self.labelForTitle.text = @"主题";
self.labelForTitle.font = [UIFont systemFontOfSize:20]; self.labelForTitle.textColor = [UIColor whiteColor]; self.labelForTitle.numberOfLines = 0;
self.labelForTitle.textAlignment = NSTextAlignmentCenter;
[self.labelForTitle release];
self.imageCoverLarge = [[UIImageView alloc]initWithImage:[UIImage imageNamed:@"2.jpg"]];
self.imageCoverLarge.frame = CGRectMake((WIDTH - (HEIGHT - 60 - 300)) / 2, 110, HEIGHT - 60 - 300, HEIGHT - 60 - 300);
[showView addSubview:self.imageCoverLarge];
[self.imageCoverLarge release];
self.progress = [[UIProgressView alloc]initWithProgressViewStyle:UIProgressViewStyleDefault]; self.progress.frame = CGRectMake(50, HEIGHT - 200, [UIScreen mainScreen].bounds.size.width - 100, 30);
self.progress.backgroundColor = [UIColor greenColor];
[showView addSubview:self.progress];
[self.progress release];
self.buttonPlayOrPause = [UIButton buttonWithType:UIButtonTypeCustom]; self.buttonPlayOrPause.frame = CGRectMake((WIDTH - 50) / 2, HEIGHT - 150, 50, 50);
[showView addSubview:self.buttonPlayOrPause];
[self.buttonPlayOrPause setBackgroundImage:[UIImage imageNamed:@"zanting2"] forState:UIControlStateNormal]; [self.buttonPlayOrPause addTarget:self action:@selector(handleAction:) forControlEvents:UIControlEventTouchUpInside];
self.buttonRight = [UIButton buttonWithType:UIButtonTypeCustom]; self.buttonRight.frame = CGRectMake((WIDTH - 50) / 2 + 70, HEIGHT - 150, 50, 50);
[showView addSubview:self.buttonRight];
[self.buttonRight setBackgroundImage:[UIImage imageNamed:@"xiayiqu"] forState:UIControlStateNormal];
[self.buttonRight addTarget:self action:@selector(handleButtonRight:) forControlEvents:UIControlEventTouchUpInside];
self.buttonLeft = [UIButton buttonWithType:UIButtonTypeCustom]; self.buttonLeft.frame = CGRectMake((WIDTH - 50) / 2 - 70, HEIGHT - 150, 50, 50);
[showView addSubview:self.buttonLeft];
[self.buttonLeft setBackgroundImage:[UIImage imageNamed:@"shangyiqu"] forState:UIControlStateNormal];
[self.buttonLeft addTarget:self action:@selector(handleButtonLeft:) forControlEvents:UIControlEventTouchUpInside];
self.playerItem = [AVPlayerItem playerItemWithURL:url];
self.player = [AVPlayer playerWithPlayerItem:self.playerItem];
[self.player play];
[self addProgressObserver];//
[self addObserverToPlayerItem:self.playerItem]; }// 返回按钮方法实现.
- (void)backButton:(UIButton *)button {
NSNotification *notice = [NSNotification notificationWithName:@"返回" object:nil];
[[NSNotificationCenter defaultCenter]postNotification:notice];}
#pragma mark UIButton的点击方法.// 播放按钮的点击事件
- (void)handleAction:(UIButton *)button {
if (button.selected) {
[self.buttonPlayOrPause setBackgroundImage:[UIImage imageNamed:@"zanting2"] forState:UIControlStateNormal];
[self.player play];
}else {
[self.buttonPlayOrPause setBackgroundImage:[UIImage imageNamed:@"bofang2"] forState:UIControlStateNormal];
[self.player pause]; }
button.selected = !button.selected;
}
- (void)handleButtonRight:(UIButton *)button {
NSNotification *notice = [NSNotification notificationWithName:@"下一曲" object:nil];
[[NSNotificationCenter defaultCenter]postNotification:notice];
}
- (void)handleButtonLeft:(UIButton *)button {
NSNotification *notice = [NSNotification notificationWithName:@"上一曲" object:nil];
[[NSNotificationCenter defaultCenter]postNotification:notice];}
#pragma mark 监控// 给播放器添加进度更新
.- (void)addProgressObserver {
AVPlayerItem *playerItem = self.player.currentItem;
UIProgressView *progress = self.progress;
// 设置每秒执行一次.
[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds([playerItem duration]);
// NSLog(@"当前已经播放了%.2fs",current);
if (current) {
[progress setProgress:(current/total) animated:YES];
} }];}
// 给AVPlayerItem添加监控.
- (void)addObserverToPlayerItem:(AVPlayerItem *)playerItem {
// 监控状态属性,注意AVPlayer也有一个status属性,通过监控它的status也可以获得播放的状态.
[playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];
// 监控网络加载情况的属性.
[playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];}
// 移除AVPlayerItem上的监控.
- (void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem { [playerItem removeObserver:self forKeyPath:@"status"];
[playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"];}
// 通过KVO监控播放器的状态. keyPath:监控属性, object:监视器, change:状态改变, context:上下文.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context {
AVPlayerItem *playerItem = object;
if ([keyPath isEqualToString:@"status"]) {
AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
if (status == AVPlayerStatusReadyToPlay) {
NSLog(@"正在播放.....,视频总长度为:%.2f",CMTimeGetSeconds(playerItem.duration));
}
}else if([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSArray *array = playerItem.loadedTimeRanges;
// 本次缓冲范围.
CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];
float startSecends = CMTimeGetSeconds(timeRange.start);
float durationSecends = CMTimeGetSeconds(timeRange.duration);
// 缓冲总长度.
NSTimeInterval totalBuffer = startSecends + durationSecends;
NSLog(@"共缓冲:%.2f",totalBuffer);
}
}
@end
VC部分的代码:
.h
//// ItemVC3.h// 喜马拉雅FM//// Created by dllo on 16/1/22.// Copyright © 2016年 lanou.com. All rights reserved.//#import@class ModelForSecendVC;
@interface ItemVC3 : UIViewController
@property (nonatomic, retain) NSString *musicUrlString;
@property (nonatomic, retain) ModelForSecendVC *model;
@property (nonatomic, retain) NSMutableArray *arrForModel;
@property (nonatomic, assign) NSInteger index;
+ (instancetype)shareItemVC3;
@end
.m
//// ItemVC3.m// 喜马拉雅FM//// Created by dllo on 16/1/22.// Copyright © 2016年 lanou.com. All rights reserved.//#define WIDTH [UIScreen mainScreen].bounds.size.width#define HEIGHT [UIScreen mainScreen].bounds.size.height#import "ItemVC3.h"#import "ModelForSecendVC.h"#import "UIImageView+WebCache.h"#import#import#import "Player.h"
#import "PlayerSingle.h"
@interface ItemVC3 ()
@property (nonatomic, retain) NSMutableArray *arrForUrl;
@end
@implementation ItemVC3
+ (instancetype)shareItemVC3 {
// 在静态区,只初始化一次 创建ItemVC3对象.
static ItemVC3 *itemThree = nil;
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
itemThree = [[ItemVC3 alloc]init];
});
return itemThree;
}
#pragma mark ViewController的生命周期.
- (void)viewWillAppear:(BOOL)animated {
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.navigationController setNavigationBarHidden:YES animated:YES];
NSString *urlStr = self.musicUrlString;
urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *url = [NSURL URLWithString:urlStr];
self.arrForUrl = [NSMutableArray array];
for (ModelForSecendVC *model in self.arrForModel) {
[self.arrForUrl addObject:model.playUrl64];
}
[[PlayerSingle sharePlayer]playWithUrl:url showView:self.view];
[self getValue];
// 通知中心记得移除,代理方法记得置空.dealloc.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(handleBack:) name:@"返回" object:nil];
[center addObserver:self selector:@selector(handleXiaYiQu:) name:@"下一曲" object:nil];
[center addObserver:self selector:@selector(handleShangYiQu:) name:@"上一曲" object:nil];
}
// 给播放界面的赋值.
- (void)getValue {
[[PlayerSingle sharePlayer].imageBackGroundView sd_setImageWithURL:[NSURL URLWithString:self.model.coverLarge]];
[[PlayerSingle sharePlayer].imageCoverLarge sd_setImageWithURL:[NSURL URLWithString:self.model.coverLarge]];
[PlayerSingle sharePlayer].labelForTitle.text = self.model.title;
}
- (void)handleBack:(NSNotificationCenter *)center {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)handleXiaYiQu:(NSNotificationCenter *)center {
NSLog(@"下一曲");
if (self.index < self.arrForModel.count - 1) {
[[PlayerSingle sharePlayer].player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.arrForUrl[self.index + 1]]]];
ModelForSecendVC *model = self.arrForModel[self.index + 1];
[[PlayerSingle sharePlayer].imageBackGroundView sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[[PlayerSingle sharePlayer].imageCoverLarge sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[PlayerSingle sharePlayer].labelForTitle.text = model.title;
// VC的内容视图更新.
[self reloadInputViews];
self.index++;
}else {
self.index = 0;
[[PlayerSingle sharePlayer].player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.arrForUrl[0]]]];
ModelForSecendVC *model = self.arrForModel[0];
[[PlayerSingle sharePlayer].imageBackGroundView sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[[PlayerSingle sharePlayer].imageCoverLarge sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[PlayerSingle sharePlayer].labelForTitle.text = model.title;
}
}
- (void)handleShangYiQu:(NSNotificationCenter *)center {
NSLog(@"上一曲");
if (self.index > 0) {
[[PlayerSingle sharePlayer].player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.arrForUrl[self.index - 1]]]];
ModelForSecendVC *model = self.arrForModel[self.index - 1];
[[PlayerSingle sharePlayer].imageBackGroundView sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[[PlayerSingle sharePlayer].imageCoverLarge sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[PlayerSingle sharePlayer].labelForTitle.text = model.title;
// VC的内容视图更新.
[self reloadInputViews];
self.index--;
}else {
self.index = self.arrForModel.count - 1;
[[PlayerSingle sharePlayer].player replaceCurrentItemWithPlayerItem:[AVPlayerItem playerItemWithURL:[NSURL URLWithString:self.arrForUrl[self.arrForModel.count - 1]]]];
ModelForSecendVC *model = self.arrForModel[self.arrForModel.count - 1];
[[PlayerSingle sharePlayer].imageBackGroundView sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[[PlayerSingle sharePlayer].imageCoverLarge sd_setImageWithURL:[NSURL URLWithString:model.coverLarge]];
[PlayerSingle sharePlayer].labelForTitle.text = model.title;
// VC的内容视图更新.
[self reloadInputViews];
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
```