在 SwiftUI 中更改视频端的视图

dev*_*vlo 1 ios swift 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

解决后,如何更改 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) {
    }
}
Run Code Online (Sandbox Code Playgroud)

jn_*_*pdx 7

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())
    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里假设您想使用 a 进行导航NavigationView,但如果情况并非如此,我可以编辑答案。

这里发生了什么:

  1. 我将播放器及其“完成”状态移至ObservableObject- 这样,我可以更好地控制如何处理通知。

  2. 收到AVPlayerItemDidPlayToEndTime通知后,我设置videoDone为 true

  3. 当为 true 时,我在视图层次结构中videoDone渲染 a ,并将其设置为NavigationLinkisActivevideoDone

  4. 请注意,如果您的视图未嵌入NavigationView(请参阅我的ContentView)中,则这将不起作用 -NavigationLink如果它存在于该层次结构之外,则不会执行任何操作。

更新:如果您想返回不是新视图,则可以使用以下方法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()
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 好吧——我假设你的意思是“ContentView”而不是“HomeView”——但是,我现在明白你在说什么了。您想要导航回原始视图,而不是导航到*新*视图吗?我可以编辑我的代码来适应这一点。 (2认同)
  • 我更新了另一个如何向后导航而不是向前导航的示例。我没有尝试编辑您的代码,因为对于一个小示例来说它太多了,特别是因为没有包含所有视图,所以重新创建需要很多时间。 (2认同)