Let’s choose some movies!
When you keep in mind my earlier tutorial about picture choosing in iOS, then you realize that I already made fairly a reusable picker class constructed on high 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 incredible 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 information. You realize: 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 deceive you, however I used to be slightly bit lazy this time, so our VideoPicker
class can 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 will discuss in regards to the variations. 😅
import UIKit
public protocol VideoPickerDelegate: class {
func didSelect(url: URL?)
}
open class VideoPicker: NSObject {
personal let pickerController: UIImagePickerController
personal weak var presentationController: UIViewController?
personal 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
}
personal 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)
}
personal 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 information: [UIImagePickerController.InfoKey: Any]) {
guard let url = information[.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 slightly bit. After the picker end the job you may 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 can too reserve it to the media library, that is simply two traces of additional code.
Congrats, play-back time! 📹
Taking part in video recordsdata utilizing AVPlayer & UIView
Is not it nice when a webpage has some properly themed video within the background of the header? Effectively, you may have the very same factor in iOS by utilizing 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 exchange its default layer with an AVPlayerLayer
. This can can help you play movies immediately 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 once I first tried to report a brand new video the payback of the participant view always stopped after a second. I needed to seek for solutions, as a result of I am not an AVFoundation skilled in any respect, however it turned out that you must look ahead to the speed property, as a result of 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 always 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 answer, however it’s a very good start line, 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
}
personal 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)
}
}
@obtainable(*, unavailable)
override init(body: CGRect) {
tremendous.init(body: body)
self.initialize()
}
@obtainable(*, 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()
}
personal 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(_:)),
identify: .AVPlayerItemDidPlayToEndTime,
object: self.participant?.currentItem)
NotificationCenter.default.addObserver(self,
selector: #selector(self.itemFailedToPlayToEndTime(_:)),
identify: .AVPlayerItemFailedToPlayToEndTime,
object: self.participant?.currentItem)
}
personal func teardown() {
self.participant?.pause()
self.participant?.currentItem?.removeObserver(self, forKeyPath: "standing")
self.participant?.removeObserver(self, forKeyPath: "fee")
NotificationCenter.default.removeObserver(self,
identify: .AVPlayerItemDidPlayToEndTime,
object: self.participant?.currentItem)
NotificationCenter.default.removeObserver(self,
identify: .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.period) != CMTimeGetSeconds(participant.currentTime())
{
self.participant?.play()
}
}
}
I made a pattern undertaking for you and truthfully my view controller is straightforward as f.ck. It demonstrates each the picture choosing and the video capturing capabilities. Be at liberty to obtain it from The.Swift.Dev tutorials repository, it is referred to as Pickers.