SwiftUI:如何在使用 ImagePickerController 从设备加载视频后正确编码 AVPlayer? [英] SwiftUI: How to Properly Code AVPlayer After Loading A Video From the Device with an ImagePickerController?

查看:26
本文介绍了SwiftUI:如何在使用 ImagePickerController 从设备加载视频后正确编码 AVPlayer?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 SwiftUI 创建了一个视频播放器,它使用 imagePickerController 加载视频,然后假设从设备检索到的视频播放.我发现检索视频后视频播放器没有刷新.我不确定如何提供刷新它所需的适当 @State|@Binding.

I used SwiftUI to create a Video Player, which loads a video using an imagePickerController, and then it is suppose to play the video once retrieved from the device. I found that the Video Player was not refreshing after retrieving the video. I am not sure how to give the appropriate @State|@Binding which is necessary to refresh it.

我学会了如何使用可用的在线资源编写视频播放器.我找到了一种从我的设备加载视频并将其加载到我的视频播放器的方法.但是,当我按下播放按钮时,加载视频后,只播放了声音.我试图让视频播放器 @State|@Binding 但找不到解决方案,因为它似乎不是直观地这样做.

I learned how to code a Video Player using available online resources. And I have found a way to load a video from my device and load it to my video player. However, when I press the play button, after I have loaded the video, only the sound was played. I have tried to make the video player @State|@Binding but cannot find the solution as it does not appear to be intuitively done so.

谁能建议如何使用 SwiftUI 更新我的视频播放器代码?

Can anyone suggest how to update my code for the Video Player using SwiftUI?

P.S. 1) 您必须使用实际设备来加载视频;和 2) 滑块还没有工作.接下来我会继续努力.

P.S. 1) You must use an actual device to load video; and 2) The slider does not work yet. I will work on that next.

披露:

我从在线资源中改编了这段代码.大部分工作的原始源代码可以在以下链接中找到:

I have adapted this code from online resources. The original source code for the majority of this work can be found at these links:

如何在 SwiftUI 中打开 ImagePicker?

https://www.raywenderlich.com/5135-how-to-play-record-and-merge-videos-in-ios-and-swift

https://medium.com/@chris.mash/avplayer-swiftui-part-2-player-controls-c28b721e7e27

import SwiftUI
import AVKit
import PhotosUI
import MobileCoreServices

struct ContentView: View {

    @State var showImagePicker: Bool = false
    @State var url: URL?

    var body: some View {
        ZStack {
            VStack {
                Button(action: {
                    withAnimation {
                        self.showImagePicker.toggle()
                    }
                }) {
                    Text("Show image picker")
                }

                // The video player will needs to be a @State??? as it is not updated with UIView changes but works when no view changes occur.
                PlayerContainerView(player: AVPlayer(url: url ?? URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!))
            }

            if (showImagePicker) {
                ImagePicker(isShown: $showImagePicker, url: $url)
            }
        }
    }
}

struct PlayerView: UIViewRepresentable {
    let player: AVPlayer
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
    }
    func makeUIView(context: Context) -> UIView {
        return PlayerUIView(player: player)
    }
}

class PlayerUIView: UIView {
    private let playerLayer = AVPlayerLayer()
    init(player: AVPlayer) {
        super.init(frame: .zero)

        playerLayer.player = player
        layer.addSublayer(playerLayer)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }
}

struct PlayerContainerView : View {

    @State var seekPos = 0.0

    private let player: AVPlayer
    init(player: AVPlayer) {
        self.player = player
    }
    var body: some View {
        VStack {
            PlayerView(player: player)
            PlayerControlsView(player: player)
        }
    }
}

struct PlayerControlsView : View {
    @State var playerPaused = true
    @State var seekPos = 0.0
    let player: AVPlayer
    var body: some View {
        HStack {
            Button(action: {
                self.playerPaused.toggle()
                if self.playerPaused {
                    self.player.pause()
                }
                else {
                    self.player.play()
                }
            }) {
                Image(systemName: playerPaused ? "play" : "pause")
                    .padding(.leading, 20)
                    .padding(.trailing, 20)
            }
            Slider(value: $seekPos, from: 0, through: 1, onEditingChanged: { _ in
                guard let item = self.player.currentItem else {
                    return
                }

                let targetTime = self.seekPos * item.duration.seconds
                self.player.seek(to: CMTime(seconds: targetTime, preferredTimescale: 600))
            })
                .padding(.trailing, 20)
        }
    }
}

struct ImagePicker: UIViewControllerRepresentable {

    @Binding var isShown: Bool
    @Binding var url: URL?

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

        @Binding var isShown: Bool
        @Binding var url: URL?

        init(isShown: Binding<Bool>, url: Binding<URL?>) {
            $isShown = isShown
            $url = url
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

            let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
            guard let mediaType = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaType)] as? String,
                mediaType == (kUTTypeMovie as String),
                let uiURL = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaURL)] as? URL
                else { return }

            url = uiURL
            isShown = false
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            isShown = false
        }

    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(isShown: $isShown, url: $url)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.mediaTypes = [kUTTypeMovie as String]
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {
    }
}

fileprivate func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] {
    return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)})
}

fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
    return input.rawValue
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView(showImagePicker: true)
    }
}
#endif

视频播放器没有按预期播放视频,它只播放声音,向我表明它正在播放视频;但是,我看不到它正在播放.它仍然是一个黑匣子.

The video player does not play the video as expected, it only played the sound which indicates to me that it is playing the video; however, I cannot see it being played. It remains a black box.

更新:以下是完全编辑的代码,按预期工作(滑块除外),已在下面的评论中回答:

UPDATE: The following is the fully edited code that works as expected (except for the slider), which was answered in comments below:

import SwiftUI
import AVKit
import PhotosUI
import MobileCoreServices

struct ContentView: View {

    @State var showImagePicker: Bool = false
    @State var url: URL?

    var body: some View {
        ZStack {
            VStack {
                Button(action: {
                    withAnimation {
                        self.showImagePicker.toggle()
                    }
                }) {
                    Text("Show image picker")
                }

                PlayerContainerView(player: AVPlayer(url: url ?? URL(string: "https://bitdash-a.akamaihd.net/content/sintel/hls/playlist.m3u8")!))
            }

            if (showImagePicker) {
                ImagePicker(isShown: $showImagePicker, url: $url)
            }
        }
    }
}

struct PlayerView: UIViewRepresentable {
    let player: AVPlayer
    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
        (uiView as? PlayerUIView)?.updatePlayer(player: player)
    }

    func makeUIView(context: Context) -> UIView {
        return PlayerUIView(player: player)
    }
}

class PlayerUIView: UIView {
    private let playerLayer = AVPlayerLayer()
    init(player: AVPlayer) {
        super.init(frame: .zero)

        playerLayer.player = player
        layer.addSublayer(playerLayer)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        playerLayer.frame = bounds
    }

    func updatePlayer(player: AVPlayer) {
        self.playerLayer.player = player
    }
}

struct PlayerContainerView : View {

    @State var seekPos = 0.0

    private let player: AVPlayer
    init(player: AVPlayer) {
        self.player = player
    }
    var body: some View {
        VStack {
            PlayerView(player: player)
            PlayerControlsView(player: player)
        }
    }
}

struct PlayerControlsView : View {
    @State var playerPaused = true
    @State var seekPos = 0.0
    let player: AVPlayer
    var body: some View {
        HStack {
            Button(action: {
                self.playerPaused.toggle()
                if self.playerPaused {
                    self.player.pause()
                }
                else {
                    self.player.play()
                }
            }) {
                Image(systemName: playerPaused ? "play" : "pause")
                    .padding(.leading, 20)
                    .padding(.trailing, 20)
            }
            Slider(value: $seekPos, from: 0, through: 1, onEditingChanged: { _ in
                guard let item = self.player.currentItem else {
                    return
                }

                let targetTime = self.seekPos * item.duration.seconds
                self.player.seek(to: CMTime(seconds: targetTime, preferredTimescale: 600))
            })
                .padding(.trailing, 20)
        }
    }
}

struct ImagePicker: UIViewControllerRepresentable {

    @Binding var isShown: Bool
    @Binding var url: URL?

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

        @Binding var isShown: Bool
        @Binding var url: URL?

        init(isShown: Binding<Bool>, url: Binding<URL?>) {
            _isShown = isShown
            _url = url
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

            let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
            guard let mediaType = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaType)] as? String,
                mediaType == (kUTTypeMovie as String),
                let uiURL = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.mediaURL)] as? URL
                else { return }

            url = uiURL
            isShown = false
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            isShown = false
        }

    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(isShown: $isShown, url: $url)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.mediaTypes = [kUTTypeMovie as String]
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {
    }
}

fileprivate func convertFromUIImagePickerControllerInfoKeyDictionary(_ input: [UIImagePickerController.InfoKey: Any]) -> [String: Any] {
    return Dictionary(uniqueKeysWithValues: input.map {key, value in (key.rawValue, value)})
}

fileprivate func convertFromUIImagePickerControllerInfoKey(_ input: UIImagePickerController.InfoKey) -> String {
    return input.rawValue
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
    static var previews: some View {
        ContentView(showImagePicker: true)
    }
}
#endif

推荐答案

您将 updateUIView 留空.你应该实现它:

You left your updateUIView empty. You should implement it:

func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PlayerView>) {
    (uiView as? PlayerUIView)?.updatePlayer(player: player)
}

并将以下方法添加到您的 PlayerUIView 中:

And also add the following method to your PlayerUIView:

func updatePlayer(player: AVPlayer) {
    self.playerLayer.player = player
}

这篇关于SwiftUI:如何在使用 ImagePickerController 从设备加载视频后正确编码 AVPlayer?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆