Search code examples
iosswiftvideoavplayer

How do I get the video to play in my Video Selection Controller


I wrote a video selection controller so a user can select a video he wants to upload to firebase. Right now the problem I am having is that I can select the video but I cannot play it in the main view. It should work a little like the image/video selector in Instagram where if it's a video it automatically plays it. I don't need images at all, I only need videos to be uploaded.

What I see right now when select the video is a still of the first frame:

Video Selection Screen

I would be very greatful if someone could help me by telling me where I need to add what to making this video play automatically, or even with a play button in the middle of the screen.

My code right now to show the selection is as follow:

import UIKit
import Firebase
import Photos

private let reuseIdentifier = "SelectVideoCell"
private let headerIdentifier = "SelectVideoHeader"

class SelectVideoController: UICollectionViewController, UICollectionViewDelegateFlowLayout {

// MARK: - Properties

var images = [UIImage]()
var assets = [PHAsset]()
var selectedImage: UIImage?
var header: SelectVideoHeader?

// MARK: - Init

override func viewDidLoad() {
    super.viewDidLoad()

    configureUI()
    fetchVideos()

    //Register cell classes
    collectionView.register(SelectVideoCell.self, forCellWithReuseIdentifier: reuseIdentifier)
    collectionView.register(SelectVideoHeader.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: headerIdentifier)

    collectionView.backgroundColor = UIColor(red: 227/255, green: 227/255, blue: 227/255, alpha: 1)
}

// MARK: - UICollectionViewFlowLayout

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
    let width = view.frame.width

    return CGSize(width: width, height: width)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let width = (view.frame.width - 3) / 4
    return CGSize(width: width, height: width)
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
    return 1
}

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
    return 1
}

// MARK: - UICollectionViewDataSource

override func numberOfSections(in collectionView: UICollectionView) -> Int {
    return 1
}

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return images.count
}

override func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

    let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerIdentifier, for: indexPath) as! SelectVideoHeader

    self.header = header

    if let selectedImage = self.selectedImage {

        // Index selected image
        if let index = self.images.index(of: selectedImage) {

        // Asset associated with Selected Image
        let selectedAsset = self.assets[index]

        let videoManager = PHImageManager.default()
        let targetSize = CGSize(width: 600, height: 600)



        // Request Image
        videoManager.requestImage(for: selectedAsset, targetSize: targetSize, contentMode: .default, options: nil) { (image, info) in

            header.videoSelectView.image = image

            }
        }
    }

    return header
}

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! SelectVideoCell

    cell.videoSelectView.image = images[indexPath.row]

    return cell
}

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    self.selectedImage = images[indexPath.row]
    self.collectionView?.reloadData()

    let indexPath = IndexPath(item: 0, section: 0)
    collectionView.scrollToItem(at: indexPath, at: .bottom, animated: true)
}

@objc func handleDismiss() {
    dismiss(animated: true, completion: nil)
}

@objc func handleNext() {
    let uploadController = UploadVideoController()
    uploadController.selectedImage = header?.videoSelectView.image
    let navController = UINavigationController(rootViewController: uploadController)
    present(navController, animated: true, completion: nil)
}

func configureUI() {

    navigationController?.navigationBar.tintColor = .black
    navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
    navigationController?.navigationBar.shadowImage = (UIImage())
    //navigationItem.title = "Videos"

    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(handleDismiss))
    navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Next", style: .plain, target: self, action: #selector(handleNext))
}

func getAssetFetchOptions() -> PHFetchOptions {

    let options = PHFetchOptions()

    // Fetch Limit
    options.fetchLimit = 30

    // Sort Videos by Date
    let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)

    // Set Sort Descriptor for Options
    options.sortDescriptors = [sortDescriptor]

    // Return Options
    return options

}

func fetchVideos() {

    let allVideos = PHAsset.fetchAssets(with: .video, options: getAssetFetchOptions())

    print("Function running")

    // Fetch Videos on Background Thread
    DispatchQueue.global(qos: .background).async {

        // Enumerate Objects
        allVideos.enumerateObjects({ (asset, count, stop) in

            print("Count is \(count)")

            let videoManager = PHImageManager.default()
            let targetSize = CGSize(width: 600, height: 600)
            let options = PHImageRequestOptions()
            options.isSynchronous = true

            // Request Video representation for specific Asset
            videoManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFit, options: options, resultHandler: { (image, info) in

                if let image = image {

                    // Append image to data source
                    self.images.append(image)

                    // Append asset to data source
                    self.assets.append(asset)

                    // Set selected Image
                    if self.selectedImage == nil {
                        self.selectedImage = image
                    }

                    // Reload collectionView with images once counr has completed
                    if count == allVideos.count - 1 {

                        // Reload collectionView on main thread
                        DispatchQueue.main.async {
                            self.collectionView?.reloadData()
                        }
                    }
                }
            })
        })
    }
}

override var prefersStatusBarHidden: Bool {
    return true
}
}

Solution

  • You can play video by create instances of AVPlayer and AVPlayerLayer. Add following code in your selection view controller or create one new view controller and present by passing video url in that.

    Add following code to play video.

    import AVFoundation
    
    var avPlayerLayer                               : AVPlayerLayer?
    var avPlayer                                    : AVPlayer?
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        // You need to pass your url here.
        self.avPlayer = AVPlayer(url: url)
    
        self.avPlayerLayer = AVPlayerLayer(player: self.avPlayer!)
        self.avPlayerLayer!.frame = self.view!.bounds
    
        self.view.layer.addSublayer(self.avPlayerLayer!)
    
        self.avPlayer!.play()
    }
    

    This code will play your video immediately after your selection. You need required to pass url in avPlayer.

    I hope this will help you.