MPMusicPlayerController.systemMusicPlayer setQueue难度

时间:2018-03-19 01:38:02

标签: ios swift mpmusicplayercontroller mpmediaitem uibackgroundtask

我的音乐应用有困难。我们的想法是点按一个按钮,播放当前播放的艺术家的更多歌曲。当我在应用程序中,我点击下一首歌它工作正常。但是,如果我让歌曲自然结束,应用程序会播放随机歌曲。所以我添加了逻辑来说明歌曲剩余时间是否为0然后使用媒体播放器func播放下一首歌曲,因为我知道这有效。这解决了它,除非应用程序在后台。我试图保持应用程序活着,如果它在后台,但我想这不起作用。

每次我认为我已经解决了问题,问题又回来了。

我期待发生的事情是锁定艺术家,关闭应用程序背景,当歌曲结束时播放艺术家的另一首歌。

实际发生的事情是当我锁定艺术家并将应用关闭到背景时,歌曲将结束,有时它会播放正确的歌曲。有时它不会。 HOWEVER 当它播放错误的歌曲并且我打开应用程序时它会结束当前(错误的)播放歌曲并开始播放由合适的艺术家播放的歌曲

要清楚,我认为需要在剩余歌曲时间为0时运行的逻辑跳到下一首歌是不明智的,但出于任何原因我需要它

我已将功能设置为背景提取和音频Airplay以及画中画

我试图只按顺序发布相关代码..

我有一个属性

let mediaPlayer = MPMusicPlayerController.systemMusicPlayer
var backgroundTask: UIBackgroundTaskIdentifier = UIBackgroundTaskInvalid

在我的 ViewDidLoad

try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategorySoloAmbient)
try? AVAudioSession.sharedInstance().setActive(true)


DispatchQueue.main.async {
  self.clearSongInfo()
  MediaManager.shared.getAllSongs { (songs) in
    guard let theSongs = songs else {
      return
    }
    self.mediaPlayer.nowPlayingItem = nil

    self.newSongs = theSongs.filter({ (item) -> Bool in
      return !MediaManager.shared.playedSongs.contains(item)
    })
    self.aSongIsInChamber = true
    self.mediaPlayer.setQueue(with: MPMediaItemCollection(items: self.newSongs.shuffled())
    )
    self.mediaPlayer.shuffleMode = .off
    self.mediaPlayer.repeatMode = .none

  }
  NotificationCenter.default.addObserver(self, selector: #selector(self.songChanged(_:)), name: NSNotification.Name.MPMusicPlayerControllerNowPlayingItemDidChange, object: self.mediaPlayer)
  self.mediaPlayer.beginGeneratingPlaybackNotifications()
}

我有一个更新播放时间和剩余时间的功能,而且我有

if Int(self.songProgressSlider.maximumValue - self.songProgressSlider.value) < 1 {
  print("song ended naturally, skipped")
  mediaPlayer.prepareToPlay(completionHandler: { (error) in
    DispatchQueue.main.async {
      self.mediaPlayer.skipToNextItem()
    }
  })
}

当我播放音乐时,我有一个布尔 isPlaying

如果是,那么时间正在运行,我有这个

let app = UIApplication.shared
  var task: UIBackgroundTaskIdentifier?
  task  = app.beginBackgroundTask {
    app.endBackgroundTask(task!)
  }

我有把它保持在后台打开定时器,所以它会在剩余时间为0时播放下一首歌

现在要锁定艺术家我有以下代码在按钮上执行

let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
      let query: MPMediaQuery = MPMediaQuery.artists()
      let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

      query.addFilterPredicate(artistPredicate)
      musicPlayerController.setQueue(with: query)

2 个答案:

答案 0 :(得分:1)

我发现在你说prepareToPlay之前设置队列不会“接受”(通过说play来字面或隐含)。我书的当前版本说:

  

我的经验是,如果您在设置队列后没有立即向play或至少prepareToPlay询问,则播放器可能会以意想不到的方式运行。显然,在你这样做之前,队列实际上不会生效。

你也说过(在评论中):

  

理想情况下,我希望首先完成这首歌,然后进入我的队列

好的,但是也许你想要的是(iOS 11中的新功能)append功能,它允许你修改队列而不完全替换它。

答案 1 :(得分:0)

我找到的解决方案不是我想要的100%,但此时我会接受它。

在IBAction上,我锁定了一位艺术家,我原本只是

self.mediaPlayer.prepareToPlay { error in
  let artistPredicate: MPMediaPropertyPredicate  = MPMediaPropertyPredicate(value: nowPlaying.artist, forProperty: MPMediaItemPropertyArtist)
  let query: MPMediaQuery = MPMediaQuery.artists()
  let musicPlayerController: MPMusicPlayerController = MPMusicPlayerController.systemMusicPlayer

  query.addFilterPredicate(artistPredicate)
  musicPlayerController.setQueue(with: query)
}

我已经采取了哪些措施来解决我的问题

musicPlayerController.play()

我设置队列后。

这种方法的缺点是,如果我在一首歌的中途,我点击按钮锁定艺术家,应用程序立即结束现在播放的歌曲并转到该艺术家的歌曲,我宁愿歌曲完成播放和然后去我想要的歌曲。

如果有人能想到一种方法来使用播放()并且让歌曲自然结束,那么答案是正确的......

注意我从不想使用检查歌曲剩余时间的逻辑,我从不想使用背景代码。所以我摆脱了它,它仍然适用于新的想法。