swift音乐播放器实现

最近在学习swift,打算用一个项目来熟悉关于UI的用法和一些功能。所以有了写一个自用的音乐播放器的想法。在这里记录一下实现的过程和中间出现的问题,方便以后的回忆。
首先使用swift的规则实现了UINavigationController,UITabBarController和UIViewController的自定义,在里面实现了一些关于iOS15以后的的适配工作。主要在"MusicPlayerVC"里实现了音乐播放功能。

2023.5.2

首先是简单的实现一个播放本地音乐的功能,通过下面的方法获取本地音乐文件的路径

Bundle.main.path(forResource: "音乐名", ofType: "mp3")

通过AVPlayer和AVPlayerItem配合,实现了音乐的播放。这个过程中难点更多的是OC转swift过程中语法的不适应。主要函数和用法其实都一致。

之后自定义了一个叫做"MusicControlView"的控制台面板,当前只有三个按钮,分别是上一曲,播放,下一曲。然后通过typealias函数自定义了block来做view和控制器间的交互。

播放和暂停分别对应AVPlayer对象的play和pause函数。上一曲下一曲的功能通过更改AVPlayerItem对象的参数,并通过AVPlayer对象调用下面函数来实现

plyaer.replaceCurrentItem(with:item)
player.play()

之后自定义了叫做MusicDurationView的进度面板,用来展示播放进度,当前播放时间,总时间。

这里工作量多了一些。首先需要player对象调用addPeriodicTimeObserver函数

player.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) {
            (CMTime) -> Void in
            let currentT = CMTimeGetSeconds(self.player!.currentTime())
            //这里是更新进度面板里的数据
            self.durationBgView.setMusicDurationData(currentTime: Float(currentT), totleTime: Float(dSecond))
 }

在进度面板中,播放进度用的是UISlider,时间展示用的UILabel。这里涉及到一个滑动slider来更改播放进度的功能。核心代码就是下面

//添加值更改对应函数
slider.addTarget(self, action: #selector(sliderValueChange), for: .valueChanged)
//player跳转到对应进度
player.seek(to: CMTime(seconds: Double(sliderValue), preferredTimescale: 1))

这里效果不太好,因为是时时的执行sliderValueChange函数,导致时时的去更改播放进度。最好的效果是等彻底滑动结束再去更改播放进度。暂时还没找到特别好的解决办法,等待后续完善。

最后在viewWillAppear函数里添加播放完毕的通知,在viewWillDisappear函数里移除通知。因为是重写父类的方法,所以在func前加上override,意为覆盖

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        //添加播放结束的通知
        NotificationCenter.default.addObserver(self, selector: #selector(musicDidFinishPlay), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player.currentItem)
    }
    
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        //移除通知
        NotificationCenter.default.removeObserver(self)
    }

在播放完成执行的函数里更改控制面板,进度面板的状态,重新设置AVPlayerItem参数。移除了上一首歌的通知,添加当前歌曲通知。这里如果不移除并重新添加通知,造成的结果就是播放完下一首歌就不会再执行这个播放完成的函数

player.replaceCurrentItem(with: playerItem)
player.play()

2023.5.14

这次为播放器加入了后台播放的功能。
首先需要在Target-Signing&Capabilities中添加进来Background Modes模块,并且选中'Audio,AirPlay,and Picture in Picture'
之后在AppDelegate.swift文件下引入AVFoundation,并且在didFinishLaunchingWithOptions函数里注册后台播放功能,具体代码如下

  let session = AVAudioSession.sharedInstance()
  do {
    try session.setActive(true)
    try session.setCategory(AVAudioSession.Category.playback)
  } catch {
    print(error)
  }

之后在音乐播放界面中添加一系列代码

1

在AVPlayer添加定时观察者的函数里,加入播放器状态的判断。在播放的时候,要设置显示后台播放的显示信息,还需要在暂停按钮执行的方法中让后台显示信息进度停止。显示和停止使用的都是自定义方法setPlaybackInfo,里面的代码如下。

import MediaPlayer

func setPlaybackInfo(playbackStatus:Int) {
        let mpic = MPNowPlayingInfoCenter.default()
        //专辑封面
        let mySize = CGSize(width: 400, height: 400)
        let albumArt = MPMediaItemArtwork(boundsSize: mySize) { sz in
            return UIImage(named: "MusicDefaultLogo")!
        }
        
        //获取进度
        let position = Double(durationBgView.sliderCurrentValue)
        let duration = Double(durationBgView.sliderTotleValue)
        
        mpic.nowPlayingInfo = [MPMediaItemPropertyTitle:musicMsgModel?.MusicName ?? "未知",
                              MPMediaItemPropertyArtist:"Linkin Park",
                             MPMediaItemPropertyArtwork:albumArt,
            MPNowPlayingInfoPropertyElapsedPlaybackTime:position,
                    MPMediaItemPropertyPlaybackDuration:duration,
                   MPNowPlayingInfoPropertyPlaybackRate:playbackStatus]
    }

上面代码中因为UISlider是放在durationBgView中的,所以只能给durationBgView定义了两个属性来传值,获取当前歌曲总时长和播放进度的。在定义了这两个属性后,我运行项目的时候在durationBgView的init方法中出现了报错,报错信息是
'Property 'self.sliderCurrentValue' not initialized at super.init call'
'Property 'self.sliderTotleValue' not initialized at super.init call'
具体原因是swift在init中,有四个阶段的检查,这里与其中一个相违背,即调用父类初始化之前,必须给子类特有的属性设置初始值,只有在类的所有存储属性状态都明确后, 这个对象才能被初始化。我在创建这两个属性的时候,并没有设置初始值,只声明了属性类型,所以才出现了这样的报错。参考资料

2

做完这些以后需要在viewWillAppear函数中接收远程响应事件,并注册成为第一响应者。在viewWillDisAppear函数中停止接收远程响应事件

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)

  //告知系统接受远程响应事件,并注册成为第一响应者
  UIApplication.shared.beginReceivingRemoteControlEvents()
  self.becomeFirstResponder()
}

 override func viewWillDisappear(_ animated: Bool) {
   super.viewWillDisappear(animated)

   //停止接受远程响应事件
   UIApplication.shared.endReceivingRemoteControlEvents()
   self.resignFirstResponder()
}

3

最后一步就是需要在后台播放时,也能对播放器进行操作,就需要重写remoteControlReceived函数,在函数中根据event.subtype来识别点击了哪个按钮,进行后续操作。
至此播放器后台播放功能实现。
Demo地址

在学习过程中借鉴了一些大佬的文章,让我能更快的熟悉swift语言
https://www.hangge.com/blog/cache/detail_1669.html //www.greatytc.com/p/1ab430c82b93

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容