在 SwiftUI 中,如何将视频循环添加为全屏背景图像?
Posted
技术标签:
【中文标题】在 SwiftUI 中,如何将视频循环添加为全屏背景图像?【英文标题】:In SwiftUI, how can I add a video on loop as a fullscreen background image? 【发布时间】:2020-01-27 19:36:18 【问题描述】:我有一个大约 10 秒长的视频,我想在我的一个 SwiftUI 视图中作为全屏背景图像循环播放。我该如何实现?
第一个想法是使用 Swift 的 import AVFoundation
,但不确定这是否是正确的路径。
【问题讨论】:
我的回答有帮助吗? 【参考方案1】:您可以使用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)
【讨论】:
回答播放部分,但如何循环播放视频? 在我原来的问题标题和描述中,我还询问了循环部分? @user12533955 哦,是的,你做到了!道歉。我已经更新了答案,以便在视频结束时循环播放。 不用担心,效果很好。我的问题是,使用 NotificationCenter 循环而不是 AVPlayerLooper 有什么好处? 我想我总是发现实现NotificationCenter
比AVPlayerLooper
更快,你的问题是关于使用活动指示器,所以它是一个阻塞实现。【参考方案2】:
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())
注意:
-
确保您的视频已导入到您的项目中
将视频名称更新为您需要的名称或稍微重构以将其传入
干杯!
【讨论】:
Representables 被定义为结构体。 Coordinator 可以存储 Looper 引用。【参考方案3】:这对我有用: 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
【讨论】:
【参考方案4】:如果人们正在搜索它,这是一个循环的、无控制的 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
【讨论】:
以上是关于在 SwiftUI 中,如何将视频循环添加为全屏背景图像?的主要内容,如果未能解决你的问题,请参考以下文章
旋转 90 时如何将 TextureView 大小调整为全屏?
Silverlight播放器:点击按钮(html / javascript)将视频设置为全屏? [关闭]