iOS 18下RealityKit的VideoMaterial显示粉色条纹,iOS17正常如何修复?
iOS 18中RealityKit VideoMaterial渲染粉色条纹问题修复
问题背景
在iOS 17上正常运行的RealityKit视频屏幕代码,到iOS 18中出现粉色条纹渲染(苹果工程师提示这是材质未正确应用的表现)。视频屏幕基于ARImageAnchor锚定,使用AVPlayer和VideoMaterial实现,但所有基础验证(URL正确性、Player初始化、播放状态)均无问题。
复现步骤
- 创建平面作为视频载体
- 通过AVPlayerItem为平面应用VideoMaterial
- 将模型实体锚定到ARImageAnchor
预期结果
视频正常渲染在RealityKit平面上并播放
实际结果
iOS 18中平面显示粉色条纹,VideoMaterial未生效
iOS 18相关行为变化分析
iOS 18对RealityKit与AVFoundation的交互逻辑做了更严格的校验,主要影响点包括:
- VideoMaterial绑定时机:iOS 18要求VideoMaterial初始化时,关联的AVPlayer必须已持有就绪状态的AVPlayerItem,否则会触发缺失材质的默认粉色渲染
- AVPlayer生命周期管理:对未就绪Player的弱引用处理更严格,若Player在绑定材质时无有效内容,RealityKit会直接放弃材质加载
- Notification监听有效性:原代码中监听
player.currentItem的播放结束通知,当Player替换Item后,旧观察者会失效,导致视频无法循环,但这不是粉色条纹的直接原因
修复方案
针对上述变化,对代码做以下调整:
1. 确保AVPlayer初始化时已绑定就绪的AVPlayerItem
修改createVideoMaterial方法,直接用已准备好的AVPlayerItem初始化AVPlayer,避免先创建空Player再替换Item:
private func createVideoMaterial(with videoItem: AVPlayerItem, uuid: UUID) -> VideoMaterial { // 直接用就绪的AVPlayerItem初始化Player let player = AVPlayer(playerItem: videoItem) player.actionAtItemEnd = .none // 确保Player有有效内容后再创建VideoMaterial let videoMaterial = VideoMaterial(avPlayer: player) player.play() // 修正通知监听:直接绑定传入的videoItem,避免后续Item替换导致监听失效 NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: videoItem, queue: .main) { [weak player] _ in guard let player = player else { return } player.seek(to: .zero) player.play() } videoPlayers[uuid] = player return videoMaterial }
2. 等待AVAsset加载完成后再创建AVPlayerItem
iOS 18对未加载完成的Asset兼容性下降,需确保AVAsset处于可播放状态后再生成AVPlayerItem:
// 修改为异步方法,确保Asset加载完成 private func createVideoItem(with url: URL, completion: @escaping (AVPlayerItem?) -> Void) { let asset = AVURLAsset(url: url) // 加载必要的资源键,确保Asset可播放 let requiredKeys = ["playable", "tracks"] asset.loadValuesAsynchronously(forKeys: requiredKeys) { var loadError: NSError? defer { DispatchQueue.main.async { if loadError != nil { completion(nil) } else { completion(AVPlayerItem(asset: asset)) } } } // 校验所有必要资源是否加载完成 for key in requiredKeys { guard asset.statusOfValue(forKey: key, error: &loadError) == .loaded else { return } } } }
3. 调整视频屏幕创建流程为异步
对应createVideoScreen方法,改为异步调用确保PlayerItem就绪:
private func createVideoScreen(width: Float, height: Float, url: URL, uuid: UUID, completion: @escaping (ModelEntity?) -> Void) { let screenMesh = MeshResource.generatePlane(width: width, height: height) createVideoItem(with: url) { [weak self] videoItem in guard let self = self, let videoItem = videoItem else { completion(nil) return } let videoMaterial = self.createVideoMaterial(with: videoItem, uuid: uuid) let videoScreenModel = ModelEntity(mesh: screenMesh, materials: [videoMaterial]) completion(videoScreenModel) } }
4. 调用时适配异步逻辑
在placeVideoScreen前,先异步创建视频屏幕:
// 示例调用 createVideoScreen(width: 1.0, height: 0.5, url: videoURL, uuid: videoUUID) { [weak self] videoScreen in guard let self = self, let videoScreen = videoScreen, let imageAnchor = imageAnchor else { return } self.placeVideoScreen(videoScreen: videoScreen, imageAnchor: imageAnchor, uuid: videoUUID) }
关键修复点总结
- 确保VideoMaterial绑定的AVPlayer在初始化时已持有就绪状态的AVPlayerItem
- 异步等待AVAsset加载完成,避免使用未就绪的资源创建PlayerItem
- 修正Notification监听对象,保证视频循环逻辑的有效性
- 维持对AVPlayer的强引用(通过
videoPlayers字典),防止Player被提前释放
内容的提问来源于stack exchange,提问作者Joao Ponte
相关产品推荐
相关产品推荐

