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

macOS下AVSynchronizedLayer无法播放动画、同代码iOS运行正常问题求助

问题原因

macOS 平台的 AVSynchronizedLayer 对仅包含音频的 AVPlayerItem 时间同步存在平台层兼容缺陷,不会主动同步纯音频轨道的时间进度,而 iOS 系统内部做了对应兼容,所以相同代码在 iOS 可以正常运行。


修复方案1:给音频资产插入空视频轨道

该方案兼容纯音频场景,不需要修改原有动画逻辑,适配性更强:

// 原音频加载逻辑修改为如下
let url = Bundle.main.url(forResource: "debugAudio", withExtension: "m4a")!
let asset = AVURLAsset(url: url)
// 创建可变资产插入空视频轨道,激活AVSynchronizedLayer的时间同步能力
let composition = AVMutableComposition()
// 插入原有音频轨道
try! composition.insertTimeRange(CMTimeRangeMake(start: .zero, duration: asset.duration), of: asset, at: .zero)
// 新增1x1尺寸的空视频轨道
let videoTrack = composition.addMutableTrack(withMediaType: .video, preferredTrackID: kCMPersistentTrackID_Invalid)!
videoTrack.preferredTransform = .identity

// 生成AVPlayerItem,后续原有逻辑完全不变
let item = AVPlayerItem(asset: composition)
avPlayer.replaceCurrentItem(with: item)

// 原有同步层、子层、动画、播放逻辑全部保持不变即可

修复方案2:等待AVPlayerItem准备完成后再添加动画

如果不想修改资产结构,也可以通过监听播放器 item 状态,配合补充动画配置解决问题:

// 音频加载、同步层创建、子层创建逻辑保持不变
let url = Bundle.main.url(forResource: "debugAudio", withExtension: "m4a")!
let asset = AVURLAsset(url: url)
let item = AVPlayerItem(asset: asset)
avPlayer.replaceCurrentItem(with: item)

let syncLayer = AVSynchronizedLayer(playerItem: avPlayer.currentItem!)
view.layer!.addSublayer(syncLayer)
syncLayer.frame = CGRect(x: 60, y: 60, width: 200, height: 200)

let progressLayer = CALayer()
progressLayer.backgroundColor = NSColor.purple.cgColor
progressLayer.borderColor = NSColor.orange.cgColor
syncLayer.addSublayer(progressLayer)
progressLayer.frame = syncLayer.bounds

// 新增监听playerItem的就绪状态
item.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.new], context: nil)

// 监听回调处理
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    guard keyPath == #keyPath(AVPlayerItem.status),
          let item = object as? AVPlayerItem,
          item.status == .readyToPlay else {
        super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        return
    }
    // 移除监听避免内存泄漏
    item.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))
    
    // 动画配置补充fillMode
    let anim = CABasicAnimation(keyPath: "borderWidth")
    anim.fromValue = 3.0
    anim.toValue = 20.0
    anim.duration = 5.0
    anim.isRemovedOnCompletion = false
    anim.fillMode = .forwards
    anim.beginTime = AVCoreAnimationBeginTimeAtZero
    progressLayer.add(anim, forKey: nil)
    
    // 播放
    self.avPlayer.play()
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 04:54:06