我有一个自定义的视频播放器,我在一个swift ui视图中使用。我希望能够播放/暂停,静音/取消静音,并快进从swift ui视图的视频。没有控制功能目前正在工作,视频播放器工作正常,但当我尝试设置self.videoDuration = item.duration.seconds时,出现错误“Can 't Publish changes when the view updates”.如何正确实现播放/暂停、静音/取消静音、快进?
import Foundation
import SwiftUI
import AVKit
struct UserStories: View {
@State private var isPlaying = true
@State private var videoDuration: Double = 0
@State private var isMuted = false
var body: some View {
VStack {
LegacyVideoPlayer(url: URL(string: "")!, isPlaying: $isPlaying, videoDuration: $videoDuration, isMuted: $isMuted)
HStack {
Button("Play") {
isPlaying = true
}
Button("Pause") {
isPlaying = false
}
Button("Toggle Mute") {
isMuted.toggle()
}
}
Text("Video Duration: \(videoDuration)")
}
}
}
public struct LegacyVideoPlayer: UIViewControllerRepresentable {
let url: URL
@Binding var isPlaying: Bool
@Binding var videoDuration: Double
@Binding var isMuted: Bool
public func makeCoordinator() -> CustomPlayerCoordinator {
CustomPlayerCoordinator(customPlayer: self)
}
public func makeUIViewController(context: UIViewControllerRepresentableContext<LegacyVideoPlayer>) -> LegacyAVPlayerViewController {
let controller = LegacyAVPlayerViewController(videoDuration: $videoDuration, isMuted: $isMuted)
controller.delegate = context.coordinator
makeAVPlayer(in: controller, context: context)
playIfNeeded(controller.player)
return controller
}
public func updateUIViewController(_ uiViewController: LegacyAVPlayerViewController, context: UIViewControllerRepresentableContext<LegacyVideoPlayer>) {
makeAVPlayer(in: uiViewController, context: context)
playIfNeeded(uiViewController.player)
uiViewController.isMuted = isMuted
}
private func makeAVPlayer(in controller: LegacyAVPlayerViewController, context: UIViewControllerRepresentableContext<LegacyVideoPlayer>) {
let item = AVPlayerItem(url: url)
let player = AVQueuePlayer(playerItem: item)
let loopingPlayer = AVPlayerLooper(player: player, templateItem: item)
controller.videoGravity = AVLayerVideoGravity.resizeAspectFill
context.coordinator.loopingPlayer = loopingPlayer
controller.player = player
controller.showsPlaybackControls = false
}
private func playIfNeeded(_ player: AVPlayer?) {
if isPlaying { player?.play() }
else { player?.pause() }
}
}
public class LegacyAVPlayerViewController: AVPlayerViewController {
@Binding var videoDuration: Double
@Binding var isMuted: Bool
private var rateObserver: NSKeyValueObservation?
private var durationObserver: NSKeyValueObservation?
public override var player: AVPlayer? {
willSet {
rateObserver?.invalidate()
durationObserver?.invalidate()
}
didSet {
if rateObserver == nil {
rateObserver = player?.observe(\AVPlayer.rate, options: [.new], changeHandler: rateHandler(_:change:))
}
}
}
deinit {
rateObserver?.invalidate()
durationObserver?.invalidate()
}
private func rateHandler(_ player: AVPlayer, change: NSKeyValueObservedChange<Float>) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard let item = player.currentItem,
item.currentTime().seconds > 0.5,
player.status == .readyToPlay
else { return }
self.videoDuration = item.duration.seconds
}
}
public override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
player?.pause()
}
init(videoDuration: Binding<Double>, isMuted: Binding<Bool>) {
_videoDuration = videoDuration
_isMuted = isMuted
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
public class CustomPlayerCoordinator: NSObject, AVPlayerViewControllerDelegate, AVPictureInPictureControllerDelegate {
let customPlayer: LegacyVideoPlayer
public init(customPlayer: LegacyVideoPlayer) {
self.customPlayer = customPlayer
super.init()
}
public func playerViewController(_ playerViewController: AVPlayerViewController, restoreUserInterfaceForPictureInPictureStopWithCompletionHandler completionHandler: @escaping (Bool) -> Void) {
completionHandler(true)
}
}
字符串
1条答案
按热度按时间n7taea2i1#
您的SwiftUI视频播放器设置看起来相当复杂:
字符串
在
UserStories
视图中,播放/暂停控件似乎处理得当。isPlaying
状态绑定到LegacyVideoPlayer
,更改此状态应播放或暂停视频。(See另一个SwiftUI视频播放器的例子也是
perimeter-inc/LegacyVideoPlayer
,用于创意/灵感。)isMuted
绑定应该控制视频的静音。请确保此状态在AVPlayer
示例中得到正确反映。当前代码中未实现快进控件。您需要一个方法来更改视频的播放位置。
错误“
cannot Publish changes when the view updates
“可能意味着在视图更新过程中更新SwiftUI state时出现问题。这可以通过确保在主线程上和视图体外部进行状态更改来解决。因此,尝试在
UserStories
中添加一个按钮来快速转发,并在LegacyVideoPlayer
中添加一个方法来处理此问题。修改持续时间更新逻辑,以确保它在主线程上完成,并且不会与SwiftUI的视图更新冲突。
在
UserStories
中:型
在
LegacyVideoPlayer
中:型
在
LegacyAVPlayerViewController
中,修改rateHandler
以确保正确更新持续时间:型
添加标志
isDurationSet
以防止多次更新。