You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 06:25:26