Monday, June 15, 2026
HomeiOS DevelopmentSelecting and enjoying movies in Swift

Selecting and enjoying movies in Swift

[ad_1]

Let’s choose some movies!

For those who bear in mind my earlier tutorial about picture selecting in iOS, then you recognize 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 offers you an ideal overview in regards to the fundamentals.

To start with you will want so as to add some keys into your Data.plist file, since you’d wish to entry some private knowledge. You understand: privateness is essential. 🤫

<key>NSCameraUsageDescription</key>
<string>This app needs to take footage & 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 document sound.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>This app needs to save lots of footage & 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 discipline. Prepared, set, motion! 🎬

I am not going to deceive you, however I used to be slightly bit lazy this time, so our VideoPicker class might 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 discuss in regards to 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 sort: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
        guard UIImagePickerController.isSourceTypeAvailable(sort) else {
            return nil
        }

        return UIAlertAction(title: title, type: .default) { [unowned self] _ in
            self.pickerController.sourceType = sort
            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: .digital camera, 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", type: .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 adjustments. The primary one is the mediaTypes property, you need to use the "public.film" worth this time. Additionally you need to set the videoQuality property on the pickerController, as a result of 4k is at all times higher than 320. 🤪

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

Congrats, play-back time! 📹

Taking part in video information utilizing AVPlayer & UIView

Is not it nice when a webpage has some properly themed video within the background of the header? Nicely, you possibly can 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 should utilize a daily UIView subclass, then substitute its default layer with an AVPlayerLayer. It will can help you play movies direcly 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 adjustments of the media file. For instance after I first tried to document 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 skilled in any respect, nevertheless it turned out that you need to look ahead to the fee property, becuase the system is making an attempt to buffer the video and that may trigger some issues.

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

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 {
            change 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: "fee", 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: "fee")

        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 == "fee",
            let participant = self.participant,
            participant.fee == 0,
            let merchandise = participant.currentItem,
            !merchandise.isPlaybackBufferEmpty,
            CMTimeGetSeconds(merchandise.length) != CMTimeGetSeconds(participant.currentTime())
        {
            self.participant?.play()
        }
    }
}

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

If in case you have any questions you possibly can ask me on twitter and please remember to subscribe to my month-to-month e-newsletter beneath, or utilizing the hyperlink. ❤️



[ad_2]

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments