Home > front end >  Pick and play a video in SwiftUI — how convert from UIKit code?
Pick and play a video in SwiftUI — how convert from UIKit code?

Time:01-22

I'm working on a camera app and I've got a problem. I've never used UIKit to build an app, but a lot of the reference code does. So I tried to convert it using swiftUI but I failed. There is UIKit code which I want to convert to SwiftUI.

static func startMediaBrowser(
    delegate: UIViewController & UINavigationControllerDelegate & UIImagePickerControllerDelegate,
    sourceType: UIImagePickerController.SourceType
  ) {
    guard UIImagePickerController.isSourceTypeAvailable(sourceType)
      else { return }

    let mediaUI = UIImagePickerController()
    mediaUI.sourceType = sourceType
    mediaUI.mediaTypes = [kUTTypeMovie as String]
    mediaUI.allowsEditing = true
    mediaUI.delegate = delegate
    delegate.present(mediaUI, animated: true, completion: nil)
  }
import AVKit
import MobileCoreServices
import UIKit

class PlayVideoViewController: UIViewController {
  @IBAction func playVideo(_ sender: AnyObject) {
    VideoHelper.startMediaBrowser(delegate: self, sourceType: .savedPhotosAlbum)
  }
}

// MARK: - UIImagePickerControllerDelegate
extension PlayVideoViewController: UIImagePickerControllerDelegate {
  func imagePickerController(
    _ picker: UIImagePickerController,
    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
  ) {
    guard
      let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
      mediaType == (kUTTypeMovie as String),
      let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL
      else { return }

    dismiss(animated: true) {
      let player = AVPlayer(url: url)
      let vcPlayer = AVPlayerViewController()
      vcPlayer.player = player
      self.present(vcPlayer, animated: true, completion: nil)
    }
  }
}

// MARK: - UINavigationControllerDelegate
extension PlayVideoViewController: UINavigationControllerDelegate {
}

Here's what I've tried, and the compilation passes, but it only does UIImagePickerController() , and the delegate function I wrote doesn't work.

import SwiftUI
import AVKit
import MobileCoreServices
import UIKit

struct ContentView: View {
    @State private var isShowVideoLibrary = false
    @State private var image = UIImage()
    @State private var isShowCamara = false
    
    var body: some View {
        VStack {
            HStack{
                Button {
                    isShowVideoLibrary.toggle()
                } label: {
                    Text("Play video")
                }
            }
        }
        .sheet(isPresented: $isShowVideoLibrary) {
            VideoPicker(sourceType: .photoLibrary)
        }
    }
struct VideoPicker: UIViewControllerRepresentable {
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    @Environment(\.presentationMode) private var presentationMode
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPicker>) -> UIViewController {
        let mediaUI = UIImagePickerController()
        mediaUI.sourceType = sourceType
        mediaUI.mediaTypes = [kUTTypeMovie as String]
        mediaUI.allowsEditing = true
        mediaUI.delegate = context.coordinator
        return mediaUI
    }
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
    final class Coordinator : NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate{
        var parent: VideoPicker
        init(_ parent: VideoPicker) {
            self.parent = parent
        }
        private func imagePickerController(
            _ picker: UIImagePickerController,
            didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) -> UIViewController {
                guard
                    let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
                    mediaType == (kUTTypeMovie as String),
                    let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL
                else { return AVPlayerViewController()}
                
                // 2
                parent.presentationMode.wrappedValue.dismiss()
                    //3
                let player = AVPlayer(url: url)
                let vcPlayer = AVPlayerViewController()
                vcPlayer.player = player
                return vcPlayer
            }
        
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

CodePudding user response:

The problem you have is that you haven't implemented the correct UIImagePickerControllerDelegate function signature.

Your Coordinator has:

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

while the correct method is:

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

The method won't get called unless the signature matches exactly.


A solution

The UIImagePickerController is just used to select the image or video, you'll need additional cod to play the selected video. Luckily SwiftUI has a VideoPlayer that makes it easy:

import UniformTypeIdentifiers

struct ContentView: View {
    
    @State private var isShowVideoLibrary = false
    @State private var url: URL?
    
    var body: some View {
        Group {
            if let url {
                VideoPlayer(player: AVPlayer(url: url))
            } else {
                VStack {
                    HStack{
                        Button {
                            isShowVideoLibrary.toggle()
                        } label: {
                            Text("Play video")
                        }
                    }
                }
            }
        }
        .sheet(isPresented: $isShowVideoLibrary) {
            VideoPicker(sourceType: .photoLibrary) { url in
                self.url = url
                isShowVideoLibrary = false
            }
        }
        
    }
}

struct VideoPicker: UIViewControllerRepresentable {
    
    var sourceType: UIImagePickerController.SourceType = .photoLibrary
    let didFinish: (URL?) -> Void
    
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<VideoPicker>) -> UIViewController {
        let mediaUI = UIImagePickerController()
        mediaUI.sourceType = sourceType
        mediaUI.mediaTypes = [UTType.movie.identifier]
        mediaUI.allowsEditing = true
        mediaUI.delegate = context.coordinator
        return mediaUI
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {        
    }
    
    final class Coordinator : NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate{
        
        let didFinish: (URL?) -> Void
        
        init(didFinish: @escaping (URL?) -> Void) {
            self.didFinish = didFinish
        }
        
        // This func passes the URL back to the calling View
        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            guard
                let mediaType = info[UIImagePickerController.InfoKey.mediaType] as? String,
                mediaType == UTType.movie.identifier,
                let url = info[UIImagePickerController.InfoKey.mediaURL] as? URL
            else {
                didFinish(nil)
                return
                
            }
            
            didFinish(url)
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(didFinish: didFinish)
    }
}
  • Related