我想控制AVPlayer
的实例,该实例被实例化并分配给符合VideoPlayer
的类UIView
中的属性。为了确保清楚我的意图,在处理SwiftUI
和UIViewRepresentable
包装程序时,我整理了一个已知的模式,如下所示:
class VideoPlayer: UIView {
private let playerLayer = AVPlayerLayer()
private var previewTimer: Timer?
var previewLength: Double
var player: AVPlayer?
init(frame: CGRect, url: URL, previewLength:Double) {
self.previewLength = previewLength
super.init(frame: frame)
self.player = AVPlayer(url: url)
self.player?.volume = 0
self.player?.play()
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: CMTime.zero)
self?.player?.play()
}
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill
playerLayer.backgroundColor = UIColor.black.cgColor
previewTimer = Timer.scheduledTimer(withTimeInterval: previewLength, repeats: true, block: { (timer) in
self.player?.seek(to: CMTime(seconds: 0, preferredTimescale: CMTimeScale(1)))
})
layer.addSublayer(playerLayer)
}
required init?(coder: NSCoder) {
self.previewLength = 15
super.init(coder: coder)
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
}
包装器MyWrapper
将UIView
暴露给SwiftUI,而wrapper
可以具有公共属性,例如myState
:
struct MyWrapper: UIViewRepresentable {
var videoURL: URL
var previewLength: Double
@Binding var mySate: Bool
func makeUIView(context: Context) -> UIView {
let videoPlayer = VideoPlayer(frame: .zero, url: videoURL, previewLength: previewLength)
return videoPlayer
}
func updateUIView(_ uiView: UIView, context: Context) {
if myState {
// do something
} else {
// do something else
}
}
}
如果需要控制实例VideoPlayer
的属性,我们可能很想直接从方法UIView
中的updateUIView
对象访问属性。因此,假设我们要访问player
属性:
func updateUIView(_ uiView: UIView, context: Context) {
if myState {
uiView.player.play()
}
}
此操作失败,并显示错误Value of type 'UIView' has no member 'player'
。
要解决此问题,请在MyWrapper
中创建一个代理来引用AVPlayer
实例,如下所示:
struct MyWrapper: UIViewRepresentable {
var videoURL: URL
var previewLength: Double
@State var player: AVPlayer?
@Binding var play: Bool
func makeUIView(context: Context) -> UIView {
let videoPlayer = VideoPlayer(frame: .zero, url: videoURL, previewLength: previewLength)
DispatchQueue.main.async {
self.player = videoPlayer.player
}
return videoPlayer
}
func updateUIView(_ uiView: UIView, context: Context) {
if play {
self.player?.play()
} else {
self.player?.pause()
self.player?.rate = 0
}
}
}
Obs:我们使用DispatchQueue.main
绕过了将在Modifying state during view update, this will cause undefined behaviour
中抛出的警告makeUIView
。
这种方法有效,但是我在Apple中找不到任何文档来确认这是处理SwiftUI和UIViewRepresentable
包装程序时正确的方法,因为SwiftUI是一个黑匣子。
因此,由于这个原因,我想知道如何在SwiftUI包装器中访问符合UIViewRepresentable的属性?
答案 0 :(得分:1)
UIViewRepresentable
从声明的return / argument类型推断类型,因此只需突出显示所表示的类型
struct MyWrapper: UIViewRepresentable {
var videoURL: URL
var previewLength: Double
@Binding var mySate: Bool
func makeUIView(context: Context) -> VideoPlayer {
let videoPlayer = VideoPlayer(frame: .zero, url: videoURL, previewLength: previewLength)
return videoPlayer
}
func updateUIView(_ uiView: VideoPlayer, context: Context) {
if myState {
uiView.player.play() // now it knows that uiView is-a VideoPlayer
} else {
// do something else
}
}
}