簡體   English   中英

在 SwiftUI 中更改視頻端的視圖

[英]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進行導航,但如果不是這種情況,我可以編輯答案。

這里發生了什么:

  1. 我將播放器及其“完成”的 state 移動到一個ObservableObject ——這樣,我對如何處理通知有了更多的控制。

  2. 收到AVPlayerItemDidPlayToEndTime通知后,我將videoDone設置為 true

  3. videoDone為 true 時,我在視圖層次結構中呈現NavigationLink ,並將isActive設置為videoDone

  4. 請注意,如果您的視圖沒有嵌入到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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM