SwiftUI开发:如何让iPhone控制中心显示视频标题?
解决iOS控制中心无法显示AVPlayer视频标题的问题
问题场景
开发环境:Xcode 14.0、Swift 5.7
测试设备:iPhone 8(iOS 16.0)
使用AVPlayer播放本地视频,通过MPNowPlayingInfoCenter设置了标题和专辑名,但控制中心始终不显示这些信息,同时已配置MPRemoteCommandCenter的播放/暂停命令。
原实现代码:
import SwiftUI import AVKit import MediaPlayer struct ContentView: View { var body: some View { let playerClass = PlayerClass() VideoPlayer(player: playerClass.aVPlayer) .onAppear { playerClass.aVPlayer.play() } } } class PlayerClass { var aVPlayer: AVPlayer init() { let fileUrl = Bundle.main.url(forResource: "sample", withExtension: "mp4")! aVPlayer = AVPlayer(url: fileUrl) setNowPlayingInfo() setMPRemoteCommandCenter() } func setNowPlayingInfo() { var nowPlayingInfo = [String : Any]() nowPlayingInfo[MPMediaItemPropertyTitle] = "my title" nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = "my album" MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } func setMPRemoteCommandCenter() { let commandCenter = MPRemoteCommandCenter.shared() commandCenter.playCommand.addTarget { [unowned self] event in print("play") return .success } commandCenter.pauseCommand.addTarget { [unowned self] event in print("pause") return .success } } }
解决方案
控制中心不显示标题的核心原因有两个:PlayerClass实例被过早释放、未激活音频会话导致系统未识别媒体播放状态。以下是修复步骤:
1. 强持有PlayerClass实例
原代码中PlayerClass是在body闭包内创建的局部变量,视图刷新时会被重新创建且无强引用,导致实例被释放,相关设置失效。需要将其改为视图的属性并保持强引用:
在ContentView中使用@StateObject(SwiftUI 2.0+)持有PlayerClass实例:
struct ContentView: View { @StateObject private var playerClass = PlayerClass() // 强引用持有 var body: some View { VideoPlayer(player: playerClass.aVPlayer) .onAppear { playerClass.aVPlayer.play() } } }
2. 激活AVAudioSession
iOS 14及以后,即使是视频播放,也需要激活音频会话才能让系统识别媒体播放状态,进而在控制中心显示NowPlaying信息。需要导入AVFoundation并在PlayerClass中激活会话:
在PlayerClass的init方法中添加音频会话激活代码:
import AVFoundation // 新增导入 class PlayerClass { var aVPlayer: AVPlayer init() { let fileUrl = Bundle.main.url(forResource: "sample", withExtension: "mp4")! aVPlayer = AVPlayer(url: fileUrl) // 激活音频会话 do { try AVAudioSession.sharedInstance().setCategory(.playback) try AVAudioSession.sharedInstance().activate() } catch { print("音频会话激活失败:\(error)") } setNowPlayingInfo() setMPRemoteCommandCenter() } // 其他方法不变... }
3. 监听播放状态,动态更新NowPlaying信息(可选)
为了确保播放状态变化时控制中心信息同步,可以监听AVPlayer的rate属性变化,重新设置NowPlaying信息:
在PlayerClass中添加监听:
class PlayerClass { var aVPlayer: AVPlayer private var observation: NSKeyValueObservation? // 监听对象 init() { // ... 原有代码 // 监听播放速率变化,更新NowPlaying信息 observation = aVPlayer.observe(\.rate, options: [.new]) { [weak self] player, change in guard let self = self else { return } self.setNowPlayingInfo() } } // ... 其他方法不变 }
修复后的完整代码
import SwiftUI import AVKit import MediaPlayer import AVFoundation struct ContentView: View { @StateObject private var playerClass = PlayerClass() var body: some View { VideoPlayer(player: playerClass.aVPlayer) .onAppear { playerClass.aVPlayer.play() } } } class PlayerClass { var aVPlayer: AVPlayer private var observation: NSKeyValueObservation? init() { guard let fileUrl = Bundle.main.url(forResource: "sample", withExtension: "mp4") else { fatalError("无法找到视频文件") } aVPlayer = AVPlayer(url: fileUrl) // 激活音频会话 do { try AVAudioSession.sharedInstance().setCategory(.playback) try AVAudioSession.sharedInstance().activate() } catch { print("音频会话激活失败:\(error)") } setNowPlayingInfo() setMPRemoteCommandCenter() // 监听播放状态变化 observation = aVPlayer.observe(\.rate, options: [.new]) { [weak self] _, _ in self?.setNowPlayingInfo() } } func setNowPlayingInfo() { var nowPlayingInfo = [String : Any]() nowPlayingInfo[MPMediaItemPropertyTitle] = "my title" nowPlayingInfo[MPMediaItemPropertyAlbumTitle] = "my album" // 可选:添加播放进度信息 nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = aVPlayer.currentTime().seconds nowPlayingInfo[MPMediaItemPropertyPlaybackDuration] = aVPlayer.currentItem?.duration.seconds MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo } func setMPRemoteCommandCenter() { let commandCenter = MPRemoteCommandCenter.shared() commandCenter.playCommand.addTarget { [unowned self] _ in aVPlayer.play() return .success } commandCenter.pauseCommand.addTarget { [unowned self] _ in aVPlayer.pause() return .success } } }
关键说明
@StateObject确保PlayerClass实例在视图生命周期内保持存活,不会被随意释放。- 激活
AVAudioSession的.playback类别,告诉系统当前应用正在播放媒体内容,这是控制中心显示NowPlaying信息的必要条件。 - 监听播放状态并动态更新NowPlaying信息,能保证控制中心的信息与实际播放状态同步。
内容的提问来源于stack exchange,提问作者number l advantage
相关产品推荐
相关产品推荐

