[英]Change view on video end in SwiftUI
嘗試查找視頻的結束時間時出現錯誤
import SwiftUI
import AVKit
struct ContentViewC: View {
private let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "videoToPlay", ofType: "mp4")!))
// throw multiple errors
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: .AVPlayerItemDidPlayToEndTime, object: nil)
func playerDidFinishPlaying(note: NSNotification) {
print("video finished")
// how to change view here?
}
var body: some View {
VStack {
AVPlayerControllerRepresented(player: player)
.edgesIgnoringSafeArea(.all)
.onAppear {
player.play()
}
.onDisappear {
player.pause()
player.seek(to: .zero)
}
}
.background(Color(.black))
.edgesIgnoringSafeArea(.all)
}
}
解決后,如何更改 func playerDidFinishPlaying 中的視圖?
我的應用程序有這個“HomeView”,它有三個按鈕,當點擊時,它們導航到一個將播放視頻的新視圖。 在新視圖的視頻結束時,我想導航回這個“HomeView”
import SwiftUI
struct ContentView: View {
var body: some View {
let width: CGFloat = 400
let height: CGFloat = 400
let txtWidth: CGFloat = 300
let txtHeight: CGFloat = 100
NavigationView {
ZStack {
Image("app_bg3")
.resizable()
.edgesIgnoringSafeArea(.all)
VStack {
ZStack {
NavigationLink(destination: ContentViewC()) {
HStack(alignment: .bottom) {
Spacer()
VStack {
VStack {
Image("OneCirclePlayBtn")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: width, height: height)
Text("The")
.font(Font.custom("Nunito-Regular", size: 43))
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color(.white))
.frame(width: txtWidth, height: txtHeight, alignment: .center)
Text("Voice")
.font(Font.custom("Nunito-Regular", size: 43))
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color(.white))
.frame(width: txtWidth, height: txtHeight, alignment: .center)
.offset(y: -50)
}
}
Spacer()
}
}
}.offset(y: 100)
HStack(spacing: 350) {
NavigationLink(destination: ContentViewA()) {
VStack {
VStack {
Image("TwoCirclePlayBtn")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: width, height: height)
Text("The Cast")
.font(Font.custom("Nunito-Regular", size: 33))
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color(.white))
.frame(width: txtWidth, height: txtHeight, alignment: .center)
}
}
}
NavigationLink(destination: ContentViewB()) {
VStack {
VStack {
Image("ThreeCirclePlayBtn")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: width, height: height, alignment: .center)
Text("The Artist")
.font(Font.custom("Nunito-Regular", size: 33))
.font(.largeTitle)
.fontWeight(.bold)
.foregroundColor(Color(.white))
.frame(width: txtWidth, height: txtHeight, alignment: .center)
}
}
}
}.offset(y: -150)
}
}
}.navigationViewStyle(StackNavigationViewStyle())
.accentColor(.white)
.statusBar(hidden: true)
.background(StatusBarHideHelperView())
}
}
class StatusBarHideHelper: UIViewController {
override var prefersStatusBarHidden: Bool { true } // << important !!
}
struct StatusBarHideHelperView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
StatusBarHideHelper()
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
class PlayerViewModel : ObservableObject {
@Published var videoDone = false
let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "Early Riser", ofType: "mp4")!))
private var cancellables = Set<AnyCancellable>()
init() {
NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
.sink { (_) in
self.videoDone = true
print("Video done")
DispatchQueue.main.async {
self.videoDone = false //reset it on the next run loop
}
}.store(in: &cancellables)
}
}
struct PlayerContentView: View {
@ObservedObject var viewModel = PlayerViewModel()
var body: some View {
VStack {
AVPlayerControllerRepresented(player: viewModel.player)
.edgesIgnoringSafeArea(.all)
.onAppear {
viewModel.player.play()
}
.onDisappear {
viewModel.player.pause()
viewModel.player.seek(to: .zero)
}
if viewModel.videoDone {
NavigationLink(destination: DetailView(), isActive: $viewModel.videoDone) {
EmptyView()
}
}
}
.background(Color(.black))
.edgesIgnoringSafeArea(.all)
}
}
struct DetailView : View {
var body: some View {
Text("Detail")
}
}
struct ContentView: View {
var body: some View {
NavigationView {
PlayerContentView()
}.navigationViewStyle(StackNavigationViewStyle())
}
}
我在這里假設您想使用NavigationView
進行導航,但如果不是這種情況,我可以編輯答案。
這里發生了什么:
我將播放器及其“完成”的 state 移動到一個ObservableObject
——這樣,我對如何處理通知有了更多的控制。
收到AVPlayerItemDidPlayToEndTime
通知后,我將videoDone
設置為 true
當videoDone
為 true 時,我在視圖層次結構中呈現NavigationLink
,並將isActive
設置為videoDone
請注意,如果您的視圖沒有嵌入到NavigationView
中(請參閱我的ContentView
),這將不起作用 - 如果NavigationLink
存在於該層次結構之外,它將不會做任何事情。
更新:如果您想返回go 而不是新視圖,則使用presentationMode
(與以前相同的PlayerViewModel)可以使用以下方法:
struct PlayerContentView: View {
@ObservedObject var viewModel = PlayerViewModel()
@Environment(\.presentationMode) private var presentationMode: Binding<PresentationMode>
var body: some View {
VStack {
AVPlayerControllerRepresented(player: viewModel.player)
.edgesIgnoringSafeArea(.all)
.onAppear {
viewModel.player.play()
}
.onDisappear {
viewModel.player.pause()
viewModel.player.seek(to: .zero)
}
}
.background(Color(.black))
.edgesIgnoringSafeArea(.all)
.onReceive(viewModel.$videoDone) { (videoDone) in
if videoDone {
presentationMode.wrappedValue.dismiss()
}
}
}
}
你的觀點應該是這樣的;
struct PlayerContentView: View {
@State var isVideoPlayed = false
let playerDidFinishNotification = NotificationCenter.default.publisher(for: .AVPlayerItemDidPlayToEndTime)
private let player = AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "videoToPlay", ofType: "mp4")!))
var body: some View {
VStack {
AVPlayerControllerRepresented(player: player)
.edgesIgnoringSafeArea(.all)
.onAppear {
player.play()
}
.onDisappear {
player.pause()
player.seek(to: .zero)
}
}
.background(Color(.black))
.edgesIgnoringSafeArea(.all)
.onReceive(playerDidFinishNotification, perform: { _ in
isVideoPlayed = true
NavigationLink("Content View", destination: ContentView(), isActive: $isVideoPlayed)
})
}
}
除了導航鏈接,您還可以使用、呈現或工作表
ps 你應該用ContentViewC替換PlayerContentView
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.