音乐播放器-小案列

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下载。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值