天天看點

iOS開發拓展篇—音頻處理(音樂播放器3)

iOS開發拓展篇—音頻處理(音樂播放器3)

說明:這篇文章主要介紹音頻工具類和播放工具類的封裝。

一、控制器間資料傳遞

1.兩個控制器之間資料的傳遞

第一種方法:self.parentViewController.music=self.music[indexPath.row];不能滿足

第二種做法:把整個數組傳遞給它

第三種做法:設定一個資料源,設定播放控制器的資料源是這個控制器。self.parentViewController.dataSource=self;好處:沒有耦合性,任何實作了協定的可以作為資料源。

第四種做法:把整個項目會使用到的音頻資源交給一個工具類去管理,這樣就不用傳遞過去了。直接向工具類索要資源就可以。

二、封裝一個音頻工具類

建立一個音頻工具類,用來管理音樂資料(音樂模型)

  

iOS開發拓展篇—音頻處理(音樂播放器3)

工具類中的代碼設計如下:

 YYMusicTool.h檔案

1 //
 2 //  YYMusicTool.h
 3 //
 4 
 5 #import <Foundation/Foundation.h>
 6 @class YYMusicModel;
 7 @interface YYMusicTool : NSObject
 8 /**
 9  *  傳回所有的歌曲
10  */
11 + (NSArray *)musics;
12 
13 /**
14  *  傳回正在播放的歌曲
15  */
16 + (YYMusicModel *)playingMusic;
17 + (void)setPlayingMusic:(YYMusicModel *)playingMusic;
18 
19 /**
20  *  下一首歌曲
21  */
22 + (YYMusicModel *)nextMusic;
23 
24 /**
25  *  上一首歌曲
26  */
27 + (YYMusicModel *)previousMusic;
28 @end      

YYMusicTool.m檔案

1 //
 2 //  YYMusicTool.m
 3 //
 4 
 5 #import "YYMusicTool.h"
 6 #import "YYMusicModel.h"
 7 #import "MJExtension.h"
 8 
 9 @implementation YYMusicTool
10 
11 static NSArray *_musics;
12 static  YYMusicModel *_playingMusic;
13 
14 /**
15  *  @return 傳回所有的歌曲
16  */
17 +(NSArray *)musics
18 {
19     if (_musics==nil) {
20         _musics=[YYMusicModel objectArrayWithFilename:@"Musics.plist"];
21     }
22     return _musics;
23 }
24 
25 +(void)setPlayingMusic:(YYMusicModel *)playingMusic
26 {
27     /*
28      *如果沒有傳入需要播放的歌曲,或者是傳入的歌曲名不在音樂庫中,那麼就直接傳回
29       如果需要播放的歌曲就是目前正在播放的歌曲,那麼直接傳回
30      */
31     if (!playingMusic || ![[self musics]containsObject:playingMusic]) return;
32     if (_playingMusic == playingMusic) return;
33     _playingMusic=playingMusic;
34 }
35 /**
36  *  傳回正在播放的歌曲
37  */
38 +(YYMusicModel *)playingMusic
39 {
40     return _playingMusic;
41 }
42 
43 /**
44  *  下一首歌曲
45  */
46 +(YYMusicModel *)nextMusic
47 {
48     //設定一個初值
49     int nextIndex = 0;
50     if (_playingMusic) {
51         //擷取目前播放音樂的索引
52         int playingIndex = [[self musics] indexOfObject:_playingMusic];
53         //設定下一首音樂的索引
54         nextIndex = playingIndex+1;
55         //檢查數組越界,如果下一首音樂是最後一首,那麼重置為0
56         if (nextIndex>=[self musics].count) {
57             nextIndex=0;
58         }
59     }
60     return [self musics][nextIndex];
61 }
62 
63 /**
64  *  上一首歌曲
65  */
66 +(YYMusicModel *)previousMusic
67 {
68     //設定一個初值
69     int previousIndex = 0;
70     if (_playingMusic) {
71         //擷取目前播放音樂的索引
72         int playingIndex = [[self musics] indexOfObject:_playingMusic];
73         //設定下一首音樂的索引
74         previousIndex = playingIndex-1;
75         //檢查數組越界,如果下一首音樂是最後一首,那麼重置為0
76         if (previousIndex<0) {
77             previousIndex=[self musics].count-1;
78         }
79     }
80     return [self musics][previousIndex];
81 }
82 @end      

三、封裝一個音樂播放工具類

該工具類中的代碼設計如下:

YYAudioTool.h檔案

1 //
 2 //  YYAudioTool.h
 3 //
 4 
 5 #import <Foundation/Foundation.h>
 6 #import <AVFoundation/AVFoundation.h>
 7 @interface YYAudioTool : NSObject
 8 /**
 9  *播放音樂檔案
10  */
11 +(BOOL)playMusic:(NSString *)filename;
12 /**
13  *暫停播放
14  */
15 +(void)pauseMusic:(NSString *)filename;
16 /**
17  *播放音樂檔案
18  */
19 +(void)stopMusic:(NSString *)filename;
20 
21 /**
22  *播放音效檔案
23  */
24 +(void)playSound:(NSString *)filename;
25 /**
26  *銷毀音效
27  */
28 +(void)disposeSound:(NSString *)filename;
29 @end      

YYAudioTool.m檔案

1 //
  2 //  YYAudioTool.m
  3 //
  4 
  5 #import "YYAudioTool.h"
  6 
  7 @implementation YYAudioTool
  8 /**
  9  *存放所有的音樂播放器
 10  */
 11 static NSMutableDictionary *_musicPlayers;
 12 +(NSMutableDictionary *)musicPlayers
 13 {
 14     if (_musicPlayers==nil) {
 15         _musicPlayers=[NSMutableDictionary dictionary];
 16     }
 17     return _musicPlayers;
 18 }
 19 
 20 /**
 21  *存放所有的音效ID
 22  */
 23 static NSMutableDictionary *_soundIDs;
 24 +(NSMutableDictionary *)soundIDs
 25 {
 26     if (_soundIDs==nil) {
 27         _soundIDs=[NSMutableDictionary dictionary];
 28     }
 29     return _soundIDs;
 30 }
 31 
 32 
 33 /**
 34  *播放音樂
 35  */
 36 +(BOOL)playMusic:(NSString *)filename
 37 {
 38     if (!filename) return NO;//如果沒有傳入檔案名,那麼直接傳回
 39     //1.取出對應的播放器
 40     AVAudioPlayer *player=[self musicPlayers][filename];
 41     
 42     //2.如果播放器沒有建立,那麼就進行初始化
 43     if (!player) {
 44         //2.1音頻檔案的URL
 45         NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];
 46         if (!url) return NO;//如果url為空,那麼直接傳回
 47         
 48         //2.2建立播放器
 49         player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
 50         
 51         //2.3緩沖
 52         if (![player prepareToPlay]) return NO;//如果緩沖失敗,那麼就直接傳回
 53         
 54         //2.4存入字典
 55         [self musicPlayers][filename]=player;
 56     }
 57     
 58     //3.播放
 59     if (![player isPlaying]) {
 60         //如果目前沒處于播放狀态,那麼就播放
 61         return [player play];
 62     }
 63 
 64     return YES;//正在播放,那麼就傳回YES
 65 }
 66 
 67 +(void)pauseMusic:(NSString *)filename
 68 {
 69     if (!filename) return;//如果沒有傳入檔案名,那麼就直接傳回
 70     
 71     //1.取出對應的播放器
 72     AVAudioPlayer *player=[self musicPlayers][filename];
 73     
 74     //2.暫停
 75     [player pause];//如果palyer為空,那相當于[nil pause],是以這裡可以不用做處理
 76 
 77 }
 78 
 79 +(void)stopMusic:(NSString *)filename
 80 {
 81     if (!filename) return;//如果沒有傳入檔案名,那麼就直接傳回
 82     
 83     //1.取出對應的播放器
 84     AVAudioPlayer *player=[self musicPlayers][filename];
 85     
 86     //2.停止
 87     [player stop];
 88     
 89     //3.将播放器從字典中移除
 90     [[self musicPlayers] removeObjectForKey:filename];
 91 }
 92 
 93 //播放音效
 94 +(void)playSound:(NSString *)filename
 95 {
 96     if (!filename) return;
 97     //1.取出對應的音效
 98     SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue];
 99     
100     //2.播放音效
101     //2.1如果音效ID不存在,那麼就建立
102     if (!soundID) {
103         
104         //音效檔案的URL
105         NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];
106         if (!url) return;//如果URL不存在,那麼就直接傳回
107         
108         OSStatus status = AudioServicesCreateSystemSoundID((__bridge CFURLRef)(url), &soundID);
109         NSLog(@"%ld",status);
110         //存入到字典中
111         [self soundIDs][filename]=@(soundID);
112     }
113     
114     //2.2有音效ID後,播放音效
115     AudioServicesPlaySystemSound(soundID);
116 }
117 
118 //銷毀音效
119 +(void)disposeSound:(NSString *)filename
120 {
121     //如果傳入的檔案名為空,那麼就直接傳回
122     if (!filename) return;
123     
124     //1.取出對應的音效
125     SystemSoundID soundID=[[self soundIDs][filename] unsignedIntegerValue];
126     
127     //2.銷毀
128     if (soundID) {
129         AudioServicesDisposeSystemSoundID(soundID);
130         
131         //2.1銷毀後,從字典中移除
132         [[self soundIDs]removeObjectForKey:filename];
133     }
134 }
135 @end      

四、在音樂播放控制器中的代碼處理

YYPlayingViewController.m檔案

1 //
  2 //  YYPlayingViewController.m
  3 //
  4 
  5 #import "YYPlayingViewController.h"
  6 #import "YYMusicTool.h"
  7 #import "YYMusicModel.h"
  8 #import "YYAudioTool.h"
  9 
 10 @interface YYPlayingViewController ()
 11 @property (weak, nonatomic) IBOutlet UIImageView *iconView;
 12 @property (weak, nonatomic) IBOutlet UILabel *songLabel;
 13 @property (weak, nonatomic) IBOutlet UILabel *singerLabel;
 14 @property (weak, nonatomic) IBOutlet UILabel *durationLabel;
 15 @property(nonatomic,strong)YYMusicModel *playingMusic;
 16 - (IBAction)exit;
 17 
 18 @end
 19 
 20 @implementation YYPlayingViewController
 21 #pragma mark-公共方法
 22 -(void)show
 23 {
 24     //1.禁用整個app的點選事件
 25     UIWindow *window=[UIApplication sharedApplication].keyWindow;
 26     window.userInteractionEnabled=NO;
 27     
 28     //2.添加播放界面
 29     //設定View的大小為覆寫整個視窗
 30     self.view.frame=window.bounds;
 31     //設定view顯示
 32     self.view.hidden=NO;
 33     //把View添加到視窗上
 34     [window addSubview:self.view];
 35     
 36     //3.檢測是否換了歌曲
 37     if (self.playingMusic!=[YYMusicTool playingMusic]) {
 38         [self RresetPlayingMusic];
 39     }
 40     
 41     //4.使用動畫讓View顯示
 42     self.view.y=self.view.height;
 43     [UIView animateWithDuration:0.25 animations:^{
 44         self.view.y=0;
 45     } completion:^(BOOL finished) {
 46         
 47         //設定音樂資料
 48         [self starPlayingMusic];
 49         window.userInteractionEnabled=YES;
 50     }];
 51 }
 52 #pragma mark-私有方法
 53 //重置正在播放的音樂
 54 -(void)RresetPlayingMusic
 55 {
 56     //1.重置界面資料
 57     self.iconView.image=[UIImage imageNamed:@"play_cover_pic_bg"];
 58     self.songLabel.text=nil;
 59     self.singerLabel.text=nil;
 60     
 61     //2.停止播放
 62     [YYAudioTool stopMusic:self.playingMusic.filename];
 63 }
 64 //開始播放音樂資料
 65 -(void)starPlayingMusic
 66 {
 67     //1.設定界面資料
 68     
 69     //取出目前正在播放的音樂
 70 //    YYMusicModel *playingMusic=[YYMusicTool playingMusic];
 71     
 72     //如果目前播放的音樂就是傳入的音樂,那麼就直接傳回
 73     if (self.playingMusic==[YYMusicTool playingMusic]) return;
 74     //存取音樂
 75     self.playingMusic=[YYMusicTool playingMusic];
 76     self.iconView.image=[UIImage imageNamed:self.playingMusic.icon];
 77     self.songLabel.text=self.playingMusic.name;
 78     self.singerLabel.text=self.playingMusic.singer;
 79     
 80     //2.開始播放
 81     [YYAudioTool playMusic:self.playingMusic.filename];
 82     
 83 }
 84 
 85 #pragma mark-内部的按鈕監聽方法
 86 //傳回按鈕
 87 - (IBAction)exit {
 88     //1.禁用整個app的點選事件
 89     UIWindow *window=[UIApplication sharedApplication].keyWindow;
 90     window.userInteractionEnabled=NO;
 91     
 92     //2.動畫隐藏View
 93     [UIView animateWithDuration:0.25 animations:^{
 94         self.view.y=window.height;
 95     } completion:^(BOOL finished) {
 96         window.userInteractionEnabled=YES;
 97         //設定view隐藏能夠節省一些性能
 98         self.view.hidden=YES;
 99     }];
100 }
101 @end      

注意:先讓使用者看到界面上的所有東西後,再開始播放歌曲。

提示:一般的播放器需要做一個重置的操作。

  當從一首歌切換到另外一首時,應該先把上一首的資訊删除,是以在show動畫顯示之前,應該檢測是否換了歌曲,如果換了歌曲,則應該做一次重置操作。

 實作效果(能夠順利的切換和播放歌曲,下面是界面顯示):

iOS開發拓展篇—音頻處理(音樂播放器3)
iOS開發拓展篇—音頻處理(音樂播放器3)
iOS開發拓展篇—音頻處理(音樂播放器3)

五、補充代碼

YYMusicsViewController.m檔案

1 //
 2 //  YYMusicsViewController.m
 3 //
 4 
 5 #import "YYMusicsViewController.h"
 6 #import "YYMusicModel.h"
 7 #import "MJExtension.h"
 8 #import "YYMusicCell.h"
 9 #import "YYPlayingViewController.h"
10 #import "YYMusicTool.h"
11 
12 @interface YYMusicsViewController ()
13 
14 @property(nonatomic,strong)YYPlayingViewController *playingViewController;
15 @end
16 
17 @implementation YYMusicsViewController
18 #pragma mark-懶加載
19 
20 -(YYPlayingViewController *)playingViewController
21 {
22     if (_playingViewController==nil) {
23         _playingViewController=[[YYPlayingViewController alloc]init];
24     }
25     return _playingViewController;
26 }
27 
28 - (void)viewDidLoad
29 {
30     [super viewDidLoad];
31 }
32 
33 #pragma mark - Table view data source
34 /**
35  *一共多少組
36  */
37 -(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
38 {
39     return 1;
40 }
41 /**
42  *每組多少行
43  */
44 -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
45 {
46     return [YYMusicTool musics].count;
47 }
48 /**
49  *每組每行的cell
50  */
51 -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
52 {
53     YYMusicCell *cell=[YYMusicCell cellWithTableView:tableView];
54     cell.music=[YYMusicTool musics][indexPath.row];
55     return cell;
56 }
57 /**
58  *  設定每個cell的高度
59  */
60 -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
61 {
62     return 70;
63 }
64 
65 /**
66  *  cell的點選事件
67  */
68 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
69 {
70     //1.取消選中被點選的這行
71     [tableView deselectRowAtIndexPath:indexPath animated:YES];
72     
73     //2.設定正在播放的歌曲
74     [YYMusicTool setPlayingMusic:[YYMusicTool musics][indexPath.row]];
75 
76     //調用公共方法
77     [self.playingViewController show];
78     
79 //    //執行segue跳轉
80 //    [self performSegueWithIdentifier:@"music2playing" sender:nil];
81 }
82 @end