I am trying to upload a video from my library to Firebase storage in my application, but it seems to be resulting in an error. This does not happen if the video has been captured using UIImagePicker, it only happens when the user pickes an already taken video. I am using XCode 11.0 beta (11M336w) and iOS 13 beta 3.
My ImagePicker class:
import UIKit
import AVFoundation
import CropViewController
public protocol ImagePickerDelegate: class {
func didSelect(image: UIImage?)
func didSelect(fileUrl: URL)
}
open class ImagePicker: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CropViewControllerDelegate {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
private var editing = false
private var croppingStyle: CropViewCroppingStyle!
public init(presentationController: UIViewController, delegate: ImagePickerDelegate, editing: Bool, croppingStyle: CropViewCroppingStyle, mediaTypes: [String]?) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.editing = editing
self.croppingStyle = croppingStyle
if let mediaTypes = mediaTypes {
self.pickerController.mediaTypes = mediaTypes
} else if let mediaTypes = UIImagePickerController.availableMediaTypes(for: .camera) {
self.pickerController.mediaTypes = mediaTypes
}
self.pickerController.sourceType = .camera
self.pickerController.showsCameraControls = true
self.pickerController.videoQuality = .typeHigh
self.pickerController.navigationBar.titleTextAttributes = nil
self.pickerController.navigationItem.rightBarButtonItem?.setTitleTextAttributes(nil, for: .normal)
self.pickerController.navigationItem.rightBarButtonItem?.setTitleTextAttributes(nil, for: .highlighted)
}
private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(type) else {
return nil
}
return UIAlertAction(title: title, style: .default) { [unowned self] _ in
self.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, animated: true)
}
}
public func present(from sourceView: UIView) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let action = self.action(for: .camera, title: "Take photo or video") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "Choose from library") {
alertController.addAction(action)
}
let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
cancel.setValue(UIColor.red, forKey: "titleTextColor")
alertController.addAction(cancel)
if UIDevice.current.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
}
self.presentationController?.present(alertController, animated: true)
}
private func pickerController(_ controller: UIImagePickerController, didSelect image: UIImage?) {
self.delegate?.didSelect(image: image)
controller.dismiss(animated: true, completion: nil)
}
private func pickerController(_ controller: UIImagePickerController, didSelectVideo videoUrl: URL) {
self.delegate?.didSelect(fileUrl: videoUrl)
controller.dismiss(animated: true, completion: nil)
}
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
var picker: UIImagePickerController!
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let videoUrl = info[.mediaURL] as? URL {
self.pickerController(picker, didSelectVideo: videoUrl)
return
}
if let image = info[.originalImage] as? UIImage {
if editing {
let cropViewController = CropViewController(croppingStyle: croppingStyle, image: image)
cropViewController.delegate = self
self.picker = picker
self.presentationController?.present(cropViewController, animated: true, completion: nil)
} else {
self.pickerController(picker, didSelect: image)
}
} else {
self.pickerController(picker, didSelect: nil)
}
}
public func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.pickerController(picker, didSelect: image)
}
}
I present the viewcontroller with the following function inside my UITableView class (I just call this function when a button has been pressed):
func choose() {
let imagePicker = ImagePicker(presentationController: self, delegate: self, editing: false, croppingStyle: .default, mediaTypes: nil)
imagePicker.present(from: view)
}
Then in the imagepicker delegate I use this code:
func didSelect(image: UIImage?) {
//
}
func didSelect(fileUrl: URL) {
let uuid = NSUUID().uuidString
let path = uuid + "." + fileUrl.pathExtension
let uploadTask = Storage.storage().reference(withPath: "message_videos/\(path)").putFile(from: fileUrl, metadata: nil) { (metadata, error) in
if let error = error {
print(error)
return
}
print("success!")
}
}
When executed, this results in the following console prints:
2019-07-05 23:41:31.967623+0200 APPLICATION_NAME[9491:1007368] Failed to issue sandbox extension for file file:///private/var/mobile/Containers/Data/PluginKitPlugin/8B54AF9B-6618-4341-85DE-2EAC29198B17/tmp/trim.FEF41C1D-7D43-4E95-84A1-36AC99FEDE14.MOV, errno = 1
2019-07-05 23:41:31.982082+0200 APPLICATION_NAME[9491:1007368] Task <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1> finished with error [-1] Error Domain=NSURLErrorDomain Code=-1 "unknown error" UserInfo={NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>"
), _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>, NSLocalizedDescription=unknown error}
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={bucket=XXXXXXXXXXXXXXXXXXXXXXX.appspot.com, _NSURLErrorFailingURLSessionTaskErrorKey=BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>, object=message_videos/BB2CB773-982B-4800-9398-B2F96ED91B6D.MOV, _NSURLErrorRelatedURLSessionTaskErrorKey=(
"BackgroundUploadTask <D08D778E-B5DD-4182-9FFE-69E892DA8EDE>.<1>"
), NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=NSURLErrorDomain, NSErrorFailingURLStringKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, NSErrorFailingURLKey=https://firebasestorage.googleapis.com/v0/b/XXXXXXXXXXXXXXXXXXXXXXX.appspot.com/o/message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV?uploadType=resumable&name=message_videos%2FBB2CB773-982B-4800-9398-B2F96ED91B6D.MOV&upload_id=AEnB2UqDOZNKSdC1TCdzQB4OV3ZS74snMqsQ4tPZRZy3iYUjO47TrpnMOrDz6HUq-_KFRAdfqhahEJsefrXe_N8T-jF8MS05cA&upload_protocol=resumable, ResponseErrorCode=-1}
Is there a way to resolve this?
So I was able to solve this issue in my app by converting the video to Data and uploading that using the putData function from Firebase Storage API.
I'm not exactly sure why this change was necessary with iOS 13, but I hope this helps.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
//ensure user picked a video and you have the URL
guard let mediaType = info[.mediaType] as? String, mediaType == "public.movie", let movieURL = info[.mediaURL] as? URL else {
dismiss(animated: true)
return
}
uploadVideo(url: movieURL)
//continue code your here...
}
private func uploadVideo(url: URL) {
do {
//convert video to Data
let videoData = try Data(contentsOf: movieURL)
//build storage "path"
let uniqueID = UUID().uuidString
let path = "message_videos/" + uniqueID
let storageRef = Storage.storage().reference().child(path)
//use metadata to specify it's a video
let uploadMetadata = StorageMetadata()
uploadMetadata.contentType = "video/quicktime"
//upload via putData
let uploadTask = storageRef.putData(videoData, metadata: uploadMetadata) { (metadata, error) in
if let error = error {
//handle upload error
} else {
//successful upload
}
} catch {
//handle video -> Data error
}
}
}
Related
PickerController class, where I create the UIImagePickerController:
public protocol ImagePickerDelegate: class {
func didSelect(image: UIImage?)
}
open class ImagePicker: NSObject {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.mediaTypes = ["public.image"]
}
private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(type) else {
return nil
}
return UIAlertAction(title: title, style: .default) { [unowned self] _ in
self.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, animated: true)
}
}
public func present(from sourceView: UIView) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let action = self.action(for: .camera, title: "Сделать фото") {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: "Выбрать из галереи") {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
alertController.popoverPresentationController?.permittedArrowDirections = [.down, .up]
}
self.presentationController?.present(alertController, animated: true)
}
private func pickerController(_ controller: UIImagePickerController, didSelect image: UIImage?) {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
}
}
extension ImagePicker: UIImagePickerControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else {
return self.pickerController(picker, didSelect: nil)
}
self.pickerController(picker, didSelect: image)
}
}
extension ImagePicker: UINavigationControllerDelegate {
}
I guess, in didFinishPickingMediaWithInfo i should do something, but don't understand what exactly
class, where i work with pickerController:
class AddPlaneImageController: AddPlaneImageView {
var imagePicker: ImagePicker!
override func viewDidLoad() {
super.viewDidLoad()
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
}
#IBAction func showImagePicker(_ sender: UIButton) {
self.imagePicker.present(from: sender)
}
#IBAction func saveButton(_ sender: Any) {
RealmModel.shared.addPlaneImage(planeImage: "", aircompany: "") // here I'm supposed to save this image as String to Realm
}
}
extension AddPlaneImageController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
self.planeImage.image = image
}
}
So, basically I don't understand, how I can turn UIImage() into String() and save it as String() too. I marked in the code, where I am supposed to do it
This is my imagepicker class
import UIKit
public protocol ImagePickerDelegate: AnyObject {
func didSelect(image: UIImage?)
}
open class ImagePicker: NSObject {
private let pickerController: UIImagePickerController
private weak var presentationController: UIViewController?
private weak var delegate: ImagePickerDelegate?
public init(presentationController: UIViewController, delegate: ImagePickerDelegate) {
self.pickerController = UIImagePickerController()
super.init()
self.presentationController = presentationController
self.delegate = delegate
self.pickerController.delegate = self
self.pickerController.allowsEditing = true
self.pickerController.mediaTypes = ["public.image"]
}
private func action(for type: UIImagePickerController.SourceType, title: String) -> UIAlertAction? {
guard UIImagePickerController.isSourceTypeAvailable(type) else {
return nil
}
return UIAlertAction(title: title, style: .default) { [unowned self] _ in
self.pickerController.sourceType = type
self.presentationController?.present(self.pickerController, animated:
true)
}
}
func openCamera() {
self.pickerController.sourceType = .camera
self.presentationController?.present(self.pickerController, animated: true)
}
func openPhotoLibrary() {
self.pickerController.sourceType = .photoLibrary
self.presentationController?.present(self.pickerController, animated: true)
}
public func present(from sourceView: UIView) {
let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
if let action = self.action(for: .camera, title: Localization.set(key: .take_photo)) {
alertController.addAction(action)
}
if let action = self.action(for: .savedPhotosAlbum, title: Localization.set(key: .camera_roll)) {
alertController.addAction(action)
}
if let action = self.action(for: .photoLibrary, title: Localization.set(key: .photo_library)) {
alertController.addAction(action)
}
alertController.addAction(UIAlertAction(title: Localization.set(key: .cancel), style: .cancel, handler: nil))
if UIDevice.current.userInterfaceIdiom == .pad {
alertController.popoverPresentationController?.sourceView = sourceView
alertController.popoverPresentationController?.sourceRect = sourceView.bounds
alertController.popoverPresentationController?.permittedArrowDirections
= [.down, .up]
}
self.presentationController?.present(alertController, animated: true)
}
private func pickerController(_ controller: UIImagePickerController, didSelect image: UIImage?) {
controller.dismiss(animated: true, completion: nil)
self.delegate?.didSelect(image: image)
} }
extension ImagePicker: UIImagePickerControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.pickerController(picker, didSelect: nil)
}
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else {
return self.pickerController(picker, didSelect: nil)
}
self.pickerController(picker, didSelect: image)
} }
extension ImagePicker: UINavigationControllerDelegate {
}
This is how I used this class
self.imagePicker = ImagePicker(presentationController: self, delegate: self)
//MARK:- ImagePickerDelegate
extension AddEditGroupViewController: ImagePickerDelegate {
func didSelect(image: UIImage?) {
if let wnwrappedImage = image , let imageData = wnwrappedImage.jpegData(compressionQuality: 0){
self.changePhotoView.profileImageView.image = wnwrappedImage
imageParams = ["group_photo" : imageData]
}
}
}
It's working fine for photo gallery and saved photo album but When I try to open camera, it's giving me error as follows
**[Camera] Failed to read exposureBiasesByMode dictionary: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: data is NULL" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: data is NULL}**
Any help would be appreciated
I can't choose an image in UIImagePickerController. When I press on choose button my picker is not interactive anymore. I can only dismiss it. Here is what im getting in error log
UpdatedWebantGallery[75511:6760388] [xpc.exceptions] <NSXPCConnection: 0x6000025c2d00> connection on anonymousListener or serviceListener from pid 75514: Warning: Exception caught during invocation of selector didSelectMediaWithInfoDictionary:, dropping incoming message and invalidating the connection.
Exception: *** -[NSURL URLByAppendingPathExtension:]: component, components, or pathExtension cannot be nil.
*** -[NSURL URLByAppendingPathExtension:]: component, components, or pathExtension cannot be nil.
And Here is my code of ViewController
class AddPhotoSceneViewController: UIViewController {
internal var presenter: AddPhotoScenePresenter!
#IBOutlet weak var imageView: UIImageView!
let picker = UIImagePickerController()
let tapGestureRecgonizer = UITapGestureRecognizer()
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
override func viewDidLoad() {
super.viewDidLoad()
tapGestureRecgonizer.addTarget(self, action: #selector(showAlertSheet))
imageView.addGestureRecognizer(tapGestureRecgonizer)
setupAlert()
self.setLeftNavBarCancelButtonView(target: self, selector: #selector(self.returnToGalleryScreenTab))
}
func setupAlert() {
let uploadFromCameraAction = UIAlertAction(title: "Upload photo from camera", style: .default) { (action) in
self.picker.sourceType = .camera
AVCaptureDevice.requestAccess(for: AVMediaType.video) { response in
if response {
//access granted
} else {
}
}
self.invokePicker()
}
let uploadFromGalleryAction = UIAlertAction(title: "Upload photo from gallery", style: .default) { (action) in
self.picker.sourceType = .photoLibrary
switch PHPhotoLibrary.authorizationStatus(){
case .authorized:
self.invokePicker()
case .denied:
let modalView = ModalPopUpViewController(title: "Access Denied",
text: "If you want to upload your own photo we need your access to open gallery",
buttontext: "Open Settings",
action: #selector(self.openSettings),
target: self)
self.present(modalView, animated: true, completion: nil)
default:
return
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { (action) in
return
}
self.actionSheet.addAction(uploadFromCameraAction)
self.actionSheet.addAction(uploadFromGalleryAction)
self.actionSheet.addAction(cancelAction)
}
#objc func dismissModalView() {
self.dismiss(animated: true, completion: nil)
}
#objc func openSettings() {
self.dismiss(animated: true, completion: nil)
if let url = URL.init(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
#objc func showAlertSheet() {
self.present(self.actionSheet, animated: true) {
return
}
}
#objc func routeToAddPhotoForm() {
self.presenter?.routeToAddPhotoForm(image: self.imageView.image!)
}
#objc func returnToGalleryScreenTab() {
self.tabBarController?.selectedIndex = 0
}
func invokePicker() {
self.picker.allowsEditing = true
self.picker.delegate = self
self.present(self.picker, animated: true)
}
}
extension AddPhotoSceneViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = UUID().uuidString
let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
if let jpegData = image.jpegData(compressionQuality: 0.8) {
try? jpegData.write(to: imagePath)
}
self.imageView.image = image
self.setRightNavBarButtonView(title: "Next",target: self, selector: #selector(self.routeToAddPhotoForm))
dismiss(animated: true)
}
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
}
I Have no idea why it can be happening.
Here is a screen where I'm getting stopped
Copy/pasting your implementation here -
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.editedImage] as? UIImage else {
// Add a print statement here to check whether we are returning from here
print("Could not find editedImage")
// if you are returning from here, then `dismiss` call at the end is not being executed
// hence your picker never gets dismissed
// try using info[.originalImage] in case you end up here
return
}
let imageName = UUID().uuidString
let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
if let jpegData = image.jpegData(compressionQuality: 0.8) {
try? jpegData.write(to: imagePath)
}
self.imageView.image = image
self.setRightNavBarButtonView(title: "Next",target: self, selector: #selector(self.routeToAddPhotoForm))
dismiss(animated: true)
}
When Cancel is pressed, UIImagePickerController.imagePickerControllerDidCancel(_:) is called.
Your delegate’s implementation of this method should dismiss the picker view by calling the dismissModalViewControllerAnimated: method of the parent view controller.
Implementation of this method is optional, but expected.
You should copy/paste following method to your code.
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
}
I have a project that requires me to take a picture and send it to the provided API. I don't want to save it locally, the picture of the image should be sent to the API immediately once the picture is captured.
I've checked the Stack Overflow question swift Take a photo and save to photo library, but it seems like the image is stored locally.
Here's my code:
func cameraSetup(){
let captureDevice = AVCaptureDevice.default(for: AVMediaType.video)
do {
let input = try AVCaptureDeviceInput(device: captureDevice!)
captureSession = AVCaptureSession()
captureSession?.addInput(input)
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession!)
videoPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
videoPreviewLayer?.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height - 150)
self.cameraView.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
} catch {
print(error)
}
}
}
How can I send it off immediately instead of having to save it locally first?
For task like this, you should consider using UIImagePickerController and its Delegate methods. Best practice for this is to show user options to choose between camera and gallery (Photo library).
class MyViewController: UIViewController {
func presentImagePickerActionSheet() {
let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
func addCameraAction() {
guard UIImagePickerController.isSourceTypeAvailable(.camera) else { return assertionFailure("No camera") }
let pickerAction = UIAlertAction(title: "Camera", style: .default) { _ in
self.pickImage(source: .camera)
}
actionSheet.addAction(pickerAction)
}
func addGalleryPickerAction() {
guard UIImagePickerController.isSourceTypeAvailable(.photoLibrary) else { return assertionFailure("No gallery") }
let pickerAction = UIAlertAction(title: "Gallery", style: .default) { _ in
self.pickImage(source: .photoLibrary)
}
actionSheet.addAction(pickerAction)
}
func addRemoveActionIfNeeded() {
return() // Do your logic if needed
let pickerAction = UIAlertAction(title: "Delete", style: .destructive) { _ in
}
actionSheet.addAction(pickerAction)
}
func addCancelAction() {
let cancelAction = UIAlertAction(title: "cancel", style: .cancel, handler: nil)
actionSheet.addAction(cancelAction)
}
addCameraAction()
addGalleryPickerAction()
addRemoveActionIfNeeded()
addCancelAction()
present(actionSheet, animated: true)
}
private func pickImage(source: UIImagePickerControllerSourceType) {
guard UIImagePickerController.isSourceTypeAvailable(source) else { return assertionFailure("Source not found") }
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = source
imagePickerController.delegate = self
imagePickerController.allowsEditing = true
present(imagePickerController, animated: true)
}
}
extension MyViewController: UIImagePickerControllerDelegate {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
defer { picker.dismiss(animated: true) }
let editedImage = info[UIImagePickerControllerEditedImage]
let originalImage = info[UIImagePickerControllerOriginalImage]
guard let image = (editedImage ?? originalImage) as? UIImage else { return assertionFailure("Image not found")}
// Do anything you want with image here
// In case of need to convert it to data:
let quality = 1.0
guard let imageData = UIImageJPEGRepresentation(image, quality) else { return assertionFailure("No image data") }
// Do anything you want with image data here
}
}
I am trying hard to understand how this works, but it's pretty hard for me. =)
I have 1 view, there is one button and one small ImageView area for preview.
The button triggers imagepickercontroller, and the UIView will display picked image.
There is no error but the image doesn't show in the UIImageView area.
var imagePicker = UIImagePickerController()
#IBOutlet var imagePreview : UIImageView
#IBAction func AddImageButton(sender : AnyObject) {
imagePicker.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
imagePicker.delegate = self
self.presentModalViewController(imagePicker, animated: true)
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingMediaWithInfo info:NSDictionary!) {
var tempImage:UIImage = info[UIImagePickerControllerOriginalImage] as UIImage
imagePreview.image = tempImage
self.dismissModalViewControllerAnimated(true)
}
func imagePickerControllerDidCancel(picker: UIImagePickerController!) {
self.dismissModalViewControllerAnimated(true)
}
You're grabbing a UIImage named UIImagePickerControllerOriginalImage and there exists no such image. You're meant to grab the UIImage with the key UIImagePickerControllerOriginalImage from the editingInfo dictionary:
let tempImage = editingInfo[UIImagePickerControllerOriginalImage] as! UIImage
Details
Xcode 11.4.1 (11E503a), Swift 5.2
More
How to build own photo picker
Solution
import UIKit
import AVFoundation
import Photos
protocol ImagePickerDelegate: class {
func imagePicker(_ imagePicker: ImagePicker, grantedAccess: Bool,
to sourceType: UIImagePickerController.SourceType)
func imagePicker(_ imagePicker: ImagePicker, didSelect image: UIImage)
func cancelButtonDidClick(on imageView: ImagePicker)
}
class ImagePicker: NSObject {
private weak var controller: UIImagePickerController?
weak var delegate: ImagePickerDelegate? = nil
func dismiss() { controller?.dismiss(animated: true, completion: nil) }
func present(parent viewController: UIViewController, sourceType: UIImagePickerController.SourceType) {
let controller = UIImagePickerController()
controller.delegate = self
controller.sourceType = sourceType
self.controller = controller
DispatchQueue.main.async {
viewController.present(controller, animated: true, completion: nil)
}
}
}
// MARK: Get access to camera or photo library
extension ImagePicker {
private func showAlert(targetName: String, completion: ((Bool) -> Void)?) {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
let alertVC = UIAlertController(title: "Access to the \(targetName)",
message: "Please provide access to your \(targetName)",
preferredStyle: .alert)
alertVC.addAction(UIAlertAction(title: "Settings", style: .default, handler: { action in
guard let settingsUrl = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settingsUrl) else { completion?(false); return }
UIApplication.shared.open(settingsUrl, options: [:]) { [weak self] _ in
self?.showAlert(targetName: targetName, completion: completion)
}
}))
alertVC.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { _ in completion?(false) }))
UIApplication.shared.windows.filter { $0.isKeyWindow }.first?
.rootViewController?.present(alertVC, animated: true, completion: nil)
}
}
func cameraAsscessRequest() {
if delegate == nil { return }
let source = UIImagePickerController.SourceType.camera
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
delegate?.imagePicker(self, grantedAccess: true, to: source)
} else {
AVCaptureDevice.requestAccess(for: .video) { [weak self] granted in
guard let self = self else { return }
if granted {
self.delegate?.imagePicker(self, grantedAccess: granted, to: source)
} else {
self.showAlert(targetName: "camera") { self.delegate?.imagePicker(self, grantedAccess: $0, to: source) }
}
}
}
}
func photoGalleryAsscessRequest() {
PHPhotoLibrary.requestAuthorization { [weak self] result in
guard let self = self else { return }
let source = UIImagePickerController.SourceType.photoLibrary
if result == .authorized {
DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
self.delegate?.imagePicker(self, grantedAccess: result == .authorized, to: source)
}
} else {
self.showAlert(targetName: "photo gallery") { self.delegate?.imagePicker(self, grantedAccess: $0, to: source) }
}
}
}
}
// MARK: UINavigationControllerDelegate
extension ImagePicker: UINavigationControllerDelegate { }
// MARK: UIImagePickerControllerDelegate
extension ImagePicker: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.editedImage] as? UIImage {
delegate?.imagePicker(self, didSelect: image)
return
}
if let image = info[.originalImage] as? UIImage {
delegate?.imagePicker(self, didSelect: image)
} else {
print("Other source")
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
delegate?.cancelButtonDidClick(on: self)
}
}
Full Usage
Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>bla-bla-bla</string>
<key>NSCameraUsageDescription</key>
<string>bla-bla-bla</string>
ViewController (do not forget to paste solution code)
import UIKit
class ViewController: UIViewController {
private weak var imageView: UIImageView!
private lazy var imagePicker: ImagePicker = {
let imagePicker = ImagePicker()
imagePicker.delegate = self
return imagePicker
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "camera", style: .plain, target: self,
action: #selector(cameraButtonTapped))
navigationItem.rightBarButtonItem = UIBarButtonItem(title: "photo", style: .plain, target: self,
action: #selector(photoButtonTapped))
let imageView = UIImageView(frame: CGRect(x: 40, y: 80, width: 200, height: 200))
imageView.backgroundColor = .lightGray
view.addSubview(imageView)
self.imageView = imageView
}
#objc func photoButtonTapped(_ sender: UIButton) { imagePicker.photoGalleryAsscessRequest() }
#objc func cameraButtonTapped(_ sender: UIButton) { imagePicker.cameraAsscessRequest() }
}
// MARK: ImagePickerDelegate
extension ViewController: ImagePickerDelegate {
func imagePicker(_ imagePicker: ImagePicker, didSelect image: UIImage) {
imageView.image = image
imagePicker.dismiss()
}
func cancelButtonDidClick(on imageView: ImagePicker) { imagePicker.dismiss() }
func imagePicker(_ imagePicker: ImagePicker, grantedAccess: Bool,
to sourceType: UIImagePickerController.SourceType) {
guard grantedAccess else { return }
imagePicker.present(parent: self, sourceType: sourceType)
}
}
Images
import MobileCoreServices
class SecondViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBOutlet var img:UIImageView!=nil
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning()
{
super.didReceiveMemoryWarning()
}
#IBAction func buttonTapped(AnyObject)
{
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.PhotoLibrary){
println("Button capture")
var imag = UIImagePickerController()
imag.delegate = self
imag.sourceType = UIImagePickerControllerSourceType.PhotoLibrary;
//imag.mediaTypes = [kUTTypeImage];
imag.allowsEditing = false
self.presentViewController(imag, animated: true, completion: nil)
}
}
func imagePickerController(picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!) {
let selectedImage : UIImage = image
//var tempImage:UIImage = editingInfo[UIImagePickerControllerOriginalImage] as UIImage
img.image=selectedImage
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Since swift 4.2 and xcode 10 the func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) change a bit.
at the function header the infoKey-Section changes from
didFinishPickingMediaWithInfo info: [String : Any] to
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]
To get the originalImage you now have to call:
let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage or
let image = info[.originalImage] as? UIImage
find other infoKeys here (editedImage, imageURL and more...)
In Swift 5.2, iOS 14.2
class YourViewController: UIViewController,UINavigationControllerDelegate, UIImagePickerControllerDelegate {
#IBAction func btnTapped(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = false //If you want edit option set "true"
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let tempImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
imgRoom.image = tempImage
self.dismiss(animated: true, completion: nil)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
}
Try this:
func imagePickerController(picker: UIImagePickerController!, didFinishPickingMediaWithInfo info:NSDictionary!) {
let tempImage = info[UIImagePickerControllerOriginalImage] as! UIImage
imagePreview.image = tempImage
or you can also use ? in place of !.