SwiftUI横竖屏切换时避免重建UIViewControllerRepresentable的方案
解决方案
问题核心是:SwiftUI在切换不同类型的视图(LandscapeMediaPlayerView和PotraitMediaPlayerView)时,会销毁旧视图树并创建新实例,导致内部的SwiftUIPlayer及其底层MediaPlayerViewController、AVPlayer被重建,视频播放中断。
最优实现:复用播放器实例
将播放器的底层控制器实例提升到父视图层级管理,让横屏/竖屏视图共用同一个播放器实例,避免重建。具体步骤:
- 创建一个持有播放器控制器的包装类,用
@StateObject保证其生命周期与父视图绑定:
class PlayerContainer: ObservableObject { let mediaPlayerVC: MediaPlayerViewController init() { mediaPlayerVC = MediaPlayerViewController() // 在这里初始化AVPlayer、加载视频等逻辑 } }
- 修改
UIViewControllerRepresentable,接收外部传入的播放器控制器,而非内部创建:
struct ReusablePlayerView: UIViewControllerRepresentable { let playerVC: MediaPlayerViewController func makeUIViewController(context: Context) -> MediaPlayerViewController { playerVC } func updateUIViewController(_ uiViewController: MediaPlayerViewController, context: Context) { // 在这里根据横竖屏状态调整播放器UI(比如控制条位置、全屏模式等) } }
- 父视图中持有播放器容器,横屏/竖屏视图复用同一个
ReusablePlayerView实例:
struct PlayerHostView: View { @StateObject private var playerContainer = PlayerContainer() @Environment(\.horizontalSizeClass) private var sizeClass var body: some View { Group { if sizeClass == .compact { PortraitPlayerLayout(playerView: ReusablePlayerView(playerVC: playerContainer.mediaPlayerVC)) } else { LandscapePlayerLayout(playerView: ReusablePlayerView(playerVC: playerContainer.mediaPlayerVC)) } } } } // 竖屏布局视图 struct PortraitPlayerLayout: View { let playerView: ReusablePlayerView var body: some View { VStack(spacing: 0) { playerView .frame(height: 200) // 竖屏专属UI:标题、评论区等 Text("视频标题") .padding() Spacer() } } } // 横屏布局视图 struct LandscapePlayerLayout: View { let playerView: ReusablePlayerView var body: some View { ZStack { playerView .ignoresSafeArea() // 横屏专属UI:悬浮控制条、进度条等 VStack { HStack { Button("返回") {} Spacer() } .padding() Spacer() } .foregroundColor(.white) } } }
这种方式下,横竖屏切换时只是调整布局容器,播放器控制器和AVPlayer实例完全不会被销毁,视频播放不会中断,同时保留了横屏/竖屏视图的代码拆分。
关于单例AVPlayer的问题
单例AVPlayer可以解决视频实例不被销毁的问题,但不推荐作为常规方案:
- 单例是全局生命周期,即使当前页面不需要播放器了,实例也不会被释放,容易造成内存泄漏;
- 如果APP中有多个播放器场景(比如列表页小窗播放、详情页全屏播放),单例会导致状态冲突(比如一个页面暂停播放,另一个页面的播放器也会暂停)。
只有当你的APP是全局唯一播放器(比如类似抖音的全屏短视频APP),且能严格控制单例的状态管理时,才考虑使用单例。否则,上述的视图层级复用实例的方案更安全、灵活。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

