Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios AVAudioPlayer';忘记';由MPRemoteCommand触发时播放_Ios_Swift_Avaudioplayer_Mpremotecommandcenter - Fatal编程技术网

Ios AVAudioPlayer';忘记';由MPRemoteCommand触发时播放

Ios AVAudioPlayer';忘记';由MPRemoteCommand触发时播放,ios,swift,avaudioplayer,mpremotecommandcenter,Ios,Swift,Avaudioplayer,Mpremotecommandcenter,我正在尝试播放音频文件,并通过锁屏上的远程命令中心控制其播放 如果我这样做: 开始播放 暂停播放 锁定装置 从锁屏开始播放(MPRemoteCommandCenter) 这样就不可能从锁屏暂停播放。按钮闪烁,什么也没发生 我怎样才能解决这个问题 详情如下: 似乎在尝试暂停音频时,AVAudioPlayer会为audioPlayer.isplay返回“false” 这发生在我的iPhoneXR、iPhoneSE和iPhone8的iOS13.1上。我没有其他设备可以测试 日志表明AVAudio

我正在尝试播放音频文件,并通过锁屏上的远程命令中心控制其播放

如果我这样做:

  • 开始播放
  • 暂停播放
  • 锁定装置
  • 从锁屏开始播放(MPRemoteCommandCenter)
这样就不可能从锁屏暂停播放。按钮闪烁,什么也没发生

我怎样才能解决这个问题

详情如下:

  • 似乎在尝试暂停音频时,AVAudioPlayer会为audioPlayer.isplay返回“false”
  • 这发生在我的iPhoneXR、iPhoneSE和iPhone8的iOS13.1上。我没有其他设备可以测试
  • 日志表明AVAudioPlayer.isplay在开始播放时最初返回true,但随后返回false。播放机的当前时间也在播放开始时左右出现停滞
  • 我的整个视图控制器低于(~100行)。这是重现问题所需的最低限度
  • Github上也提供了演示错误的示例项目
导入UIKit 导入MediaPlayer

class ViewController: UIViewController {
    @IBOutlet weak var playPauseButton: UIButton!
    @IBAction func playPauseButtonTap(_ sender: Any) {
        if self.audioPlayer.isPlaying {
            pause()
        } else {
            play()
        }
    }

    private var audioPlayer: AVAudioPlayer!
    private var hasPlayed = false

    override func viewDidLoad() {
        super.viewDidLoad()

        let fileUrl = Bundle.main.url(forResource: "temp/intro", withExtension: ".mp3")
        try! self.audioPlayer = AVAudioPlayer(contentsOf: fileUrl!)
        let audioSession = AVAudioSession.sharedInstance()
        do { // play on speakers if headphones not plugged in
            try audioSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
        } catch let error as NSError {
            print("Override headphones failed, probably because none are available: \(error.localizedDescription)")
        }
        do {
            try audioSession.setCategory(.playback, mode: .spokenAudio)
            try audioSession.setActive(true)
        } catch let error as NSError {
            print("Warning: Setting audio category to .playback|.spokenAudio failed: \(error.localizedDescription)")
        }
        playPauseButton.setTitle("Play", for: .normal)
    }

    func play() {
        playPauseButton.setTitle("Pause", for: .normal)
        self.audioPlayer.play()
        if(!hasPlayed){
            self.setupRemoteTransportControls()
            self.hasPlayed = true
        }
    }

    func pause() {
        playPauseButton.setTitle("Play", for: .normal)
        self.audioPlayer.pause()
    }

    // MARK: Remote Transport Protocol
    @objc private func handlePlay(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
        print(".......................")
        print(self.audioPlayer.currentTime)
        let address = Unmanaged.passUnretained(self.audioPlayer).toOpaque()
        print("\(address) not playing: \(!self.audioPlayer.isPlaying)")
        guard !self.audioPlayer.isPlaying else { return .commandFailed }
        print("attempting to play")
        let success = self.audioPlayer.play()
        print("play() invoked with success \(success)")
        print("now playing \(self.audioPlayer.isPlaying)")
        return success ? .success : .commandFailed
    }

    @objc private func handlePause(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
        print(".......................")
        print(self.audioPlayer.currentTime)
        let address = Unmanaged.passUnretained(self.audioPlayer).toOpaque()
        print("\(address) playing: \(self.audioPlayer.isPlaying)")
        guard self.audioPlayer.isPlaying else { return .commandFailed }
        print("attempting to pause")
        self.pause()
        print("pause() invoked")
        return .success
    }

    private func setupRemoteTransportControls() {
        let commandCenter = MPRemoteCommandCenter.shared()
        commandCenter.playCommand.addTarget(self, action: #selector(self.handlePlay))
        commandCenter.pauseCommand.addTarget(self, action: #selector(self.handlePause))

        var nowPlayingInfo = [String : Any]()
        nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = "Major title"
        nowPlayingInfo[MPMediaItemPropertyTitle] = "Minor Title"
        nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = self.audioPlayer.currentTime
        nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = self.audioPlayer.duration
        nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = self.audioPlayer.rate
        MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
    }
}
此日志记录了以下内容(添加了我的//注释):

我束手无策。帮帮我,你是我唯一的希望

编辑:我也试过 永远回来,成功 忽略audioPlayer状态,按照远程指挥中心的指示执行 这两种情况都会导致第一次在锁屏上点击
pause
时错误持续存在。随后的轻触将音频重置为原始暂停位置,然后正常工作

更新
  • 用AVPlayer替换AVAudioPlayer似乎可以彻底解决这个问题!我相当肯定这是AVAudioPlayer中的一个bug
  • 切换到AVPlayer的必要步骤如下
  • 我使用了我的一个开发者问题单,并将这个问题提交给了苹果公司。当我收到他们的回复时,我会发布一个答案
更新2
  • 苹果开发支持部门确认,截至2019年12月5日,该问题尚无已知的解决办法。我已经向feedbackassistant.apple.com提交了一个问题,当问题发生变化时,我会更新这个答案
      这确实是AVAudioPlayer中的一个bug

      如果您不想切换到AVPlayer,另一个解决方法是在暂停前检查是否播放,如果没有,请在暂停前调用play。虽然不漂亮,但效果很好:

      if (!self.player.isPlaying) [self.player play];
      [self.player pause];
      

      @马特,我只是根据你的建议尝试了一下,但没用。我正在用我试过的代码对问题进行编辑。@matt我也试过你想让我做的事。编辑附在我的问题后面。现在,第一次点击锁屏暂停按钮时忽略了第一次点击。很抱歉我误解了你,谢谢你的帮助。
      @objc private func handlePlay(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
          guard !self.audioPlayer.isPlaying else { return .success }
          self.audioPlayer.play()
          return .success
      }
      
      @objc private func handlePause(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
          print(self.audioPlayer.isPlaying)
          guard self.audioPlayer.isPlaying else { return .success }
          self.pause()
          return .success
      }
      
      @objc private func handlePlay(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
          self.audioPlayer.play()
          MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyElapsedPlaybackTime] = self.audioPlayer.currentTime
          MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = self.audioPlayer.rate
          return .success
      }
      
      @objc private func handlePause(event: MPRemoteCommandEvent) -> MPRemoteCommandHandlerStatus {
          self.audioPlayer.pause()
          MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyElapsedPlaybackTime] = self.audioPlayer.currentTime
          MPNowPlayingInfoCenter.default().nowPlayingInfo?[MPNowPlayingInfoPropertyPlaybackRate] = self.audioPlayer.rate
          return .success
      }
      
      if (!self.player.isPlaying) [self.player play];
      [self.player pause];