• iOS开发拓展篇—音频处理(音乐播放器4)


    iOS开发拓展篇—音频处理(音乐播放器4)

    说明:该文主要介绍音乐播放器实现过程中的一些细节控制。

    实现的效果:

    一、完整的代码

    YYPlayingViewController.m文件

    //
    //  YYPlayingViewController.m
    //  20-音频处理(音乐播放器1)
    //
    //  Created by apple on 14-8-13.
    //  Copyright (c) 2014年 yangyong. All rights reserved.
    //
    
    #import "YYPlayingViewController.h"
    #import "YYMusicTool.h"
    #import "YYMusicModel.h"
    #import "YYAudioTool.h"
    
    @interface YYPlayingViewController ()
    //进度条
    @property (weak, nonatomic) IBOutlet UIView *progressView;
    //滑块
    @property (weak, nonatomic) IBOutlet UIButton *slider;
    @property (weak, nonatomic) IBOutlet UIImageView *iconView;
    @property (weak, nonatomic) IBOutlet UILabel *songLabel;
    @property (weak, nonatomic) IBOutlet UILabel *singerLabel;
    //当前播放的音乐的时长
    @property (weak, nonatomic) IBOutlet UILabel *durationLabel;
    //正在播放的音乐
    @property(nonatomic,strong)YYMusicModel *playingMusic;
    //音乐播放器对象
    @property(nonatomic,strong)AVAudioPlayer *player;
    //定时器
    @property(nonatomic,strong)NSTimer *CurrentTimeTimer;
    - (IBAction)exit;
    - (IBAction)tapProgressBg:(UITapGestureRecognizer *)sender;
    - (IBAction)panSlider:(UIPanGestureRecognizer *)sender;
    
    @end
    
    @implementation YYPlayingViewController
    #pragma mark-公共方法
    -(void)show
    {
        //1.禁用整个app的点击事件
        UIWindow *window=[UIApplication sharedApplication].keyWindow;
        window.userInteractionEnabled=NO;
        
        //2.添加播放界面
        //设置View的大小为覆盖整个窗口
        self.view.frame=window.bounds;
        //设置view显示
        self.view.hidden=NO;
        //把View添加到窗口上
        [window addSubview:self.view];
        
        //3.检测是否换了歌曲
        if (self.playingMusic!=[YYMusicTool playingMusic]) {
            [self RresetPlayingMusic];
        }
        
        //4.使用动画让View显示
        self.view.y=self.view.height;
        [UIView animateWithDuration:0.25 animations:^{
            self.view.y=0;
        } completion:^(BOOL finished) {
            
            //设置音乐数据
            [self starPlayingMusic];
            window.userInteractionEnabled=YES;
        }];
    }
    
    
    #pragma mark-私有方法
    //重置正在播放的音乐
    -(void)RresetPlayingMusic
    {
        //1.重置界面数据
        self.iconView.image=[UIImage imageNamed:@"play_cover_pic_bg"];
        self.songLabel.text=nil;
        self.singerLabel.text=nil;
        
        //2.停止播放
        [YYAudioTool stopMusic:self.playingMusic.filename];
        //把播放器进行清空
        self.player=nil;
    
        //3.停止定时器
        [self removeCurrentTime];
    }
    //开始播放音乐数据
    -(void)starPlayingMusic
    {
        //1.设置界面数据
        
        //如果当前播放的音乐就是传入的音乐,那么就直接返回
        if (self.playingMusic==[YYMusicTool playingMusic])
        {
            //把定时器加进去
            [self addCurrentTimeTimer];
            return;
        }
        //存取音乐
        self.playingMusic=[YYMusicTool playingMusic];
        self.iconView.image=[UIImage imageNamed:self.playingMusic.icon];
        self.songLabel.text=self.playingMusic.name;
        self.singerLabel.text=self.playingMusic.singer;
        
        //2.开始播放
        self.player = [YYAudioTool playMusic:self.playingMusic.filename];
    
        //3.设置时长
        //self.player.duration;  播放器正在播放的音乐文件的时间长度
        self.durationLabel.text=[self strWithTime:self.player.duration];
        
        //4.添加定时器
        [self addCurrentTimeTimer];
        
    }
    
    /**
     *把时间长度-->时间字符串
     */
    -(NSString *)strWithTime:(NSTimeInterval)time
    {
        int minute=time / 60;
        int second=(int)time % 60;
        return [NSString stringWithFormat:@"%d:%d",minute,second];
    }
    
    #pragma mark-定时器控制
    /**
     *  添加一个定时*/
    -(void)addCurrentTimeTimer
    {
        //提前先调用一次进度更新,以保证定时器的工作时及时的
        [self updateCurrentTime];
        
        //创建一个定时器,每一秒钟调用一次
        self.CurrentTimeTimer=[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateCurrentTime) userInfo:nil repeats:YES];
        //把定时器加入到运行时中
        [[NSRunLoop mainRunLoop]addTimer:self.CurrentTimeTimer forMode:NSRunLoopCommonModes];
    }
    /**
     *移除一个定时器
     */
    -(void)removeCurrentTime
    {
        [self.CurrentTimeTimer invalidate];
        
        //把定时器清空
        self.CurrentTimeTimer=nil;
    }
    
    /**
     *  更新播放进度
     */
    -(void)updateCurrentTime
    {
        //1.计算进度值
        double progress=self.player.currentTime/self.player.duration;
        
        //2.计算滑块的x值
        // 滑块的最大的x值
        CGFloat sliderMaxX=self.view.width-self.slider.width;
        self.slider.x=sliderMaxX*progress;
        //设置滑块上的当前播放时间
        [self.slider setTitle:[self strWithTime:self.player.currentTime] forState:UIControlStateNormal];
        
        //3.设置进度条的宽度
        self.progressView.width=self.slider.center.x;
        
    }
    
    #pragma mark-内部的按钮监听方法
    //返回按钮
    - (IBAction)exit {
        
        //0.移除定时器
        [self  removeCurrentTime];
        //1.禁用整个app的点击事件
        UIWindow *window=[UIApplication sharedApplication].keyWindow;
        window.userInteractionEnabled=NO;
        
        //2.动画隐藏View
        [UIView animateWithDuration:0.25 animations:^{
            self.view.y=window.height;
        } completion:^(BOOL finished) {
            window.userInteractionEnabled=YES;
            //设置view隐藏能够节省一些性能
            self.view.hidden=YES;
        }];
    }
    
    /**
     *点击了进度条
     */
    - (IBAction)tapProgressBg:(UITapGestureRecognizer *)sender {
        //获取当前单击的点
        CGPoint point=[sender locationInView:sender.view];
        //切换歌曲的当前播放时间
        self.player.currentTime=(point.x/sender.view.width)*self.player.duration;
        //更新播放进度
        [self updateCurrentTime];
    }
    
    - (IBAction)panSlider:(UIPanGestureRecognizer *)sender {
        
        //1.获得挪动的距离
        CGPoint t=[sender translationInView:sender.view];
        //把挪动清零
        [sender setTranslation:CGPointZero inView:sender.view];
        
        //2.控制滑块和进度条的frame
        self.slider.x+=t.x;
        //设置进度条的宽度
        self.progressView.width=self.slider.center.x;
        
        //3.设置时间值
        CGFloat sliderMaxX=self.view.width-self.slider.width;
        double progress=self.slider.x/sliderMaxX;
        //当前的时间值=音乐的时长*当前的进度值
        NSTimeInterval time=self.player.duration*progress;
        [self .slider setTitle:[self strWithTime:time] forState:UIControlStateNormal];
        
        //4.如果开始拖动,那么就停止定时器
        if (sender.state==UIGestureRecognizerStateBegan) {
            //停止定时器
            [self removeCurrentTime];
        }else if(sender.state==UIGestureRecognizerStateEnded)
        {
            //设置播放器播放的时间
            self.player.currentTime=time;
            //开启定时器
            [self addCurrentTimeTimer];
        }
    }
    @end

    二、代码说明(一)

      调整开始播放音乐按钮,让其返回一个音乐播放器,而非BOOL型的。

    /**
     *播放音乐
     */
    +(AVAudioPlayer *)playMusic:(NSString *)filename
    {
        if (!filename) return nil;//如果没有传入文件名,那么直接返回
        //1.取出对应的播放器
        AVAudioPlayer *player=[self musicPlayers][filename];
        
        //2.如果播放器没有创建,那么就进行初始化
        if (!player) {
            //2.1音频文件的URL
            NSURL *url=[[NSBundle mainBundle]URLForResource:filename withExtension:nil];
            if (!url) return nil;//如果url为空,那么直接返回
            
            //2.2创建播放器
            player=[[AVAudioPlayer alloc]initWithContentsOfURL:url error:nil];
            
            //2.3缓冲
            if (![player prepareToPlay]) return nil;//如果缓冲失败,那么就直接返回
            
            //2.4存入字典
            [self musicPlayers][filename]=player;
        }
        
        //3.播放
        if (![player isPlaying]) {
            //如果当前没处于播放状态,那么就播放
             [player play];
        }
    
        return player;//正在播放,那么就返回YES
    }

    三、代码说明(二)

      把时间转换为时间字符串的方法:

    /**
     *把时间长度-->时间字符串
     */
    -(NSString *)strWithTime:(NSTimeInterval)time
    {
        int minute=time / 60;
        int second=(int)time % 60;
        return [NSString stringWithFormat:@"%d:%d",minute,second];
    }

    四、代码说明(三)

      说明:进度控制

      监听当前的播放,使用一个定时器,不断的监听当前是第几秒。

      关于定时器的处理:这里使用了三个方法,分别是添加定时器,移除定时器,和更新播放进度。

    注意细节

    (1)移除定时器后,对定时器进行清空处理。

    /**
     *移除一个定时器
     */
    -(void)removeCurrentTime
    {
        [self.CurrentTimeTimer invalidate];
        
        //把定时器清空
        self.CurrentTimeTimer=nil;
    }

    2)当看不到界面的时候,停止定时器。

    (3)在开始播放音乐的方法中进行判断,如果当前播放的音乐和传入的音乐一致,那么添加定时器后直接返回。

    (4)重置播放的音乐方法中,停止定时器。

    五、代码说明(四)

      说明:点击和拖动进度条的处理

    1.点击进度条

      先添加单击的手势识别器。

      往控制器拖线:

     

    涉及的代码:

    /**
     *点击了进度条
     */
    - (IBAction)tapProgressBg:(UITapGestureRecognizer *)sender {
        //获取当前单击的点
        CGPoint point=[sender locationInView:sender.view];
        //切换歌曲的当前播放时间
        self.player.currentTime=(point.x/sender.view.width)*self.player.duration;
        //更新播放进度
        [self updateCurrentTime];
    }

    2.拖拽进度条

      先添加拖拽手势识别器

    往控制器拖线

     

    涉及的代码:

    /**
     *拖动滑块
     */
    - (IBAction)panSlider:(UIPanGestureRecognizer *)sender {
        
        //1.获得挪动的距离
        CGPoint t=[sender translationInView:sender.view];
        //把挪动清零
        [sender setTranslation:CGPointZero inView:sender.view];
        
        //2.控制滑块和进度条的frame
        self.slider.x+=t.x;
        //设置进度条的宽度
        self.progressView.width=self.slider.center.x;
        
        //3.设置时间值
        CGFloat sliderMaxX=self.view.width-self.slider.width;
        double progress=self.slider.x/sliderMaxX;
        //当前的时间值=音乐的时长*当前的进度值
        NSTimeInterval time=self.player.duration*progress;
        [self .slider setTitle:[self strWithTime:time] forState:UIControlStateNormal];
        
        //4.如果开始拖动,那么就停止定时器
        if (sender.state==UIGestureRecognizerStateBegan) {
            //停止定时器
            [self removeCurrentTime];
        }else if(sender.state==UIGestureRecognizerStateEnded)
        {
            //设置播放器播放的时间
            self.player.currentTime=time;
            //开启定时器
            [self addCurrentTimeTimer];
        }
    }
  • 相关阅读:
    SQL慢查询安装过程
    grafana + influxdb + telegraf , 构建性能监控平台
    JDK安装、java环境配置
    多源最短路Floyd 算法————matlab实现
    单源最短路Dijkstra算法——matlab实现
    Layout基本属性总结
    Scrollview中嵌套ListView(自定义组件解决)
    SQL 一列拆分多行
    C# 在异步中使用HttpWebRequest出现的“正在终止线程”错误的解决方案
    sqlserver中分区函数 partition by与 group by 区别 删除关键字段重复列
  • 原文地址:https://www.cnblogs.com/yipingios/p/5566770.html
Copyright © 2020-2023  润新知