我个人之前有个想法,把平常用的小软件,闲暇之余自己都实现一个迷你版本,打造自己的app专属文件夹,比如闹钟、音乐播放器、视频播放器、电子书以及贪吃蛇之类的小游戏。我相信通过实现这些小程序,能够进一步熟悉iOS开发各方面的专业知识,毕竟我们平常工作过程中,大多数时间都花费在实现业务逻辑上,反而忽略了这些知识细节。上个月抽空初步用OC实现了迷你音乐播放器、迷你闹钟以及用swift实现了贪吃蛇游戏。这些实例我会陆续在博客里分享出来,不过牵涉的知识难度较低,仅对新手有一定帮助意义,大虾请自行绕过。

先看效果图:

效果图初看比较简陋,其实音乐播放器基础功能都有了:播放、暂停、上一首和下一首,除此之外还实现了歌词的滚动显示、进度更新、后台播放、远程控制以及来电处理等功能。下面我们来看如何一步步实现迷你音乐播放器。'

一、新建工程

在xcode中选择新建单视图工程,过程按部就班,不做赘述。

二、导入mp3资源文件

限于时间,这里仅导入了三个mp3文件,都是王菲的歌:红豆、笑忘书和爱情呼叫转移。在网上下载这三个mp3后,拖拽至工程即可。

三、主界面的展示

从上面的效果图可以看出,整个工程只有一个页面,不牵涉到任何的页面跳转,因此主界面不需要使用UINavigationController这么重的视图控制器,UIViewController便能满足要求,项目中主界面对应的视图控制器名称为RootViewController。下面是主界面的显示代码:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{RootViewController *rootViewController = [[RootViewController alloc] init];self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];self.window.rootViewController = rootViewController;[self.window makeKeyAndVisible];return YES;
}

四、音乐播放基础功能实现

先让我们看看音频播放器的初始化方法:

self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];

url表示实际mp3文件的路径。这里需要注意的是,构造一个音频播放器后,该播放器只能播一首歌曲。

播放功能:调用播放器的play函数

暂停功能:调用播放器的pause函数

上一首、下一首功能:RootViewController初始化时,会构建一个mp3资源列表,当用户点击“下一首”按钮后,从资源列表取出下一首音乐的url,并销毁当前播放器,利用刚取到的url构建一个新的播放器,然后调用play函数。

五、播放进度显示

音频播放器中包含有音频持续时间的属性duration,单位为秒。进度条区间为0-1,因此播放进度 = 已经播放的秒数 / duration。实现方案很简单,创建一个时间间隔为1s的定时器,每次执行播放时间加1,然后按照上面的公式更新progeressview的进度。

六、歌词的滚动显示

歌词部分用UITableView实现,每一行歌词都是一个cell,随着时间的推移调用

[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];

达到歌词滚动的动态效果。

七、音乐的后台播放

如果不实现后台播放模式,用户只能够在音乐播放器显示在前台的时候方能听音乐,按home键退到后台后,音乐自动暂停播放。由此可见,作为音乐播放软件,后台播放功能必不可少。

在工程的plist文件中加入如下字段:

设置AVAudioSession模式:

    AVAudioSession *session = [AVAudioSession sharedInstance];[session setActive:YES error:nil];[session setCategory:AVAudioSessionCategoryPlayback error:nil];

八、远程控制

锁屏之后能够控制音乐的播放

首先设置开启接收远程事件:

    [[UIApplication sharedApplication] beginReceivingRemoteControlEvents];

实现远程控制代理方法:

- (void)remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {if (receivedEvent.type == UIEventTypeRemoteControl) {switch (receivedEvent.subtype) {case UIEventSubtypeRemoteControlPause:[self.playButton setTitle:@"播放" forState:UIControlStateNormal];[self.audioPlayer pause];break;case UIEventSubtypeRemoteControlPlay:[self.playButton setTitle:@"播放" forState:UIControlStateNormal];[self.audioPlayer pause];break;case UIEventSubtypeRemoteControlPreviousTrack:NSLog(@"上一首");[self playPreMusic];break;case UIEventSubtypeRemoteControlNextTrack:NSLog(@"下一首");[self playNextMusic];break;default:break;  }  }
}

九、 来电处理

设置播放器的delegate

    self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];self.audioPlayer.delegate = self;

处理各种代理方法

#pragma mark - AVAudioPlayerDelegate- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{[self playNextMusic];
}- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error
{// 解码失败,自动播放下一首[self playNextMusic];
}//  音乐播放器被打断 (如开始 打、接电话)
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{// 会自动暂停  do nothing ...NSLog(@"audioPlayerBeginInterruption---被打断");
}//  音乐播放器打断终止 (如结束 打、接电话)
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{// 手动恢复播放[player play];NSLog(@"audioPlayerEndInterruption---打断终止");
}

到这里我们的音乐播放器就实现完毕了,目前界面细节以及动态添加歌曲功能尚未实现,后续有时间再补上。

项目的完整代码:https://github.com/wanglichun/WLCMusicPlayer,欢迎有兴趣的同学加进来,一起学习进步。

最后附上项目主界面的完整代码:

#import "RootViewController.h"
#import <AVFoundation/AVFoundation.h>@interface RootViewController ()<AVAudioPlayerDelegate, UITableViewDelegate, UITableViewDataSource>@property (strong, nonatomic) AVAudioPlayer *audioPlayer;
@property (strong, nonatomic) NSArray *musicFiles;
@property (assign, nonatomic) NSInteger currentIndex;
@property (weak, nonatomic) IBOutlet UILabel *musicNameLabel;
@property (weak, nonatomic) IBOutlet UIProgressView *progressView;
@property (strong, nonatomic) NSTimer *progressTimer;
@property (assign, nonatomic) NSInteger playedTotalSeconds;
@property (weak, nonatomic) IBOutlet UIButton *playButton;
@property (strong, nonatomic) NSString *lyricsText;
@property (strong, nonatomic) NSArray *lyricsArr;
@property (weak, nonatomic) IBOutlet UITableView *tableView;@end@implementation RootViewController- (void)viewDidLoad
{[super viewDidLoad];self.progressView.progress = 0;//让app支持接受远程控制事件[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];AVAudioSession *session = [AVAudioSession sharedInstance];[session setActive:YES error:nil];[session setCategory:AVAudioSessionCategoryPlayback error:nil];[self loadMusicFiles];NSDictionary *musicInfo = self.musicFiles[self.currentIndex];[self playMusic:musicInfo[@"url"]];[self.playButton setTitle:@"暂停" forState:UIControlStateNormal];self.lyricsText =  @"还没好好的感受 雪花绽放的气候\r\n"@"我们一起颤抖 会更明白\r\n"@"什么是温柔\r\n"@"还没跟你牵著手 走过荒芜的沙丘\r\n"@"可能从此以后 学会珍惜\r\n"@"天长和地久\r\n"@"有时候 有时候\r\n"@"我会相信一切有尽头\r\n"@"相聚离开 都有时候\r\n"@"没有什么会永垂不朽\r\n"@"可是我 有时候\r\n"@"宁愿选择留恋不放手\r\n"@"等到风景都看透\r\n"@"也许你会陪我 看细水长流\r\n"@"还没为你把红豆 熬成缠绵的伤口\r\n"@"然后一起分享 会更明白\r\n"@"相思的哀愁\r\n"@"还没好好的感受 醒著亲吻的温柔\r\n"@"可能在我左右 你才追求\r\n"@"孤独的自由\r\n"@"有时候 有时候\r\n"@"我会相信一切有尽头\r\n"@"相聚离开 都有时候\r\n"@"没有什么会永垂不朽\r\n"@"可是我 有时候\r\n"@"宁愿选择留恋不放手\r\n"@"等到风景都看透\r\n"@"也许你会陪我 看细水长流\r\n"@"有时候 有时候\r\n"@"我会相信一切有尽头\r\n"@"相聚离开 都有时候\r\n"@"没有什么会永垂不朽\r\n"@"可是我 有时候\r\n"@"宁愿选择留恋不放手\r\n"@"等到风景都看透\r\n"@"也许你会陪我 看细水长流";self.lyricsArr = [self.lyricsText componentsSeparatedByString:@"\r\n"];
}- (void)didReceiveMemoryWarning
{[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}#pragma mark - Private- (void)loadMusicFiles
{self.musicFiles = @[@{@"name":@"红豆", @"url":@"hongdou.mp3"},@{@"name":@"笑忘书", @"url":@"xiaowangshu.mp3"},@{@"name":@"因为爱情", @"url":@"yinweiaiqing.mp3"}];
}- (void)playMusic:(NSString *)musicUrl
{NSURL *url = [[NSBundle mainBundle] URLForResource:musicUrl withExtension:nil];self.audioPlayer = nil;self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];self.audioPlayer.delegate = self;[self.audioPlayer prepareToPlay];[self.audioPlayer play];[self.playButton setTitle:@"暂停" forState:UIControlStateNormal];self.progressView.progress = 0;self.playedTotalSeconds = 0;[self stopTimer];[self startTimer];
}- (void)playNextMusic
{if (self.currentIndex == self.musicFiles.count - 1) {self.currentIndex = 0;} else {self.currentIndex++;}NSDictionary *musicInfo = self.musicFiles[self.currentIndex];[self playMusic:musicInfo[@"url"]];self.musicNameLabel.text = musicInfo[@"name"];
}- (void)playPreMusic
{if (self.currentIndex == 0) {self.currentIndex = self.musicFiles.count - 1;} else {self.currentIndex--;}NSDictionary *musicInfo = self.musicFiles[self.currentIndex];[self playMusic:musicInfo[@"url"]];self.musicNameLabel.text = musicInfo[@"name"];
}#pragma mark - Button actions- (IBAction)onPlayButtonClicked:(id)sender
{if (self.audioPlayer.isPlaying) {[self.playButton setTitle:@"播放" forState:UIControlStateNormal];[self.audioPlayer pause];} else {[self.playButton setTitle:@"暂停" forState:UIControlStateNormal];[self.audioPlayer play];}
}- (IBAction)onNextButtonClicked:(id)sender
{[self playNextMusic];
}- (IBAction)onPreButtonClicked:(id)sender
{[self playPreMusic];
}#pragma mark - AVAudioPlayerDelegate- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{[self playNextMusic];
}- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)player error:(NSError * __nullable)error
{// 解码失败,自动播放下一首[self playNextMusic];
}//  音乐播放器被打断 (如开始 打、接电话)
- (void)audioPlayerBeginInterruption:(AVAudioPlayer *)player
{// 会自动暂停  do nothing ...NSLog(@"audioPlayerBeginInterruption---被打断");
}//  音乐播放器打断终止 (如结束 打、接电话)
- (void)audioPlayerEndInterruption:(AVAudioPlayer *)player withOptions:(NSUInteger)flags
{// 手动恢复播放[player play];NSLog(@"audioPlayerEndInterruption---打断终止");
}#pragma mark - Timer- (void)startTimer
{if (!self.progressTimer) {self.progressTimer = [NSTimer timerWithTimeInterval:1.0f target:self selector:@selector(updateProgress) userInfo:nil repeats:YES];[[NSRunLoop currentRunLoop] addTimer:self.progressTimer forMode:NSRunLoopCommonModes];}
}- (void)stopTimer
{if (self.progressTimer) {[self.progressTimer invalidate];self.progressTimer = nil;}
}- (void)updateProgress
{self.playedTotalSeconds++;CGFloat rate = self.playedTotalSeconds / self.audioPlayer.duration;self.progressView.progress = rate;NSInteger currentRow = rate * self.lyricsArr.count;if (currentRow < self.lyricsArr.count) {NSIndexPath *indexPath = [NSIndexPath indexPathForRow:currentRow inSection:0];[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];}
}#pragma mark - RemoteControlEvent- (void)remoteControlReceivedWithEvent: (UIEvent *) receivedEvent {if (receivedEvent.type == UIEventTypeRemoteControl) {switch (receivedEvent.subtype) {case UIEventSubtypeRemoteControlPause:[self.playButton setTitle:@"播放" forState:UIControlStateNormal];[self.audioPlayer pause];break;case UIEventSubtypeRemoteControlPlay:[self.playButton setTitle:@"播放" forState:UIControlStateNormal];[self.audioPlayer pause];break;case UIEventSubtypeRemoteControlPreviousTrack:NSLog(@"上一首");[self playPreMusic];break;case UIEventSubtypeRemoteControlNextTrack:NSLog(@"下一首");[self playNextMusic];break;default:break;  }  }
}#pragma mark - UITableViewDataSource- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{return self.lyricsArr.count;
}- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{return 28;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{static NSString *reuseIdentifier = @"LyricsTableViewCell";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];if (!cell) {cell = [[UITableViewCell alloc] init];cell.textLabel.textAlignment = NSTextAlignmentCenter;cell.textLabel.font = [UIFont systemFontOfSize:12.0];cell.backgroundColor = [UIColor clearColor];}cell.textLabel.text = self.lyricsArr[indexPath.row];return cell;
}@end

iOS开发--手把手教你制作音乐播放器相关推荐

  1. 手把手教你做音乐播放器(八)桌面小工具(上)

    第8节 桌面小工具 桌面小工具是可以放置在主界面的.快速控制应用的小助手.例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停.播放.切换等操作,而不需要启动应用本身. 在安卓系统中,我们也常 ...

  2. 手把手教你做音乐播放器(一)功能规划

    前言 学习完"计算器" "视频播放器" "蓝牙聊天"以后,对安卓应用的开发我们基本上就入门70%了. 现在,我们将在之前学习的基础上,进一步 ...

  3. 手把手教你做音乐播放器(八)桌面小工具(下)(完)

    8.4 MusicService的改造 8.4.1 App widget触发MusicService 当App widget的按钮被点击后,会触发隐式定义的Intent发送给MusicService. ...

  4. iOS开发手记-仿QQ音乐播放器动态歌词的实现

    最近朋友想做个音乐App,让我帮忙参考下.其中歌词动态滚动的效果,正好我之前也没做过,顺便学习一下,先来个预览效果. 实现思路 歌词常见的就是lrc歌词了,我们这里也是通过解析lrc歌词文件来获取其播 ...

  5. 手把手教你做音乐播放器(四)播放服务的搭建

    第4节 播放服务的搭建 播放音乐的服务-MusicService是整改音乐播放器的核心,它将播放界面和实际的播放功能连接在一起. 4.1 MusicService的接口 它对外要提供两类主要的功能,播 ...

  6. 手把手教你做音乐播放器(五)音乐列表的存储(上)

    第5节 播放列表的存取 关于播放列表的存取需要三个组件的协同配合, MusicListActivity:让用户选择多首或一首音乐,将用户的选择项,传递给MusicService: MusicServi ...

  7. 手把手教你做音乐播放器(三)获取音乐信息

    第3节 获取音乐信息 在"视频播放器"的开发过程当中,我们已经学会了如何获取视频文件的信息: 定义一个视频信息的数据结构VideoItem: 自定义一个AnsycTask,在它的工 ...

  8. 手把手教你做音乐播放器(七)播放音乐(上)

    第7节 播放音乐 音乐播放列表也准备好了,我们来播放音乐吧.完成后效果如下, 实现音乐的播放,我们需要播放界面和音乐服务两个方面的合作. 7.1 MusicService 前面我们已经为播放音乐的功能 ...

  9. Vue项目——手把手教你做出音乐播放器

    进入VueMusic-安装所需的东西 cnpm install 之后 启动这个项目 npm start App.vue 设置默认样式 <template><div id=" ...

最新文章

  1. DataGridView 密码列(显示为*号)的设置
  2. SQL Server 索引和表体系结构(聚集索引)
  3. dijkstra邻接表_掌握算法-图论-最短路径算法-Dijkstra算法
  4. VTK:网格之TableBasedClipDataSetWithPolyData2
  5. HTML 5 Web Socket:下一次Web通信革命揭幕,互联网营销
  6. Linux进程管理与调度-之-目录导航【转】
  7. 遇到一个打开文件方式“w+”和“a+”的问题
  8. Mysql学习总结(81)——为什么MySQL不推荐使用uuid或者雪花id作为主键?
  9. 函数的非固定参数,默认参数,参数组
  10. python进阶06并发之二技术点关键词
  11. 不用编程实现USB或串口条码枪对接PLC,将数据写入到寄存器
  12. Python字符串str和json格式的转换
  13. ogg是什么文件?ogg怎么转mp3格式?
  14. python3 排列组合
  15. Vue3 理解 toRef 和 toRefs 的作用、用法、区别
  16. 2022年中国现制咖啡行业市场规模及重点企业经营现状分析:市场规模达89.7亿元[图]
  17. 宇视摄像头安装——筒机安装
  18. Matlab求解AX=XB(手眼标定用)
  19. Access根据出生日期计算年龄_不好意思,Power Query里根据出生日期计算年龄有点儿繁琐!...
  20. 办公族的正确呼吸方式

热门文章

  1. 10款最佳在线地图软件介绍
  2. 数字信号处理7——点到向量的距离
  3. 28.深度学习模型压缩方法-2
  4. Python_FontTools使用
  5. vue使用tradingview开发K线图相关问题
  6. 51单片机学习2——DS1302制作简易数码管电子时钟
  7. H5音乐播放器(包含源码与示例)
  8. 红外线遥控协议简介(NEC格式)
  9. JAVA毕业设计飞机航班信息查询系统演示视频2021计算机源码+lw文档+系统+调试部署+数据库
  10. 企业电子邮件系统全局地址簿管理及使用方法介绍