You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS锁屏音频播放控件不显示问题求助

排查锁屏音频控件不显示的问题

从你贴的代码来看,核心问题是没正确配置MPNowPlayingInfoCenter,另外还有几处细节需要调整,我来一步步帮你搞定:

1. 补全并正确设置NowPlaying信息

你的setupLockScreen函数只初始化了部分元数据,但没把它赋值给系统的MPNowPlayingInfoCenter——这是锁屏控件不显示的关键,系统需要这些信息才知道要渲染播放控件。而且你还搞混了「总时长」和「已播放时长」的字段,我帮你修正并抽离出复用的更新方法:

func setupLockScreen(){
    let commandCenter = MPRemoteCommandCenter.shared()
    
    // 保留你已有的播放命令逻辑,补充状态更新
    commandCenter.playCommand.isEnabled = true
    commandCenter.playCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
        guard let player = self.player else { return .commandFailed }
        if player.rate == 0.0 {
            player.play()
            self.updateNowPlayingState(isPlaying: true)
            return .success
        }
        return .commandFailed
    }
    
    // 必须补充暂停命令,否则控件的暂停按钮会失效
    commandCenter.pauseCommand.isEnabled = true
    commandCenter.pauseCommand.addTarget { (event) -> MPRemoteCommandHandlerStatus in
        guard let player = self.player else { return .commandFailed }
        if player.rate != 0.0 {
            player.pause()
            self.updateNowPlayingState(isPlaying: false)
            return .success
        }
        return .commandFailed
    }
    
    // 初始化基础播放信息
    self.updateNowPlayingState(isPlaying: true)
}

// 单独抽离更新方法,方便播放状态变化时调用
private func updateNowPlayingState(isPlaying: Bool) {
    guard let player = self.player, let currentItem = player.currentItem else { return }
    
    var nowPlayingInfo = [String: Any]()
    nowPlayingInfo[MPMediaItemPropertyTitle] = "My Song"
    nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = currentItem.duration.seconds // 总时长
    nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = player.currentTime() // 当前播放进度
    nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = isPlaying ? 1.0 : 0.0 // 播放状态
    
    // 关键:把信息同步给系统
    MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
}

2. 简化AppDelegate的音频会话配置

你在setupAudioSession里调用的self.becomeFirstResponder()其实没什么用(AppDelegate作为UIResponder子类,这个操作对音频会话没帮助),可以删掉,同时简化激活逻辑:

func setupAudioSession(){
    do {
        try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options: [])
        try AVAudioSession.sharedInstance().setActive(true)
        print("AVAudioSession is Active")
    } catch let error as NSError {
        print("Audio Session Error: \(error.localizedDescription)")
    }
}

另外,UIApplication.shared.beginReceivingRemoteControlEvents在iOS 7之后已经不需要手动调用了,MPRemoteCommandCenter会自动处理,留着也不影响,但可以考虑删掉。

3. 确认后台模式配置到位

除了代码,还要检查项目配置:

  • 打开Xcode的Signing & Capabilities标签
  • 添加Background Modes能力
  • 勾选Audio, AirPlay, and Picture in Picture选项
  • 或者直接在Info.plist里添加UIBackgroundModes数组,包含audio字符串

4. 时机:在播放开始后调用setupLockScreen

要等你的音频播放器真正开始播放(比如调用player.play()之后)再调用setupLockScreen,因为此时播放器才有currentItem,能获取到正确的时长信息。比如在播放按钮的点击事件里:

@IBAction func playButtonTapped(_ sender: UIButton) {
    if player?.rate == 0.0 {
        player?.play()
        setupLockScreen()
    } else {
        player?.pause()
        updateNowPlayingState(isPlaying: false)
    }
}

额外小提示

  • 如果用的是AVPlayer,可以监听AVPlayerItemDidPlayToEndTime通知,在播放结束时更新NowPlaying信息
  • 可以监听播放器的rate属性变化,自动同步播放状态到系统,不用手动调用更新方法

内容的提问来源于stack exchange,提问作者Nayef

火山引擎 最新活动