1.封装音频播放工具类
//
// AudioTool.h
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface AudioTool : NSObject
/**
* 播放音效
*
* @param filename 音效文件名
*/
+ (void)playSound:(NSString *)filename;
/**
* 销毁音效
*
* @param filename 音效文件名
*/
+ (void)disposeSound:(NSString *)filename;
/**
* 播放音乐
*
* @param filename 音乐文件名
*/
+ (AVAudioPlayer *)playMusic:(NSString *)filename;
/**
* 暂停音乐
*
* @param filename 音乐文件名
*/
+ (void)pauseMusic:(NSString *)filename;
/**
* 停止音乐
*
* @param filename 音乐文件名
*/
+ (void)stopMusic:(NSString *)filename;
/**
* 当前播放的音乐播放器
*/
+ (AVAudioPlayer *)currentPlayingAudioPlayer;
@end
//
// AudioTool.m
#import "AudioTool.h"
@implementation AudioTool
/**
* 存放所有的音效ID
* 字典: filename作为key,soundID作为value
*/
static NSMutableDictionary *_soundIDDict;
/**
* 存放所有的音乐播放器
* 字典: filename作为key,audioPlayer作为value
*/
static NSMutableDictionary *_audioPlayerDict;
/**
* 初始化
*/
+ (void)initialize
{
_soundIDDict = [NSMutableDictionary dictionary];
_audioPlayerDict = [NSMutableDictionary dictionary];
//设置音频回话类型
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
[session setActive:YES error:nil];
}
/**
* 播放音效
*
* @param filename 音效文件名
*/
+ (void)playSound:(NSString *)filename
{
if (!filename) return;
//1.从字典中取出soundID
SystemSoundID soundID = [_soundIDDict[filename] unsignedIntValue];
if (!soundID) { //创建
//加载音效文件
NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if(!url) return;
//创建音效ID
AudioServicesCreateSystemSoundID((__bridge CFURLRef _Nonnull)(url), &soundID);
//存入字典
_soundIDDict[filename] = @(soundID);
}
//2.播放
AudioServicesPlaySystemSound(soundID);
}
/**
* 销毁音效
*
* @param filename 音效文件名
*/
+ (void)disposeSound:(NSString *)filename
{
if (!filename) return;
SystemSoundID soundID = [_soundIDDict[filename] unsignedIntValue];
if (!soundID) {
//销毁音效ID
AudioServicesDisposeSystemSoundID(soundID);
//从字典中移除
[_soundIDDict removeObjectForKey:filename];
}
}
/**
* 播放音乐
*
* @param filename 音乐文件名
*/
+ (AVAudioPlayer *)playMusic:(NSString *)filename
{
if (!filename) return nil;
//1.从字典中取出audioPlayer
AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
if (!audioPlayer) { //创建
//加载音乐文件
NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:nil];
if(!url) return nil;
//创建audioPlayer
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
//缓冲
[audioPlayer prepareToPlay];
//存入字典
_audioPlayerDict[filename] = audioPlayer;
}
//2.播放
if (!audioPlayer.isPlaying) {
[audioPlayer play];
}
return audioPlayer;
}
/**
* 暂停音乐
*
* @param filename 音乐文件名
*/
+ (void)pauseMusic:(NSString *)filename
{
if (!filename) return;
//1.从字典中取出audioPlayer
AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
//2.暂停
if (audioPlayer.isPlaying) {
[audioPlayer pause];
}
}
/**
* 停止音乐
*
* @param filename 音乐文件名
*/
+ (void)stopMusic:(NSString *)filename
{
if (!filename) return;
//1.从字典中取出audioPlayer
AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
//2.暂停
if (audioPlayer.isPlaying) {
[audioPlayer stop];
//从字典中移除
[_audioPlayerDict removeObjectForKey:filename];
}
}
/**
* 当前播放的音乐播放器
*/
+ (AVAudioPlayer *)currentPlayingAudioPlayer
{
for (NSString *filename in _audioPlayerDict) {
AVAudioPlayer *audioPlayer = _audioPlayerDict[filename];
if (audioPlayer.isPlaying) {
return audioPlayer;
}
}
return nil;
}
@end
2.要播放的音乐模型
//
// MusicModel.h
#import <Foundation/Foundation.h>
@interface MusicModel : NSObject
/**
* 歌曲名称
*/
@property(nonatomic,copy)NSString *name;
/**
* 歌曲文件名称
*/
@property(nonatomic,copy)NSString *filename;
/**
* 歌手
*/
@property(nonatomic,copy)NSString *singer;
/**
* 歌手图片
*/
@property(nonatomic,copy)NSString *singerIcon;
/**
* 大图
*/
@property(nonatomic,copy)NSString *icon;
@property(nonatomic,assign,getter=isPlaying)BOOL playing;
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)musicWithDict:(NSDictionary *)dict;
@end
//
// MusicModel.m
#import "MusicModel.h"
@implementation MusicModel
- (instancetype)initWithDict:(NSDictionary *)dict
{
self = [super init];
if (self) {
self.name = dict[@"name"];
self.filename = dict[@"filename"];
self.singer = dict[@"singer"];
self.singerIcon = dict[@"singerIcon"];
self.icon = dict[@"icon"];
}
return self;
}
+ (instancetype)musicWithDict:(NSDictionary *)dict
{
return [[self alloc] initWithDict:dict];
}
@end
3.控制器里(这里我们用的UITableView)
//
// MusicViewController.m
#import "MusicViewController.h"
#import "MusicModel.h"
#import "AudioTool.h"
#import <AVFoundation/AVFoundation.h>
#import <MediaPlayer/MediaPlayer.h>
#import "MusicCell.h"
@interface MusicViewController ()<AVAudioPlayerDelegate>
- (IBAction)jump:(UIBarButtonItem *)sender;
/**
* 存放所有的音乐模型数据
*/
@property(nonatomic,strong) NSArray *musics;
@property(nonatomic,strong)CADisplayLink *link;
/**
* 当前正在播放的音乐播放器
*/
@property (nonatomic, strong) AVAudioPlayer *currentPlayingAudioPlayer;
@end
@implementation MusicViewController
/**
* 懒加载 创建音乐模型数据
*/
- (NSArray *)musics
{
if (!_musics) {
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Musics.plist" ofType:nil]];
NSMutableArray *arrayM = [NSMutableArray array];
for (NSDictionary *dict in array) {
[arrayM addObject:[MusicModel musicWithDict:dict]];
}
_musics = arrayM;
}
return _musics;
}
- (CADisplayLink *)link
{
if (!_link) {
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
}
return _link;
}
/**
* 实时更新(1秒中调用60次)
*/
- (void)update
{
//监听播放进度
//总时长、当前时间
// NSLog(@"%f %f", self.currentPlayingAudioPlayer.duration, self.currentPlayingAudioPlayer.currentTime);
#warning 调整歌词
}
- (void)viewDidLoad {
[super viewDidLoad];
}
#pragma mark - Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.musics.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
//1.创建cell
MusicCell *cell = [MusicCell cellWithTableView:tableView];
//2.设置cell的数据
cell.model = self.musics[indexPath.row];
//3.
return cell;
}
#pragma mark -Table view delegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 70;
}
/**
* 取消选中会调用
*/
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
//停止播放
MusicModel *model = self.musics[indexPath.row];
[AudioTool stopMusic:model.filename];
//再次传递模型(让上一手歌的图片停止转动)
MusicCell *cell = (MusicCell *)[tableView cellForRowAtIndexPath:indexPath];
model.playing = NO;
cell.model = model;
}
/**
* 点击了cell(选中了)
*/
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//播放音乐
MusicModel *model = self.musics[indexPath.row];
AVAudioPlayer * audioPlayer = [AudioTool playMusic:model.filename];
audioPlayer.delegate = self;
self.currentPlayingAudioPlayer = audioPlayer;
//在锁屏界面显示歌曲信息
[self showInfoInLockedScreen:model];
//开启定时器监听播放进度
[self.link invalidate];
self.link = nil;
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
//再次传递模型(让cell上图片转动起来)
MusicCell *cell = (MusicCell *)[tableView cellForRowAtIndexPath:indexPath];
model.playing = YES;
cell.model = model;
}
/**
* 在锁屏界面显示歌曲信息
*/
- (void)showInfoInLockedScreen:(MusicModel *)model
{
NSMutableDictionary *info = [NSMutableDictionary dictionary];
// 标题(音乐名称)
info[MPMediaItemPropertyTitle] = model.name;
// 作者
info[MPMediaItemPropertyArtist] = model.singer;
// 专辑
info[MPMediaItemPropertyAlbumTitle] = model.singer;
// 图片
info[MPMediaItemPropertyArtwork] = [[MPMediaItemArtwork alloc] initWithImage:[UIImage imageNamed:model.icon]];
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = info;
}
#pragma mark - AVAudioPlayerDelegate
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
//计算下一行
NSIndexPath *selectedPath = [self.tableView indexPathForSelectedRow];
long next = selectedPath.row + 1;
if (next == self.musics.count) {
next = 0;
}
//停止上一首歌的转圈
MusicCell *cell = (MusicCell *)[self.tableView cellForRowAtIndexPath:selectedPath];
MusicModel *model = self.musics[selectedPath.row];
model.playing = NO;
cell.model = model;
//播放下一首歌
NSIndexPath *currentPath = [NSIndexPath indexPathForRow:next inSection:selectedPath.section];
[self.tableView selectRowAtIndexPath:currentPath animated:YES scrollPosition:UITableViewScrollPositionTop];
[self tableView:self.tableView didSelectRowAtIndexPath:currentPath];
}
/**
* 音乐播放器被打断(电话)
*/
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player{}
/**
* 打断结束(电话结束)
*/
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{
[player play];
}
- (IBAction)jump:(UIBarButtonItem *)sender
{
//设置当前播放器的当前时间为 总时长的 最后3秒
self.currentPlayingAudioPlayer.currentTime = self.currentPlayingAudioPlayer.duration - 3;
}
@end
4.自定义的UITableViewCell
//
// MusicCell.h
#import <UIKit/UIKit.h>
@class MusicModel;
@interface MusicCell : UITableViewCell
+ (instancetype)cellWithTableView:(UITableView *)tableView;
@property (nonatomic, strong) MusicModel *model;
@end
//
// MusicCell.m
#import "MusicCell.h"
#import "MusicModel.h"
#import "UIImage+MJ.h"
#import "Colours.h"
@interface MusicCell()
@property (nonatomic, strong) CADisplayLink *link;
@end
@implementation MusicCell
- (CADisplayLink *)link
{
if (!_link) {
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
}
return _link;
}
+ (instancetype)cellWithTableView:(UITableView *)tableView
{
static NSString *ID = @"music";
MusicCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[MusicCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:ID];
}
return cell;
}
- (void)setModel:(MusicModel *)model
{
_model = model;
self.textLabel.text = model.name;
self.detailTextLabel.text = model.singer;
self.imageView.image = [UIImage circleImageWithName:model.singerIcon borderWidth:2.0 borderColor:[UIColor steelBlueColor]];
if (model.isPlaying) {
//转圈
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}else{ //停止动画
[self.link invalidate];
self.link = nil;
[UIView animateWithDuration:2.0 animations:^{
self.imageView.transform = CGAffineTransformIdentity;
}];
}
}
/**
* 8秒转一圈, 45°/s
*/
- (void)update
{
// 1/60秒 * 45
// 规定时间内转动的角度 == 时间 * 速度
CGFloat angle = self.link.duration * M_PI_4;
self.imageView.transform = CGAffineTransformRotate(self.imageView.transform, angle);
}
@end
5.用到的其他
//
// UIImage+MJ.h
#import <UIKit/UIKit.h>
@interface UIImage (MJ)
/**
* 返回一个带边框的圆形图片
*
* @param name 图片名称
* @param borderWidth 边框宽度
* @param borderColor 边框颜色
*/
+ (instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor;
@end
//
// UIImage+MJ.m
#import "UIImage+MJ.h"
@implementation UIImage (MJ)
+ (instancetype)circleImageWithName:(NSString *)name borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)borderColor
{
// 1.加载原图
UIImage *oldImage = [UIImage imageNamed:name];
// 2.开启上下文
CGFloat imageW = oldImage.size.width + 2 * borderWidth;
CGFloat imageH = oldImage.size.height + 2 * borderWidth;
CGSize imageSize = CGSizeMake(imageW, imageH);
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0.0);
// 3.取得当前的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 4.画边框(大圆)
[borderColor set];
CGFloat bigRadius = imageW * 0.5; // 大圆半径
CGFloat centerX = bigRadius; // 圆心
CGFloat centerY = bigRadius;
CGContextAddArc(ctx, centerX, centerY, bigRadius, 0, M_PI * 2, 0);
CGContextFillPath(ctx); // 画圆
// 5.小圆
CGFloat smallRadius = bigRadius - borderWidth;
CGContextAddArc(ctx, centerX, centerY, smallRadius, 0, M_PI * 2, 0);
// 裁剪(后面画的东西才会受裁剪的影响)
CGContextClip(ctx);
// 6.画图
[oldImage drawInRect:CGRectMake(borderWidth, borderWidth, oldImage.size.width, oldImage.size.height)];
// 7.取图
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 8.结束上下文
UIGraphicsEndImageContext();
return newImage;
}
@end
还用到了Colours.h,非必要。如需要可去github下载。