• 播放音乐首先,创建一个项目MediaDemo。 导入所需要的包import AVFoundation import MediaPlayer定义一个播放器的变量var audioPlayer: AVAudioPlayer?添加查找播放源的代码 let path = NSBundle.mainBundle()....

    播放音乐

    首先,创建一个项目MediaDemo。
    导入所需要的包

    import AVFoundation
    import MediaPlayer

    定义一个播放器的变量

    var audioPlayer: AVAudioPlayer?

    添加查找播放源的代码

     let path = NSBundle.mainBundle().pathForResource("music", ofType: "mp3")
            let pathURL=NSURL(fileURLWithPath: path!)
    
            do {
                audioPlayer = try AVAudioPlayer(contentsOfURL: pathURL)
            } catch {
                audioPlayer = nil
            }

    缓冲音乐

    audioPlayer?.prepareToPlay()

    在页面上加创建两个按钮play,pause(没做约束)

     let playbtn = UIButton()
            playbtn.frame = CGRectMake(60 , 200, 100, 40)
            playbtn.backgroundColor = UIColor.cyanColor()
            playbtn.setTitle("play", forState: .Normal)
            playbtn.setTitleColor(UIColor.whiteColor(), forState: .Normal)
    
    
            let pausebtn = UIButton(frame: CGRectMake(180 , 200, 100, 40) )
            pausebtn.setTitle("pause", forState: .Normal)
            pausebtn.setTitleColor(UIColor.whiteColor(), forState: .Normal)
            pausebtn.backgroundColor = UIColor.cyanColor()
            self.view.addSubview(playbtn)
            self.view.addSubview(pausebtn)

    给两个按钮加上监听,实现播放和暂停功能。

     playbtn.addTarget(self, action: "play", forControlEvents: .TouchUpInside)
     pausebtn.addTarget(self, action: "pause", forControlEvents: .TouchUpInside)
        func play(){
            audioPlayer.play()
        }
    
        func pause(){
            audioPlayer.pause()
        }

    这样音乐就可以播放啦。

    后台播放及控制

    首先在info.list中需要添加下面这条信息
    在info.list中添加

    在viewload中添加,这句话要添加在audioPlayer.play()之前

           do {
                try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
                try AVAudioSession.sharedInstance().setActive(true)
            } catch {
    
            }

    下面这句话是为了在锁屏界面显示信息,及控制面板

    UIApplication.sharedApplication().beginReceivingRemoteControlEvents()

    设置锁屏界面

     func setLockView(){
            MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [
    // 歌曲名称
    MPMediaItemPropertyTitle:"第一夫人",
    // 演唱者
    MPMediaItemPropertyArtist:"张杰",
    // 锁屏图片
    MPMediaItemPropertyArtwork:MPMediaItemArtwork(image: UIImage(named: "img.jpeg")!),
    // 
    MPNowPlayingInfoPropertyPlaybackRate:1.0,
    // 总时长            MPMediaItemPropertyPlaybackDuration:audioPlayer.duration,
    // 当前时间        MPNowPlayingInfoPropertyElapsedPlaybackTime:audioPlayer.currentTime
            ]
        }

    viewDidLoad中调用代码

    self.setLockView()

    添加锁屏远程控制音乐播放代码

    
        override func remoteControlReceivedWithEvent(event: UIEvent?) {
            switch event!.subtype {
            case .RemoteControlPlay:  // play按钮
                audioPlayer.play()
            case .RemoteControlPause:  // pause按钮
                audioPlayer.pause()
            case .RemoteControlNextTrack:  // next
                // ▶▶ 
                break
            case .RemoteControlPreviousTrack:  // previous
                // ◀◀ 
                break
            default:
                break
            }
        }

    源代码:
    https://github.com/iancui/MediaDemo.git

    展开全文
  • 作为一个刚入行的菜鸡终于鼓起勇气写博客并发布出来,本周课程讲到了ios多媒体应用关于音频播放这部分(本菜还在读大学走的移动端ios方向= =),课下作业老师让做个基于AVAudioPlayer的音乐播放器,问题不大,比较...

    2017.05.22 17:46* 字数 1585 阅读 5095评论 0喜欢 8赞赏 2

    学习ios以来差不多接近两个月了,作为一个刚入行的菜鸡终于鼓起勇气写博客并发布出来,本周课程讲到了ios多媒体应用关于音频播放这部分(本菜还在读大学走的移动端ios方向= =),课下作业老师让做个基于AVAudioPlayer的音乐播放器,问题不大,比较简单,想给自己加点难度,最近swift这么火,干脆用swift写个吧!于是逼着自己一点点的边做边啃swift语法,花了差不多3天的时间,粗制滥造 = =!的搞出来个简单版的音乐播放器(希望大家多多指点,供跟我一样刚入行的朋友互相交流,互相学习),如图:

     

     

    效果图.gif

    1.界面

    最初想的是该怎么入门,毕竟swift认识我,我不认识它啊!还是去世界上最大的同性交友网站GitHub上去看看有没有关于swift播放器的demo把,从学习别人的代码来入门,结果转了一大圈都没有找到合适的,不过发现了个有四百个star的提供炫酷手势界面壳子的项目,但仅仅是个壳子而已,好吧我的界面就用它了,对了说到界面,他界面用的是StoryBoard还用到了个新控件叫ContainerView,ContainerView是用来在一个视图控制器上添加子视图控制器的,他这样做的好处就是可以让迷你播放栏一只处于界面最上方,点击tabbar切换界面的时候,只需要将ContainerView里的子视图控制器更换即可。具体用法大家可以自行简书。

    2.获取音乐及相关信息,封装音乐播放类

    界面有了,使用AVAudioPlayer根据本地音乐的路径进行音乐播放,在这里我封装了个方法用来获取本地文件夹myMusic里所有歌曲路径以及作家,封面,歌曲名等。为了做出上一曲下一曲功能我将每首歌曲都编了号。

    static func getALL()->Array<Music>{

            if musicArry == nil {

                var musicArryList=Array<Music>()

                var fileArry:[String]?

                var num:Int=0;

                let path=Bundle.main.path(forResource:"mymusic", ofType: nil)

                do{

                    try fileArry=FileManager.default.contentsOfDirectory(atPath: path!)

                }

                catch{

                    print("error")

                }

                for n in fileArry! {

                    let singlePath=path!+"/"+n

                    let avURLAsset = AVURLAsset(url: URL.init(fileURLWithPath: singlePath))

                    let musicModel:Music = Music()

                    

                    for i in avURLAsset.availableMetadataFormats {

                        

                        for j in avURLAsset.metadata(forFormat: i) {

                            //歌曲名

                            if j.commonKey == "title"{

                                musicModel.musicName = j.value as? String

                                

                            }//封面图片

                            if j.commonKey == "artwork"{

                                musicModel.musicimg=j.value as? Data// 这里是个坑坑T T

                            }//专辑名

                            if j.commonKey == "albumName"{

                                musicModel.musicAlbum=j.value as? String

                            }

                            //歌手

                            if j.commonKey == "artist"{

                                musicModel.musicAuthor=j.value as? String

                            }

                        }

                        

                    }

                    musicModel.musicURL=URL.init(fileURLWithPath: singlePath)

                    num += 1

                    musicModel.musicNum=num;

                    musicArryList.append(musicModel)

                }

                musicArry=musicArryList

                return musicArry!

     

            }else{

                return musicArry!

            }

        }

    一个音乐播放器每次播放都只能放一首歌,于是我将其封装成了一个AudioPlayer单例类,其实也不能算单例,应该叫工具类比较合适。

    import UIKit

    import AVFoundation

    final class AudioPlayer: NSObject {

        private static var instance: AVAudioPlayer? = nil //static 直到被销毁 全局存在

        private static var activeMusic:Music?=nil

        private static var isRandomPlay=false

        static func share(model:Music) -> Bool {

                do{

                    try instance = AVAudioPlayer(contentsOf: model.musicURL!)

                }

                catch{

                    instance=nil;

                    print("error")

                    return false;

                }

                instance?.play()

                activeMusic=model

                return true

        }

        //停止

        static func stop(){

            instance?.stop()

        }

        //播放

        static func play()->Bool{

            if (instance?.isPlaying)! {

                instance?.pause()

                return false

            }else{

                instance?.play()

                return true

            }

        }

        //暂停

        static func pause(){

            instance?.pause()

        }

        //下一曲

        static func nextsong( num:Int)->Bool{

            var num = num

            var musicArry:Array<Music>!

            musicArry=Music.getALL()

            if isRandomPlay{

               num = Int(arc4random_uniform(UInt32(musicArry.count-1)))

            }

            if(share(model: musicArry[num])){

                return true

            }else{

                return false

            }

        }

        //上一曲

        static func prevsong(num:Int)->Bool{

            var num = num

            var musicArry:Array<Music>!

            musicArry=Music.getALL()

            if isRandomPlay{

                num = Int(arc4random_uniform(UInt32(musicArry.count-1)))

            }

            if(share(model: musicArry[num])){

                return true

            }else{

                return false

            }

        }

        //声音控制

        static func voice(num:Float){

            instance?.volume=num

        }

        //进度条相关

        static func progress()->Double{

            return (instance?.currentTime)!/(instance?.duration)!

        }

        static func musicDuration()->Double{

            return (instance?.duration)!

        }

        

        static func currentTime()->Double{

            return (instance?.currentTime)!

        }

     

        //当前播放的音乐

        static func activeSong()->Music?{

            return activeMusic

        }

        //是否在播放音乐

        static func isPlaying()->Bool{

            return (instance?.isPlaying)!

        }

        //随机播放

        static func musicRandomPlay()->Bool{

            if  isRandomPlay==false{

                isRandomPlay=true

                return isRandomPlay

            }else{

                isRandomPlay=false

                return isRandomPlay

            }

        }

        

    }

    3.音乐播放

    所有歌曲及其信息都能拿到了,展示在tableView上我就不用多说,播放音乐也只需要在tableView的相应点击方法里使用AudioPlayer类的share方法将对应的歌曲model传入即可,这里有个如何让miniplayer展示所点击歌曲的问题,我是将tableView放进ContainerView,tableView相当于主视图的子视图,而miniplayer是在主视图里,这里就要用到子视图向父视图回传值的方法,在这里我是采用的闭包回传,其实也不需要传什么值,只需要让主视图知道子视图被点击了即可,因为我可以通过AudioPlayer类来得到当前播放的歌曲信息。swift里的闭包传值在语法上和oc的区别也是很大,花了我不少时间- -

    子视图里:

    var musicPlayByTableViewCell: ((Int) -> Void)//声明

        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

            musicPlayByTableViewCell?(indexPath.row)

            tableView.deselectRow(at: indexPath, animated: true)//取消被选中状态

        }

    主视图里:

    self.musicTalbleVC.musicPlayByTableViewCell={

              [weak self]  musicNum in self?.musicModel=self?.musicArry[musicNum]

                self?.nowNum=musicNum

                self?.miniPlayerWork()

            }

    3.1界面功能及相关细节

    音乐播放时的界面也就是几个button和常见的视图控件,说说进度条,AVAudioPlayer类提供了当前进度,以及总时长,所以我是用定时器每一秒获取一次当前进度的方法,再当前进度除以总进度使用即可,上下曲因为我在获取所有歌曲时就将每首歌曲编了号,上下曲就只需要将编号加一或减一就可以了,包括随机播放也只需要使用随机函数随机总歌曲编号就行了,还有些界面上的细节,在详细歌曲播放界面点击了暂停,回到主视图时miniplayer的图标及相关信息也应该与其同步哦。tableView列表上也将当前播放的歌曲进行了高亮展示(加了歌活跃标记),在这里,我在musicmodel里添加了IsActive属性,并在tableView的Cell里进行了判断,如果cell展示的歌曲是当前正在播放的歌曲isActive为true展示活跃标记。还有音乐播放完毕后需要自动播放下一曲哦,这里我是用的定时器控件,读取歌曲进度进度完了就播放下一曲(相当点击了一次下一曲按钮)

    3.12tabBar控制界面

    在storyBoard上是直接拖的TabBar,和直接用tabBarController大不一样,点击不同的图标响应不同的事件ContainerView里的视图控制器也就相应的变化,关于如何改变ContainerView里的视图控制器我也是找了好久,还比较复杂,但确实ContainerView很好用啊!

    ContainerView:更改视图控制器

        func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {

            if item.tag == 1 {

                let newController = self.musicTalbleVC!

                let oldController = childViewControllers.last!

                if newController != oldController {

                  //  self.setStatusBarBackgroundColor(color: UIColor.white)

                    oldController.willMove(toParentViewController: nil)

                    addChildViewController(newController)

                    newController.view.frame = oldController.view.frame

                    //isAnimating = true

                    transition(from: oldController, to: newController, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromLeft, animations: nil, completion: { (finished) -> Void in

                        oldController.removeFromParentViewController()

                        newController.didMove(toParentViewController: self)                //self.isAnimating = false

                    })

                }

            }else{

                let newController = self.moreVC!

                let oldController = childViewControllers.last!

                 if newController != oldController {

                    self.setStatusBarBackgroundColor(color: UIColor.clear)

                    oldController.willMove(toParentViewController: nil)

                    addChildViewController(newController)

                    newController.view.frame = oldController.view.frame

                    transition(from: oldController, to: newController, duration: 0.5, options: UIViewAnimationOptions.transitionFlipFromRight, animations: nil, completion: { (finished) -> Void in

                        oldController.removeFromParentViewController()

                        newController.didMove(toParentViewController: self)                //self.isAnimating = false

                    })

                }

            }

        }

    3.13音乐的后台播放以及锁屏和上拉栏展示音乐信息

    音乐的后台播放,因为我还从为接触过应用后台运行这一块,所以我也只是参照网上的代码将功能做出来了而已,还有不完善的地方,后台播放中断(如打电话)后不会恢复播放。上拉栏展示音乐信息以及之中的按钮触发方法,都是系统封装好了的 直接用就是了。

    话说很多警告呀!

        func setBackground() {

            //大标题 - 小标题  - 歌曲总时长 - 歌曲当前播放时长 - 封面

            self.musicModel=AudioPlayer.activeSong()

            var settings = [MPMediaItemPropertyTitle: self.musicModel?.musicName,

                            MPMediaItemPropertyArtist: self.musicModel?.musicAuthor ,

                            MPMediaItemPropertyPlaybackDuration: "\(AudioPlayer.musicDuration())",

                MPNowPlayingInfoPropertyElapsedPlaybackTime: "\(AudioPlayer.currentTime())",MPMediaItemPropertyArtwork: MPMediaItemArtwork.init(image: UIImage (data: (self.musicModel?.musicimg)!)!)] as [String : Any]

            MPNowPlayingInfoCenter.default().setValue(settings, forKey: "nowPlayingInfo")

            if AudioPlayer.progress() > 0.99 { // 自动播放下一曲 ()后台

                    self.nowNum=self.nowNum+1

                if self.nowNum>=self.musicArry.count{

                    self.nowNum=0

                }

                if AudioPlayer.nextsong(num: self.nowNum){

                    refreashView()

                }

            }

        }

    4.最后

    这是刚入行的菜鸡的第一篇博客,肯定有很多不妥当的地方,希望大家见谅,项目也没做多久,对swift的理解还很浅,肯定不如大大们的法眼,我也只希望通过写这篇博客,总结一下我学到的知识,分享出来,大家互相交流学习。

    附源码 https://github.com/calvinWen/SwiftMusicPlayer

     

    展开全文
  • swift 音乐播放器项目-《lxy的杰伦情歌》开发实战演练

    最近准备将项目转化为OC与swift混合开发,试着写一个swift音乐播放器的demo,体会到了swift相对OC的优势所在,废话不多说,先上效果图:
    这里写图片描述
    ps:身为杰伦的铁粉,demo的主题必须跟杰伦有关,哈哈!而且自我感觉我有转型UI的天赋,有木有?

    一、导入OC文件
    创建好swift项目之后,导入OC工具类文件,Xcode会自动生成桥接文件
    这里写图片描述

    打开这个文件,在开头导入OC工具类的头文件,就可以调用OC工具类了

    //
    //  Use this file to import your target's public headers that you would like to expose to Swift.
    //
    #import "UIView+Frame.h"

    二、界面绘制
    这里方便起见,没有考虑性能问题,只是简单的demo,所以直接创建十个音乐按钮,加上一个播放按钮和一个暂停按钮。十个乐曲按钮放在一个UIScrollView上面。而且播放的音乐是本地音乐,首先在viewDidLoad前创建全局变量,代码如下:

        var btnIndex = 0
        let arrMusicName = ["蒲公英的约定","窃爱","退后","告白气球","阳光宅男","甜甜的","彩虹","心雨","哪里都是你","说好的幸福呢"]
        let btnPlay:UIButton = UIButton(type:.System)
        let btnPause:UIButton = UIButton(type:.System)
    
        override func viewDidLoad() {

    界面绘制代码如下:

    let SCREEN_W = UIScreen.mainScreen().bounds.size.width
            let SCREEN_H = UIScreen.mainScreen().bounds.size.height
    
    
    
            let ivBack:UIImageView = UIImageView(frame:CGRect(x:0, y:20, width:SCREEN_W, height:SCREEN_H))
            ivBack.image = UIImage(named:"aa.jpg")
            self.view.addSubview(ivBack)
    
    
            let lbTitle:UILabel = UILabel(frame:CGRect(x:0, y:20, width:SCREEN_W, height:44))
            lbTitle.text = "LXY的杰伦情歌"
            lbTitle.textColor = UIColor.whiteColor()
            lbTitle.textAlignment = NSTextAlignment.Center
            lbTitle.backgroundColor = UIColor.blackColor()
            lbTitle.alpha = 0.2
            self.view.addSubview(lbTitle)
    
            let scv:UIScrollView = UIScrollView(frame:CGRect(x:0, y:64, width:SCREEN_W, height:SCREEN_H - 74 - 44))
            scv.backgroundColor = UIColor.clearColor()
            self.view.addSubview(scv)
    
            var boom:Int = 0
            for st in arrMusicName {
    
                print(st)
                if let index = arrMusicName.indexOf(st) {
    
                    let btn:UIButton = UIButton(type:.System)
    
                    btn.backgroundColor = UIColor.whiteColor()
                    btn.alpha = 0.5
                    btn.setTitle(st, forState: UIControlState.Normal)
                    btn.setTitleColor(UIColor.blackColor(), forState: UIControlState.Normal)
                    btn.titleLabel?.font = UIFont.systemFontOfSize(16)
                    btn.layer.masksToBounds = true
                    btn.layer.shadowOffset = CGSizeMake(0, 10)
                    btn.layer.shadowColor = UIColor.grayColor().CGColor
                    btn.layer.shadowOpacity = 1
                    btn.layer.shadowRadius = 10
                    btn.layer.cornerRadius = 6
                    btn.tag = index
    
                    //传递触摸对象(即点击的按钮),需要在定义action参数时,方法名称后面带上冒号
                    btn.addTarget(self, action:#selector(play(_:)), forControlEvents:.TouchUpInside)
    
                    if index%2 == 0 {
                        btn.frame = CGRectMake(SCREEN_W/9,  20 + (SCREEN_W/3 + SCREEN_W/9)*CGFloat(index/2),SCREEN_W/3,SCREEN_W/3)
                    }else {
    
                        btn.frame = CGRectMake(5*SCREEN_W/9,  20 + (SCREEN_W/3 + SCREEN_W/9)*CGFloat(index/2),SCREEN_W/3,SCREEN_W/3)
    
                    }
                    scv.addSubview(btn)
                    boom = Int(btn.bottom)
    
    
                }
            }
    
            scv.contentSize = CGSizeMake(SCREEN_W,CGFloat(boom + 20))
    
    
            btnPlay.frame = CGRectMake(0, scv.bottom, SCREEN_W/2 - 10, 44)
            btnPlay.backgroundColor = UIColor.whiteColor()
            btnPlay.alpha = 0.5
            btnPlay.setTitle("播放", forState: UIControlState.Normal)
            btnPlay.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
            btnPlay.titleLabel?.font = UIFont.systemFontOfSize(16)
            btnPlay.addTarget(self, action:#selector(MPMediaPlayback.play), forControlEvents:.TouchUpInside)
            btnPlay.hidden = true
            self.view.addSubview(btnPlay)
    
    
            btnPause.frame = CGRectMake(10 + SCREEN_W/2, scv.bottom, SCREEN_W/2 - 10, 44)
            btnPause.backgroundColor = UIColor.whiteColor()
            btnPause.alpha = 0.5
            btnPause.setTitle("暂停", forState: UIControlState.Normal)
            btnPause.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
            btnPause.titleLabel?.font = UIFont.systemFontOfSize(16)
            btnPause.addTarget(self, action:#selector(MPMediaPlayback.pause), forControlEvents:.TouchUpInside)
            btnPause.hidden = true
            self.view.addSubview(btnPause)
    
        }
    
    
        func play(btn: UIButton){
    
    
            btnIndex = btn.tag
            let music = arrMusicName[btn.tag]
    
            let path = NSBundle.mainBundle().pathForResource(music, ofType: "mp3")
            let pathURL=NSURL(fileURLWithPath: path!)
    
            do {
                audioPlayer = try AVAudioPlayer(contentsOfURL: pathURL)
            } catch {
                audioPlayer = nil
            }
    
            audioPlayer?.prepareToPlay()
    
            audioPlayer.play()
    
            btnPause.hidden = false
    
            btnPlay.hidden = false
    

    三、音乐播放器创建

    class AudioManager: NSObject, AVAudioPlayerDelegate {
        static let sharedManager = AudioManager()  // singleton
        var audioPlayer: AVAudioPlayer!
    }

    然后在曲剧变量里实例化播放器

    var audioPlayer = AudioManager.sharedManager.audioPlayer

    四、按钮事件绑定

    1、音乐按钮播放音乐事件

     func playWitnIndex(index: Int){
    
            let music = arrMusicName[index]
            let path = NSBundle.mainBundle().pathForResource(music, ofType: "mp3")
            let pathURL=NSURL(fileURLWithPath: path!)
    
            do {
                audioPlayer = try AVAudioPlayer(contentsOfURL: pathURL)
            } catch {
                audioPlayer = nil
            }
    
            audioPlayer?.prepareToPlay()
    
            audioPlayer.play()
    
            btnPause.hidden = false
    
            btnPlay.hidden = false
        }

    这个方法吧button带过来,通过button的tag值判断所点击的按钮

    2、播放按钮的点击事件

    func play(){
            audioPlayer.play()
        }

    3、暂停按钮的点击事件

     func pause(){
            audioPlayer.pause()
        }

    到这里,音乐播放器的基本功能就已经实现了,点击就会有音乐播放了,并且播放暂停功能都已经实现

    五、音乐后台播放的实现

    要实现后台音乐播放功能,还学要对音乐播放器设置一下

    在viewDidLoad方法中添加一下代码:

    UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
            do {
    
                try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
                try AVAudioSession.sharedInstance().setActive(true)
     } catch {
    
            }

    只添加这个还不够,需要对工程进行设置
    在info.plist添加Required background modes,具体如图:
    这里写图片描述

    这样,音乐播放器就实现了后台播放功能

    六、音乐播放器锁屏音乐播放设置

    添加setLockView方法,代码如下:

    func setLockView(){
            MPNowPlayingInfoCenter.defaultCenter().nowPlayingInfo = [
                // 歌曲名称
                MPMediaItemPropertyTitle:arrMusicName[btnIndex],
                // 演唱者
                MPMediaItemPropertyArtist:"周杰伦",
                // 锁屏图片
                MPMediaItemPropertyArtwork:MPMediaItemArtwork(image: UIImage(named: "aa.jpg")!),
                //
                MPNowPlayingInfoPropertyPlaybackRate:1.0,
                // 总时长            MPMediaItemPropertyPlaybackDuration:audioPlayer.duration,
                // 当前时间        MPNowPlayingInfoPropertyElapsedPlaybackTime:audioPlayer.currentTime
            ]
        }

    在viewDidLoad方法中调用这个方法:

      self.setLockView()

    这样就实现了锁屏界面了按钮设置

    这里写图片描述

    锁屏按钮点击事件实现需要重写remoteControlReceivedWithEvent方法,代码如下:

    override func remoteControlReceivedWithEvent(event: UIEvent?) {
            switch event!.subtype {
            case .RemoteControlPlay:  // play按钮
                audioPlayer.play()
            case .RemoteControlPause:  // pause按钮
                audioPlayer.pause()
            case .RemoteControlNextTrack:  // next
                // ▶▶
    
                self.btnIndex = self.btnIndex + 1
                self.playWitnIndex(self.btnIndex)
                self.setLockView()
    
                break
            case .RemoteControlPreviousTrack:  // previous
                // ◀◀
                if self.btnIndex>0 {
                    self.btnIndex = self.btnIndex - 1
                    self.playWitnIndex(self.btnIndex)
                    self.setLockView()
                }
                break
            default:
                break
            }
        }
    

    七、外放问题的解决

    此时,虽然功能都实现了,但是会发现音乐播放声音特别小,因为AVAudioPlayer默认是听筒播放,而不是扬声器播放
    这里需要在播放器设置里添加如下设置代码

     let session = AVAudioSession.sharedInstance()
                try session.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: AVAudioSessionCategoryOptions.DefaultToSpeaker)

    其他设置如图所示:

    这里写图片描述
    到这里,这个swift版的lxy杰伦情歌APP就大功告成了,文章最后附上项目源码(由于音乐文件太大,只保留前两首歌曲),希望大家喜欢:

    项目源码下载地址

    展开全文
  • 实现播放模式的思路: 1.通过点击按钮 弹出来一个下弹窗 可以选择播放模式 声明一个全局变量 不同的点击全局变量的值改变 全局变量默认的播放模式是列表循环 2.在音乐播放完毕的时候调用方法 根据不同的全局变量 ...

    首先我在这里讲一下我的整个播放器的思路:
    首先是一个歌曲的列表,我把数据请求放在了一个单例里面,方便以后获取每首歌曲对应的model, 我给AVPlayer的播放也放在了一个单例里面,有播放开始,停止,等方法吗,我通过单例的代理方法将当前的播放时间传给播放时的控制器,这样控制器就可以根据传过去的当前时间给界面赋值了,比如播放界面的当前时间,以及当前的歌词等.

    实现播放模式的思路:
    1.通过点击按钮 弹出来一个下弹窗 可以选择播放模式 声明一个全局变量 不同的点击全局变量的值改变 全局变量默认的播放模式是列表循环
    2.播放音乐时给播放添加计时器每隔0/1秒就要响应一次 通过代理方法传给控制器当前播放时间
    3.在控制器的代理方法中 根据传过来的时间与当前歌曲的总时间对比,如果相等说明这首歌结束了,就调用歌曲结束的方法.
    4.在音乐播放完毕的时候调用方法 根据不同的全局变量 实现不同的操作

    第一步 实现button的点击方法 通过点击不同的下弹窗的值改变全局变量,记录选择的模式

    / 模式typeButton的点击方法的实现
    - (void)actionTypeButton:(UIButton *)typeButton {
        UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请选择模式" message:nil preferredStyle:(UIAlertControllerStyleActionSheet)];
        // 添加顺序播放按钮
        UIAlertAction *serialAction = [UIAlertAction actionWithTitle:@"顺序播放" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {
              // 给定义的全局变量赋值
            self.typeCount = 0;
        }];
        // 添加随机播放按钮
        UIAlertAction *ArcAction = [UIAlertAction actionWithTitle:@"随机播放" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {
            self.typeCount = 1;
        }];
        // 添加重复播放按钮
        UIAlertAction *repeatAction = [UIAlertAction actionWithTitle:@"重复播放" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {
            self.typeCount = 2;
        }];
        [alertController addAction:serialAction];
        [alertController addAction:ArcAction];
        [alertController addAction:repeatAction];
    
        [self presentViewController:alertController animated:YES completion:nil];
    }

    第二步:给播放添加计时器每隔0/1秒就要响应一次 通过代理方法传给控制器当前播放时间

    // 开始播放
    - (void)musicPlay {
        self.isPlaying = YES; // 当前的播放状态
        [self.player play]; // AVPlayer开始播放
        self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(playingAction) userInfo:nil repeats:YES]; // 开始计时器 调用播放的响应方法
    }
    
    #pragma mark -播放过程中执行
    - (void)playingAction {
        // 取到当前播放时间的秒数
        CGFloat time = self.player.currentTime.value / self.player.currentTime.timescale;
    
        // 代理方法 将获取到的时间 传递到控制器
        if (self.delegate && [self.delegate performSelector:@selector(playingWithProgress:)]) {
             [_delegate playingWithProgress:time];
        }
    
    }

    第三步:在控制器的代理方法中 根据传过来的时间与当前歌曲的总时间对比,如果相等说明这首歌结束了,就调用歌曲结束的方法.

    #pragma mark -- 实现代理方法
    - (void)playingWithProgress:(CGFloat)progress {
      // progress 当前歌曲播放到的时间
     // self.model.duration 当前歌曲的总时间
        NSInteger second = self.model.duration / 1000;
        if (progress == second) {
            [self musicEnd];
        }
     }

    第四步:得到歌曲的进度的值 当播放完毕的时候 做不同的操作

    #pragma mark --音乐结束后 不同模式下的反应--
    - (void)musicEnd {
    // progress 当前歌曲播放到的时间
     // self.model.duration 当前歌曲的总时间
        NSInteger second = self.model.duration / 1000;
        if (progress == second) {
            switch (self.typeCount) {
                case 0:
                {
                    // 当选择列表循环时候的操作
                    // actionDownButton: 下面有方法的实现
                    [self performSelector:@selector(actionDownButton:) withObject:nil];
                    break;
                }
                case 1:
                {
                    // 当选择随机播放是的操作
                    NSInteger num = [[RootTableViewManager shareManager] getDataArrayCount];
                    self.index = arc4random() % (num + 1);
                    // 更改了index 就相当于改变了model 更改了数据  所以要刷新界面
                    [self Valuation]; // 该方法是更改界面 给AVPlayer更换playerItem(就是穿进去新的MP3的url) 播放音乐
                    break;
                }
                case 2:
                {
                    // 当选择循环播放时的操作 只要更改界面就可以了
                   [self Valuation];
                    break;
                }
    
                default:
                    break;
            }
        }
    
    }
    

    // actionDownButton:方法的实现

    // 下一首的实现方法 
    - (void)actionDownButton:(UIButton *)downButton {
        self.index ++;
        NSInteger num = [[RootTableViewManager shareManager] getDataArrayCount];
        // 当时最后一首的时候 跳到最前面
        if (self.index > num) {
            self.index = 0;
        }
        [self Valuation];
    
    }

    // 界面的赋值是根据model的 线面是model的实现

    / 重写model的get方法
    - (Model *)model {
       // 根据当前的index 选中对应index歌曲的model 成为当前播放的数据源
       odel *model =  [[RootTableViewManager shareManager] getModelAtIndex:self.index];
        return model;
    }
    
    
    展开全文
  • 主要实现的功能为:音频播放音频播放时自定义动画以及音频时间显示

    主要实现的功能为:音频播放,音频播放时自定义动画以及音频时间显示

    一、头文件和代理添加

    在.h文件中添加

    #import <AVFoundation/AVFoundation.h>
    <AVAudioPlayerDelegate,infoRecordViewDelegate//自定义播放界面的代理>

    二、在.h文件中声明

    @property (nonatomic,strong) playRecordView *playRecView;//录音
    
    @property (nonatomic,strong) UILabel *title;
    
    @property (nonatomic,strong) UIImageView *recordPhoto;
    
    @property (nonatomic,strong) UILabel *lbltime;
    
    @property (nonatomic,strong) UITapGestureRecognizer *tapPlayGesture;//点击播放手势
    @property (nonatomic,strong) UITapGestureRecognizer *tapCloseGesture;//点击添加关闭手势
    @property (nonatomic,strong) UITapGestureRecognizer *tapShowGesture;//点击添加展示手势
    
    @property (nonatomic,assign) NSInteger countNum;//录音计时(秒)
    @property (nonatomic,strong) NSTimer *playTimer;//控制播放时长显示更新
    @property (nonatomic,strong) NSString *timeString;//播放时间

    三、播放界面布局

    #pragma mark - playRecView界面布局
    - (UILabel *)titleInit
    {
        if (!_title) {
            self.title = [[UILabel alloc]init];
            self.title.text = @"录音";
            [self addSubview:self.title];
    
            [self.title mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.mas_equalTo(self).offset(0);
                make.centerY.mas_equalTo(self.centerY).offset(0);
                make.width.mas_equalTo(60);
            }];
        }
        return self.title;
    }
    
    - (UIImageView *)recordImageViewInit
    {
        if (!_recordPhoto) {
    //        self.recordPhoto = [[UIImageView alloc]init];
    //        self.recordPhoto.image = [UIImage imageNamed:@"playRecord0.png"];
    
            //自定义动画
            self.recordPhoto = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"playRecord0"]];
    //        self.recordPhoto.size = CGSizeMake(SCREEN_WIDTH * 0.9 - 10 - 60,SCREEN_HEIGHT * 0.06);
    
    
            NSArray *picArray = @[[UIImage imageNamed:@"playRecord1"],
                                  [UIImage imageNamed:@"playRecord2"],
                                  [UIImage imageNamed:@"playRecord3"],];
    
    
            [self.recordPhoto setAnimationImages:picArray];
            //    self.recordPhoto.contentMode = UIViewContentModeScaleAspectFit;
            [self.recordPhoto setAnimationDuration:1.5];
            [self.recordPhoto setAnimationRepeatCount:0];
    
            [self addSubview:self.recordPhoto];
    
    
            [self.recordPhoto mas_makeConstraints:^(MASConstraintMaker *make) {
                make.left.mas_equalTo(self.title.mas_right).offset(0);
                make.centerY.mas_equalTo(self.centerY).offset(0);
                make.width.mas_equalTo(SCREEN_WIDTH * 0.9 - 10 - 60);
                make.height.mas_equalTo(SCREEN_HEIGHT * 0.06);
            }];
        }
        return self.recordPhoto;
    }
    
    - (UILabel *)lbltimeInit
    {
        if (!_lbltime) {
            self.lbltime = [[UILabel alloc]init];
            self.lbltime.textColor = MF_ColorFromRGBA2(0x333333, 1);
            self.lbltime.text = @"00:00";
            [self.lbltime setHidden:YES];
    
            [self addSubview:self.lbltime];
            [self.lbltime mas_makeConstraints:^(MASConstraintMaker *make) {
                make.right.mas_equalTo(self.recordPhoto.mas_right).offset(0);
                make.centerY.mas_equalTo(self.recordPhoto.centerY).offset(0);
                make.size.mas_equalTo(CGSizeMake(60, 30));
            }];
        }
        return _lbltime;
    }

    四、播放界面

    在.m文件中添加

    #define kSandboxPathStr [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
    - (void)viewWillDisappear:(BOOL)animated
    {
        [self stopAllMusic];
    }
    - (void)viewDidDisappear:(BOOL)animated
    {
        //停止播放动画
        [self stopPlayAnimation];
    }
    #pragma mark - 数据初始化
    - (void)setData
    {
                _tapPlayGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(playRecordGestureClick:)];
            self.playRecView = [[playRecordView alloc]init];
            self.playRecView.recordPhoto.userInteractionEnabled = YES;
            self.playRecView.recordPhoto.tag = 20000 + i;
            self.playRecView.tag = 20000 + i;
            [[self.playRecView.recordPhoto viewWithTag:20000 + i] addGestureRecognizer:_tapPlayGesture];
    
    
    
            self.playRecView.frame = CGRectMake(SCREEN_WIDTH * 0.05, 60 + SCREEN_HEIGHT * 0.08 * i, SCREEN_WIDTH * 0.9 - 10, SCREEN_HEIGHT * 0.08);
            [self.recordBackView addSubview:self.playRecView];
    }
    //播放手势触发
    - (void)playRecordGestureClick:(UITapGestureRecognizer *)playGesture
    {
        if(playImageView && playImageView.isAnimating)
        {
    
            [_recordView stopAllMusic];
            [playImageView stopAnimating];
            for (playRecordView *playRecView in _recordBackView.subviews) {
                if (playRecView.tag == playImageView.tag) {
                    [playRecView.lbltime setHidden:YES];
    
                    playRecView.lbltime.text = @"00:00";
                }
            }
            [self.playTimer invalidate];
    
        }else{
            playImageView = (UIImageView*)playGesture.view;
            NSInteger index = playImageView.tag - 20000;
    
            [playImageView startAnimating];
    
            [_recordView playRecord:[self.recordArray objectAtIndex:index]];
    
            //获取总时间
            [self.playTimer fire];
    
        }
    
        NSLog(@"%ld",(long)index);
    
    
    }
    
    //停止播放动画代理
    - (void)stopPlayAnimation
    {
        if(playImageView && playImageView.isAnimating)
        {
            for (playRecordView *playRecView in _recordBackView.subviews) {
                if (playRecView.tag == playImageView.tag) {
                    [playRecView.lbltime setHidden:YES];
                    playRecView.lbltime.text = @"00:00";
                }
            }
            [self.playTimer invalidate];
    
            [playImageView stopAnimating];
        }
    }
    -(void)sendTotalTime:(NSTimeInterval)totalTime currentTime:(NSTimeInterval)currentTime
    {
        self.countNum = 0;
        NSTimeInterval timeInterval =1 ;
        //    1s
        self.playTimer = [NSTimer scheduledTimerWithTimeInterval:timeInterval  target:self selector:@selector(updatePlayTimerValue)  userInfo:nil  repeats:YES];
    
        tduration = totalTime;
    
    }
    
    -(void)updatePlayTimerValue
    {
        self.countNum += 1;
        NSUInteger seconds = (NSUInteger)round(tduration);
        _timeString = [NSString stringWithFormat:@"00:%02lu",seconds % 60 - self.countNum];
    
        for (playRecordView *playRecView in _recordBackView.subviews) {
            if (playRecView.tag == playImageView.tag) {
                [playRecView.lbltime setHidden:NO];
                if (self.countNum >= (seconds % 60)) {
                    playRecView.lbltime.text = @"00:00";
                }else{
                    playRecView.lbltime.text = _timeString;
                }
            }
        }
    
    }
    

    五、recordView(播放方法:核心)

    .h文件

    @protocol infoRecordViewDelegate<NSObject>
    @optional
    //获取录音数据
    -(void)getRecordData:(NSString *)path type:(NSString *)type fileName:(NSString *)fileName;//获取数据
    //停止播放动画
    - (void)stopPlayAnimation;
    
    //发送时间
    - (void)sendTotalTime:(NSTimeInterval)totalTime currentTime:(NSTimeInterval)currentTime;
    
    @end
    
    @interface infoRecordView : UIView<AVAudioPlayerDelegate>
    
    @property (nonatomic,strong) AVAudioPlayer *audioPlayer;//音频播放器
    @property (nonatomic,assign) NSInteger countNum;//录音计时(秒)
    @property (nonatomic,strong) NSTimer *playTimer;//控制播放时长显示更新
    @property (nonatomic,strong) NSString *timeString;//播放时间
    @property (nonatomic,strong) UIButton *closeBtn; //关闭按钮
    @property (nonatomic,strong) UIView *playBackView;//播放动画背景
    
    @property (nonatomic,copy) NSString *cafPathStr;
    
    @property (nonatomic,weak) id<infoRecordViewDelegate>delegate;
    
    @property (nonatomic,strong) UIImageView *playAniImageView;//播放动画
    
    - (void)playRecord:(NSString *)path view:(UIView *)view;
    
    - (void)playRecord:(NSString *)path;
    
    - (void)stopAllMusic;//停止播放
    

    .m文件

    #pragma mark - 播放
    - (void)playRecord:(NSString *)path view:(UIView *)view
    {
        [self stopAllMusic];
    
        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];  //此处需要恢复设置回放标志,否则会导致其它播放声音也会变小
    
        NSString *newPath = [path substringFromIndex:1];//路径第一位多了个/
    
        [self playMusicWithUrl:[NSURL URLWithString:newPath]];
    }
    
    //查看历史使用
    - (void)playRecord:(NSString *)path
    {
        [self stopAllMusic];
    
        AVAudioSession *audioSession=[AVAudioSession sharedInstance];
        [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil];  //此处需要恢复设置回放标志,否则会导致其它播放声音也会变小
    
        NSString *newPath = [path substringFromIndex:1];//路径第一位多了个/
    
        [self playMusicWithUrl:[NSURL URLWithString:newPath]];
    
    }
    
    - (void)stopPlayRecord
    {
        [self stopMusicWithUrl:[NSURL URLWithString:self.cafPathStr]];
    }
    -(void)deleteOldRecordFile{
        NSFileManager* fileManager=[NSFileManager defaultManager];
    
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:self.cafPathStr];
        if (!blHave) {
    //        NSLog(@"不存在");
            return ;
        }else {
    //        NSLog(@"存在");
            BOOL blDele= [fileManager removeItemAtPath:self.cafPathStr error:nil];
            if (blDele) {
    //            NSLog(@"删除成功");
            }else {
    //            NSLog(@"删除失败");
            }
        }
    }
    
    
    -(void)deleteOldRecordFileAtPath:(NSString *)pathStr{
        NSFileManager* fileManager=[NSFileManager defaultManager];
    
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:pathStr];
        if (!blHave) {
    //        NSLog(@"不存在");
            return ;
        }else {
    //        NSLog(@"存在");
            BOOL blDele= [fileManager removeItemAtPath:self.cafPathStr error:nil];
            if (blDele) {
    //            NSLog(@"删除成功");
            }else {
    //            NSLog(@"删除失败");
            }
        }
    }
    
    -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
    {
    //    NSLog(@"delegate--播放完毕----------------------");
    
        //停止播放动画代理
        [self.delegate stopPlayAnimation];
    
        [HUD hide:YES];
    }
    
    
    #pragma mark - AudioPlayer方法
    
    /**
     *播放音乐文件
     */
    - (BOOL)playMusicWithUrl:(NSURL *)fileUrl
    {
        //其他播放器停止播放
        [self stopAllMusic];
    
        if (!fileUrl) return NO;
    
        AVAudioSession *session=[AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];  //此处需要恢复设置回放标志,否则会导致其它播放声音也会变小
        [session setActive:YES error:nil];
    
        NSError *error;
    
        _audioPlayer = [self musices][fileUrl];
    
    #warning 不能加入全局异常断点,不然会卡在这里
        if (!_audioPlayer) {
            //2.2创建播放器
            _audioPlayer=[[AVAudioPlayer alloc]initWithContentsOfURL:fileUrl error:&error];
    
        }
    
        _audioPlayer.delegate = self;
    
        if (![_audioPlayer prepareToPlay]){
            NSLog(@"缓冲失败--");
            //        [self myToast:@"播放器缓冲失败"];
            return NO;
        }
    
        if (![_audioPlayer isPlaying]){
            //串行同步
            dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
    
            dispatch_sync(queue, ^{
                [self.delegate sendTotalTime:_audioPlayer.duration currentTime:_audioPlayer.currentTime];
            });
            dispatch_sync(queue, ^{
                //播放
                [_audioPlayer play];
            });
    
        }
        //2.4存入字典
        [self musices][fileUrl]=_audioPlayer;
    
    //    NSLog(@"musices:%@ musices",self.musices);
          return YES;//正在播放,那么就返回YES
    }
    
    /**
     *停止播放音乐文件
     */
    - (void)stopMusicWithUrl:(NSURL *)fileUrl{
        if (!fileUrl) return;//如果没有传入文件名,那么就直接返回
    
        //1.取出对应的播放器
        AVAudioPlayer *player=[self musices][fileUrl];
    
        //2.停止
        if ([player isPlaying]) {
            [player stop];
    //        NSLog(@"播放结束:%@--------",fileUrl);
        }
    
        if ([[self musices].allKeys containsObject:fileUrl]) {
    
            [[self musices] removeObjectForKey:fileUrl];
        }
    }
    
    //停止所有正在播放的音频
    - (void)stopAllMusic
    {
    
        if ([self musices].allKeys.count > 0) {
            for ( NSString *playID in [self musices].allKeys) {
    
                AVAudioPlayer *player=[self musices][playID];
                [player stop];            
            }
        }
    
        if(HUD){
            [HUD hide:YES];
        }
    
    
    }
    
    - (NSMutableDictionary *)musices
    {
        if (_musices==nil) {
            _musices=[NSMutableDictionary dictionary];
        }
        return _musices;
    }

    PS:ios录音功能实现http://blog.csdn.net/fantasy_jun/article/details/77894709

    展开全文
  • 思路: 创建一个空的Activity页面、两个Fragment、两个Adapter适配器、一个实体...在LocalFragment里获取手机音乐文件资源(歌曲名、歌手名、专辑名、专辑图片),绑定适配器; 在MIanActivity.java里绑定碎片、设置
  • iOS精选源码 飞机大作战 MUPhotoPreview -简单易用的图片浏览器 LLDebugTool是一款针对开发者和测试者的调试工具,它可以帮… 多个UIScrollView、UITableView嵌套 Swift仿QQ空间可拉伸...iOS实现网易云音乐右...
  • iOS企业级Swift项目实战之我的云音乐(第一部分) 任苹蜻,爱学啊创始人...
  • Swift学习资料@SwiftGuide很赞 的Swift学习资料leetcode一个练习、评估自己水平的代码平台,跟ACM有点类似完整App@Swift 30 Projects- 最新 Swift 3.0 的30个小App,更注重代码规范和架构设计(故胤道长)V2ex-Swift- ...
  • swift学习笔记

    2018-06-28 18:47:11
    swift笔记 基础知识 进制 0x代表16进制 0o代表8进制 0b代表2进制er dict dict: listdata listdata.count 总数 for i in listdata{ print i.key piint i.value } dict to array Array(dict.allkeys) ...
  • 应用场景:网络工具,音频工具(切换界面时,背景音乐继续放,不会重新播放,不会暂停)  */ @interface Singleton : NSObject //提供的静态实例的创建方法。(全局访问点) +(instancetype)...
  • 原来用swift也可以轻松使用Accelerate 框架。本文中只演示了两个波的叠加,然后整形,学习FFT音频处理难得的好文
  • 1.iina A few of the Features: Based on mpv, which provides the best decoding capacity on macOS Designed for modern macOS (10.10+), aims to offer the best user experience All the features you nee...
  • 本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源项目Swift Radio的...在这个项目中一共包含了6个界面控制器,其中包含了5个用于分别显示电台频道(Swift Radio)、正在播放(Now Playing View)、电...
  • 俄罗斯方块,是一款我们小时候都玩过的小游戏,我自己也是看着书上的思路,学着用 Swift 来写这个小游戏,在写这个游戏的过程中,除了一些位置的计算,数据模型和理解 Swift 语言之外,最好知道UIKIt框架中的Quartz...
  • 原文地址:Design Patterns on iOS using Swift – Part 1/2 原文作者:Lorenzo Boaro 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m… 译者:iWeslie 校对者:swants, Chunk49 使用 Swift 的 iOS...
1 2 3 4 5 ... 20
收藏数 504
精华内容 201
关键字:

swift 全局音频播放界面