我正在尝试创建循环视频播放器视图。根据Apple的docs,我这样做了:
struct
VideoPlayerView: View
{
let video : Video
let isPlaying : Bool
init(video: Video, isPlaying: Bool)
{
Self.logger.info("VideoPlayerView.init: \(video.name)")
self.video = video
self.isPlaying = isPlaying
if let url = self.video.url
{
let playerItem = AVPlayerItem(url: url)
self._player = State(initialValue: AVQueuePlayer(playerItem: playerItem))
self._looper = State(initialValue: AVPlayerLooper(player: self.player!, templateItem: playerItem))
}
}
var
body: some View
{
let _ = print("VideoPlayerView body requested: “\(self.video.name)” [\(self.namespace)]", terminator: " -- ")
let _ = Self._printChanges()
VStack
{
if let player = self.player
{
VideoPlayer(player: player)
}
else
{
Text("Unable to create player.")
}
}
.onAppear()
{
Self.logger.info("Player appeared: \(self.video.name)")
self.isPlaying ? self.player?.play() : self.player?.pause()
}
.onDisappear()
{
Self.logger.info("Player disappeared: \(self.video.name)")
self.player?.pause()
self.player?.seek(to: .zero)
}
.onChange(of: self.isPlaying)
{ inOld, inNew in
Self.logger.info("isPlaying changed: \(inNew)")
inNew ? self.player?.play() : self.player?.pause()
}
}
@State private var player : AVQueuePlayer?
@State private var looper : AVPlayerLooper?
@Namespace private var namespace
static let logger = Logger(subsystem: "<>", category: "VideoPlayerView")
}
我根据当前选择将其实例化为
NavigationSplitView
细节的一部分:
detail:
{
let sortedSelection = self.selection.sorted(using: SortDescriptor(\.lastViewed))
if sortedSelection.count > 0
{
ScrollView
{
VStack
{
ForEach(sortedSelection)
{ video in
VideoPlayerView(video: video, isPlaying: self.isPlaying)
.id(video)
.aspectRatio(4.0/3.0, contentMode: .fit)
}
}
}
}
else
{
Text("Select an Item")
}
}
@Model
final
class
Video
{
var name : String
var bookmark : Data
var lastViewed : Date?
var
url: URL?
{
<get url from bookmark>
}
这种方法存在问题:
pause()
中拨打 .onDisappear()
,无论如何)。@State
在这种情况下会做什么。由于该视图的状态已经存在,它是否只是丢弃新的初始值?我想不出更好的方法来管理这个玩家状态。在
.onAppear()
中分配状态并将其设置为 nil
中的 .onDisppear()
不会改变行为;内存仍然增长并且线程继续创建。
不幸的是,苹果的示例代码存在重大缺陷,就像其他框架团队尝试 SwiftUI 时常见的那样。
@State 不能与像 AVPlayer 这样的类一起使用,这本身就是内存泄漏。
isPlaying 的@State 违反了 SwiftUI 的单一事实来源设计,因为它试图将状态复制到播放器中,而不是使用播放器作为播放的事实来源。
尝试将问题分解为单独的视图结构。