【发布时间】:2020-01-27 19:36:18
【问题描述】:
我有一个大约 10 秒长的视频,我想在我的一个 SwiftUI 视图中作为全屏背景图像循环播放。我该如何实现?
第一个想法是使用 Swift 的 import AVFoundation,但不确定这是否是正确的路径。
【问题讨论】:
-
我的回答有帮助吗?
我有一个大约 10 秒长的视频,我想在我的一个 SwiftUI 视图中作为全屏背景图像循环播放。我该如何实现?
第一个想法是使用 Swift 的 import AVFoundation,但不确定这是否是正确的路径。
【问题讨论】:
您可以使用AV 系列框架和UIViewRepresentable 来执行此操作:
import SwiftUI
import AVKit
struct PlayerView: UIViewRepresentable {
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
}
func makeUIView(context: Context) -> UIView {
return PlayerUIView(frame: .zero)
}
}
为了让视频循环播放,我添加了一个观察者并将actionAtItemEnd 设置为.none 以支持循环播放。
当视频到达结尾时,它会执行playerItemDidReachEnd(...) 方法并寻找到视频的开头并继续循环。
该示例指向一个远程视频 URL。如果您想指向应用程序中的文件,您可以使用 Bundle.main.url 来代替:
if let fileURL = Bundle.main.url(forResource: "IMG_2770", withExtension: "MOV") {
let player = AVPlayer(url: fileURL)
// ...
}
class PlayerUIView: UIView {
private let playerLayer = AVPlayerLayer()
override init(frame: CGRect) {
super.init(frame: frame)
let url = URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!
let player = AVPlayer(url: url)
player.actionAtItemEnd = .none
player.play()
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: .AVPlayerItemDidPlayToEndTime,
object: player.currentItem)
layer.addSublayer(playerLayer)
}
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: .zero, completionHandler: nil)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
}
struct ContentView: View {
var body: some View {
NavigationView {
ZStack {
PlayerView()
.edgesIgnoringSafeArea(.all)
}
}
}
}
【讨论】:
NotificationCenter 比AVPlayerLooper 更快,你的问题是关于使用活动指示器,所以它是一个阻塞实现。
SwiftUI
作为一个对 swift 完全陌生的人,对于那些不想像我一样花几个小时调试这个的人。我的用例是尝试创建一个在后台播放视频的登录屏幕。我一直在努力解决循环不起作用,然后视频在几秒钟后停止并在持续时间后重新开始。这对我有用。
添加新视图:
import SwiftUI
import AVKit
import AVFoundation
struct WelcomeVideo: View {
var body: some View {
WelcomeVideoController()
}
}
struct WelcomeVideo_Previews: PreviewProvider {
static var previews: some View {
WelcomeVideo()
}
}
final class WelcomeVideoController : UIViewControllerRepresentable {
var playerLooper: AVPlayerLooper?
func makeUIViewController(context: UIViewControllerRepresentableContext<WelcomeVideoController>) ->
AVPlayerViewController {
let controller = AVPlayerViewController()
controller.showsPlaybackControls = false
guard let path = Bundle.main.path(forResource: "welcome", ofType:"mp4") else {
debugPrint("welcome.mp4 not found")
return controller
}
let asset = AVAsset(url: URL(fileURLWithPath: path))
let playerItem = AVPlayerItem(asset: asset)
let queuePlayer = AVQueuePlayer()
// OR let queuePlayer = AVQueuePlayer(items: [playerItem]) to pass in items
playerLooper = AVPlayerLooper(player: queuePlayer, templateItem: playerItem)
queuePlayer.play()
controller.player = queuePlayer
return controller
}
func updateUIViewController(_ uiViewController: AVPlayerViewController, context: UIViewControllerRepresentableContext<WelcomeVideoController>) {
}
}
然后将其附加到视图背景:
.background(WelcomeVideo())
注意:
干杯!
【讨论】:
这对我有用: source
var body: some View {
ZStack{
HStack{
Spacer()
.frame(width: 50)
AmbienceVid()
}
.edgesIgnoringSafeArea(.all)
}
}
struct AmbienceVid: UIViewRepresentable {
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<AmbienceVid>) {
}
func makeUIView(context: Context) -> UIView {
return PlayerUIView(frame: .zero)
}
}
class PlayerUIView: UIView {
private var playerLooper: AVPlayerLooper?
private var playerLayer = AVPlayerLayer()
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
// Load the resource
let fileUrl = Bundle.main.url(forResource: "ambiencevid", withExtension: "mp4")!
let asset = AVAsset(url: fileUrl)
let item = AVPlayerItem(asset: asset)
// Setup the player
let player = AVQueuePlayer()
playerLayer.player = player
playerLayer.videoGravity = .resizeAspectFill
layer.addSublayer(playerLayer)
// Create a new player looper with the queue player and template item
playerLooper = AVPlayerLooper(player: player, templateItem: item)
// Start the movie
player.play()
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
}
【讨论】:
如果人们正在搜索它,这是一个循环的、无控制的 macOS 实现。
import SwiftUI
import AVKit
struct NSVideoPlayer: NSViewRepresentable {
var videoURL: URL
func makeNSView(context: Context) -> AVPlayerView {
let item = AVPlayerItem(url: videoURL)
let queue = AVQueuePlayer(playerItem: item)
context.coordinator.looper = AVPlayerLooper(player: queue, templateItem: item)
let view = AVPlayerView()
view.player = queue
view.controlsStyle = .none
view.player?.playImmediately(atRate: 1)
return view
}
func updateNSView(_ nsView: AVPlayerView, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator()
}
class Coordinator {
var looper: AVPlayerLooper? = nil
}
}
【讨论】: