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

实现类Twitter/X的视频自动播放功能及续播问题求助

问题分析

当前实现中,每个VideoPlayerView都持有独立的VideoPlayerViewModel实例,导致信息流和详情页的同一个视频无法共享播放进度;同时旧视图的播放器未被妥善处理,出现多播放器同时播放导致的音频重叠问题。要实现Twitter/X式的跨页面续播,核心是统一管理视频播放状态与进度,并确保同一时间仅一个播放器处于活跃状态。

解决方案

通过全局单例管理器统一存储所有视频的播放进度,同时控制当前活跃的播放器,实现跨视图的状态共享与播放同步。

修改后代码

1. 全局视频播放状态管理器

import AVKit

class VideoPlaybackManager: ObservableObject {
    static let shared = VideoPlaybackManager()
    
    // 存储每个视频URL对应的播放进度
    private var playbackProgress: [URL: Double] = [:]
    // 记录当前正在播放的URL,避免多视频同时播放
    private var currentPlayingURL: URL?
    // 复用播放器实例,减少资源消耗
    private var activePlayer: AVPlayer?
    
    // 获取指定视频的已保存进度
    func getProgress(for url: URL) -> Double? {
        playbackProgress[url]
    }
    
    // 保存指定视频的播放进度
    func saveProgress(_ progress: Double, for url: URL) {
        playbackProgress[url] = progress
    }
    
    // 获取或复用指定视频的播放器实例
    func getPlayer(for url: URL) -> AVPlayer {
        if let existingPlayer = activePlayer, 
           let asset = existingPlayer.currentItem?.asset as? AVURLAsset, 
           asset.url == url {
            return existingPlayer
        }
        
        let newPlayer = AVPlayer(url: url)
        // 恢复已保存的进度
        if let progress = getProgress(for: url) {
            newPlayer.seek(to: CMTime(seconds: progress, preferredTimescale: 600))
        }
        activePlayer = newPlayer
        return newPlayer
    }
    
    // 更新当前播放的URL,自动暂停之前的视频
    func updateCurrentPlayingURL(_ url: URL?) {
        if let previousURL = currentPlayingURL, previousURL != url {
            activePlayer?.pause()
            // 保存之前视频的进度
            if let currentTime = activePlayer?.currentTime().seconds {
                saveProgress(currentTime, for: previousURL)
            }
        }
        currentPlayingURL = url
    }
}

2. 修改ViewModel依赖全局管理器

class VideoPlayerViewModel: ObservableObject {
    @Published var player: AVPlayer?
    private let url: URL
    private let playbackManager = VideoPlaybackManager.shared
    
    init(url: URL) {
        self.url = url
        player = playbackManager.getPlayer(for: url)
    }
    
    func handleViewAppear() {
        playbackManager.updateCurrentPlayingURL(url)
        player?.play()
    }
    
    func handleViewDisappear() {
        if let currentTime = player?.currentTime().seconds {
            playbackManager.saveProgress(currentTime, for: url)
        }
        player?.pause()
        // 如果当前播放的是此视频,清空活跃标记
        if playbackManager.currentPlayingURL == url {
            playbackManager.updateCurrentPlayingURL(nil)
        }
    }
}

3. 调整VideoPlayerView逻辑

import SwiftUI
import AVKit

struct VideoPlayerView: View {
    
    @StateObject private var viewModel: VideoPlayerViewModel
    
    init(url: URL) {
        _viewModel = StateObject(wrappedValue: .init(url: url))
    }
    
    var body: some View {
        VStack {
            if let player = viewModel.player {
                VideoPlayer(player: player)
                    .onAppear {
                        viewModel.handleViewAppear()
                    }
                    .onDisappear {
                        viewModel.handleViewDisappear()
                    }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}
关键说明
  1. 状态共享:通过VideoPlaybackManager单例存储所有视频的播放进度,信息流和详情页的同一个视频会读取同一份进度数据,实现续播。
  2. 播放器复用:全局维护一个活跃播放器实例,避免重复创建AVPlayer,减少内存占用,同时彻底解决多播放器同时播放的音频重叠问题。
  3. 播放同步:切换视图时自动更新当前活跃的视频URL,暂停之前的播放并保存进度,确保同一时间仅一个视频处于播放状态。
注意点
  • 如果需要支持多视频同时播放(非Twitter/X模式),可修改VideoPlaybackManager的逻辑,允许存储多个活跃播放器,但仍需确保切换视图时暂停非当前视图的播放器。
  • 若视频URL存在动态变化(如带签名的临时URL),需调整进度存储的键值,改用视频的唯一标识(如帖子ID)代替URL。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 10:57:02