Selecting and taking part in movies in Swift


Let’s decide some movies!

If you happen to bear in mind my earlier tutorial about picture choosing in iOS, then you realize that I already made fairly a reusable picker class constructed on prime of UIKit. If you do not know how the UIImagePickerController class works, please learn that tutorial first as a result of it provides you an amazing overview concerning the fundamentals.

Initially you may want so as to add some keys into your Data.plist file, since you’d prefer to entry some private information. You recognize: privateness is essential. 🤫

<key>NSCameraUsageDescription</key>
<string>This app needs to take photos & movies.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs to make use of your image & video library.</string>
<key>NSMicrophoneUsageDescription</key>
<string>This app needs to report sound.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs to save lots of photos & movies to your library.</string>

Since we’re not going to seize silent movies we even have so as to add the Privateness – Microphone Utilization Description area. Prepared, set, motion! 🎬

I am not going to misinform you, however I used to be a little bit bit lazy this time, so our VideoPicker class will probably be 90% the identical as our ImagePicker class was. You may make an summary class, no matter, I am going to present you the ultimate code, then we are able to speak concerning the variations. 😅

import UIKit

public protocol VideoPickerDelegate: class {
    func didSelect(url: URL?)
}

open class VideoPicker: NSObject {

    non-public let pickerController: UIImagePickerController
    non-public weak var presentationController: UIViewController?
    non-public weak var delegate: VideoPickerDelegate?

    public init(presentationController: UIViewController, delegate: VideoPickerDelegate) {
        self.pickerController = UIImagePickerController()

        tremendous.init()

        self.presentationController = presentationController
        self.delegate = delegate

        self.pickerController.delegate = self
        self.pickerController.allowsEditing = true
        self.pickerController.mediaTypes = ["public.movie"]
        self.pickerController.videoQuality = .typeHigh
    }

    non-public func motion(for kind: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
        guard UIImagePickerController.isSourceTypeAvailable(kind) else {
            return nil
        }

        return UIAlertAction(title: title, model: .default) { [unowned self] _ in
            self.pickerController.sourceType = kind
            self.presentationController?.current(self.pickerController, animated: true)
        }
    }

    public func current(from sourceView: UIView) {

        let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)

        if let motion = self.motion(for: .digicam, title: "Take video") {
            alertController.addAction(motion)
        }
        if let motion = self.motion(for: .savedPhotosAlbum, title: "Digital camera roll") {
            alertController.addAction(motion)
        }
        if let motion = self.motion(for: .photoLibrary, title: "Video library") {
            alertController.addAction(motion)
        }

        alertController.addAction(UIAlertAction(title: "Cancel", model: .cancel, handler: nil))

        if UIDevice.present.userInterfaceIdiom == .pad {
            alertController.popoverPresentationController?.sourceView = sourceView
            alertController.popoverPresentationController?.sourceRect = sourceView.bounds
            alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
        }

        self.presentationController?.current(alertController, animated: true)
    }

    non-public func pickerController(_ controller: UIImagePickerController, didSelect url: URL?) {
        controller.dismiss(animated: true, completion: nil)

        self.delegate?.didSelect(url: url)
    }
}

extension VideoPicker: UIImagePickerControllerDelegate {

    public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        self.pickerController(picker, didSelect: nil)
    }

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

        guard let url = data[.mediaURL] as? URL else {
            return self.pickerController(picker, didSelect: nil)
        }


        self.pickerController(picker, didSelect: url)
    }
}

extension VideoPicker: UINavigationControllerDelegate {

}

There are only a few small that modifications. The primary one is the mediaTypes property, you should utilize the “public.film” worth this time. Additionally you must set the videoQuality property on the pickerController, as a result of 4k is all the time higher than 320. 🤪

The delegate is the very last thing that modified a little bit bit. After the picker end the job you will get the .mediaURL property, which is a URL to get your media file (a.okay.a. the captured / chosen video file). If a brand new file was recorded you may also reserve it to the media library, that is simply two traces of additional code.

Congrats, play-back time! 📹

Enjoying video recordsdata utilizing AVPlayer & UIView

Is not it nice when a webpage has some properly themed video within the background of the header? Properly, you’ll be able to have the very same factor in iOS through the use of AVFoundation, UIKit and a few low-level layer magic. Don’t be concerned it is not that tough. 😬

You need to use a daily UIView subclass, then change its default layer with an AVPlayerLayer. This may assist you to play movies instantly within the view. Additionally an AVPlayer is only a easy controller object that may handle the playback and timing of a media file.

The toughest half was checking the standing modifications of the media file. For instance after I first tried to report a brand new video the payback of the participant view continually stopped after a second. I needed to seek for solutions, as a result of I am not an AVFoundation professional in any respect, however it turned out that you must look ahead to the speed property, as a result of the system is attempting to buffer the video and that may trigger some issues.

Anyway I used to be in a position to put collectively a reasonably good VideoView with some good further options like continually looping the video or selecting between the fill / match side content material modes. I am not telling you that this can be a 100% bulletproof resolution, however it’s a superb place to begin, plus it is greater than sufficient in some circumstances. 👻

import UIKit
import AVFoundation

open class VideoView: UIView {

    public enum Repeat {
        case as soon as
        case loop
    }

    override open class var layerClass: AnyClass {
        return AVPlayerLayer.self
    }

    non-public var playerLayer: AVPlayerLayer {
        return self.layer as! AVPlayerLayer
    }

    public var participant: AVPlayer? {
        get {
            self.playerLayer.participant
        }
        set {
            self.playerLayer.participant = newValue
        }
    }


    open override var contentMode: UIView.ContentMode {
        didSet {
            swap self.contentMode {
            case .scaleAspectFit:
                self.playerLayer.videoGravity = .resizeAspect
            case .scaleAspectFill:
                self.playerLayer.videoGravity = .resizeAspectFill
            default:
                self.playerLayer.videoGravity = .resize
            }
        }
    }

    public var `repeat`: Repeat = .as soon as

    public var url: URL? {
        didSet {
            guard let url = self.url else {
                self.teardown()
                return
            }
            self.setup(url: url)
        }
    }

    @out there(*, unavailable)
    override init(body: CGRect) {
        tremendous.init(body: body)

        self.initialize()
    }

    @out there(*, unavailable)
    public required init?(coder aDecoder: NSCoder) {
        tremendous.init(coder: aDecoder)

        self.initialize()
    }

    public init() {
        tremendous.init(body: .zero)

        self.translatesAutoresizingMaskIntoConstraints = false

        self.initialize()
    }

    open func initialize() {

    }

    deinit {
        self.teardown()
    }


    non-public func setup(url: URL) {

        self.participant = AVPlayer(playerItem: AVPlayerItem(url: url))

        self.participant?.currentItem?.addObserver(self,
                                              forKeyPath: "standing",
                                              choices: [.old, .new],
                                              context: nil)

        self.participant?.addObserver(self, forKeyPath: "charge", choices: [.old, .new], context: nil)


        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.itemDidPlayToEndTime(_:)),
                                               title: .AVPlayerItemDidPlayToEndTime,
                                               object: self.participant?.currentItem)

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(self.itemFailedToPlayToEndTime(_:)),
                                               title: .AVPlayerItemFailedToPlayToEndTime,
                                               object: self.participant?.currentItem)
    }

    non-public func teardown() {
        self.participant?.pause()

        self.participant?.currentItem?.removeObserver(self, forKeyPath: "standing")

        self.participant?.removeObserver(self, forKeyPath: "charge")

        NotificationCenter.default.removeObserver(self,
                                                  title: .AVPlayerItemDidPlayToEndTime,
                                                  object: self.participant?.currentItem)

        NotificationCenter.default.removeObserver(self,
                                                  title: .AVPlayerItemFailedToPlayToEndTime,
                                                  object: self.participant?.currentItem)

        self.participant = nil
    }



    @objc func itemDidPlayToEndTime(_ notification: NSNotification) {
        guard self.repeat == .loop else {
            return
        }
        self.participant?.search(to: .zero)
        self.participant?.play()
    }

    @objc func itemFailedToPlayToEndTime(_ notification: NSNotification) {
        self.teardown()
    }


    open override func observeValue(forKeyPath keyPath: String?,
                                          of object: Any?,
                                          change: [NSKeyValueChangeKey : Any]?,
                                          context: UnsafeMutableRawPointer?) {
        if keyPath == "standing", let standing = self.participant?.currentItem?.standing, standing == .failed {
            self.teardown()
        }

        if
            keyPath == "charge",
            let participant = self.participant,
            participant.charge == 0,
            let merchandise = participant.currentItem,
            !merchandise.isPlaybackBufferEmpty,
            CMTimeGetSeconds(merchandise.period) != CMTimeGetSeconds(participant.currentTime())
        {
            self.participant?.play()
        }
    }
}

I made a pattern venture for you and actually my view controller is easy as f.ck. It demonstrates each the picture choosing and the video capturing capabilities. Be happy to obtain it from The.Swift.Dev tutorials repository, it is referred to as Pickers.

Recent Articles

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Stay on op - Ge the daily news in your inbox