How to load image from camera or photo library in Swift [closed] - ios

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
How do you take a photo with the camera or pick an image from the photo library to be able to display it in an imageView for example?
Any help on this would be appreciated.

Full update for Swift 5+ including Github Repo
First of all we want to organize everything into a dedicated class.
import MobileCoreServices
import UIKit
class CameraProvider: NSObject {
enum PhotoLibraryTypes {
case photoLibrary, savedPhotosAlbum
var casted: UIImagePickerController.SourceType {
switch self {
case .photoLibrary: return UIImagePickerController.SourceType.photoLibrary
case .savedPhotosAlbum: return UIImagePickerController.SourceType.savedPhotosAlbum
}
}
}
public typealias SourceType = UIImagePickerController.SourceType
public typealias ImagePicker = UIImagePickerController
public typealias Delegate = UINavigationControllerDelegate & UIImagePickerControllerDelegate
private let delegate: Delegate
init(delegate: Delegate) {
self.delegate = delegate
}
// MARK: - Public
public func getImagePicker(source: PhotoLibraryTypes,
canEditPhotos: Bool = true,
onlyImages: Bool = false) throws -> ImagePicker {
do {
return try getBaseController(
source: source.casted,
allowsEditing: canEditPhotos,
onlyImages: onlyImages
)
} catch {
throw error
}
}
public func getCamera(canEditPhotos: Bool = true,
onlyImages: Bool = false) throws -> ImagePicker {
do {
let picker = try getBaseController(
source: .camera,
allowsEditing: canEditPhotos,
onlyImages: onlyImages
)
if UIImagePickerController.isCameraDeviceAvailable(.rear) {
picker.cameraDevice = .rear
} else if UIImagePickerController.isCameraDeviceAvailable(.front) {
picker.cameraDevice = .front
} else {
throw "No known camera type available"
}
picker.showsCameraControls = true
return picker
} catch {
throw error
}
}
// MARK: - Private
private func getBaseController(source: SourceType,
allowsEditing: Bool,
onlyImages: Bool) throws -> ImagePicker {
guard UIImagePickerController.isSourceTypeAvailable(source) else {
throw "Requested source not available"
}
let picker = UIImagePickerController()
let imageType = kUTTypeImage as String
picker.sourceType = source
picker.allowsEditing = allowsEditing
picker.delegate = self.delegate
if onlyImages,
let mediaTypes = UIImagePickerController.availableMediaTypes(for: source),
mediaTypes.contains(imageType){
picker.mediaTypes = [imageType]
}
return picker
}
}
extension String: LocalizedError {
public var errorDescription: String? { return self }
}
Lastly we call our CameraProvider as following.
import UIKit
class ViewController: UIViewController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let provider = CameraProvider(delegate: self)
do {
let picker = try provider.getImagePicker(source: .photoLibrary)
present(picker, animated: true)
} catch {
NSLog("Error: \(error.localizedDescription)")
}
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = (
info[UIImagePickerController.InfoKey.editedImage] as? UIImage ??
info[UIImagePickerController.InfoKey.originalImage] as? UIImage
)
picker.dismiss(animated: true, completion: nil)
}
}

Related

UIImagePickerController camera keeps open after closing

I wrote a UIViewControllerRepresentable for a VideoRecordingView in SwiftUI:
import SwiftUI
import AVFoundation
import Photos
struct VideoRecordingView: UIViewControllerRepresentable {
#Binding var videoURL: URL?
#Environment(\.viewController) private var viewControllerHolder: UIViewController?
let imagePickerController: UIImagePickerController
init(videoURL: Binding<URL?>) {
self._videoURL = videoURL
imagePickerController = UIImagePickerController()
}
func makeUIViewController(context: UIViewControllerRepresentableContext<VideoRecordingView>) -> UIImagePickerController {
imagePickerController.allowsEditing = true
imagePickerController.sourceType = .camera
imagePickerController.mediaTypes = ["public.movie"]
imagePickerController.videoMaximumDuration = .infinity
if #available(iOS 14.0, *) {
imagePickerController.videoQuality = .typeHigh
} else {
}
imagePickerController.delegate = context.coordinator
return imagePickerController
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<VideoRecordingView>) {}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
final class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var parent: VideoRecordingView
init(_ parent: VideoRecordingView) {
self.parent = parent
NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: nil) { notification in
parent.imagePickerController.stopVideoCapture()
// parent.viewControllerHolder?.dismiss(animated: true, completion: nil)
}
}
func requestAuthorizationToPhotos(completionHandler: #escaping (Bool) -> Void) {
guard PHPhotoLibrary.authorizationStatus() != .authorized else {
completionHandler(true)
return
}
PHPhotoLibrary.requestAuthorization { status in
completionHandler(status == .authorized ? true : false)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.stopVideoCapture()
parent.viewControllerHolder?.dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.stopVideoCapture()
if let videoURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL {
DispatchQueue.global(qos: .background).async {
let fileURL = videoURL.copyFileToTempDirectory()
DispatchQueue.main.async {
self.parent.videoURL = fileURL
}
self.requestAuthorizationToPhotos { granted in
if (granted && UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(videoURL.path)) {
UISaveVideoAtPathToSavedPhotosAlbum(videoURL.path, nil, nil, nil)
}
}
}
}
parent.viewControllerHolder?.dismiss(animated: true, completion: nil)
}
}
}
I use imagePickerController.stopVideoCapture() and dismiss it, when I close it.
When I close it, I exit the app and go back to the app again. (I go to the background and return to the foreground.)
At this time, a green light appears in the status bar, which means it is using the camera. While it is closed and I do not know which part of my code is using the camera.
Please help me. Thanks :)

Editing Images With PHPickerViewController SwiftUI

I want my user to be able to edit his image right after he picks it with the new PHPickerViewController, I was able to do it with UIImagePickercontroller like so:
struct ImagePicker: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var image: UIImage?
typealias UIViewControllerType = UIImagePickerController
var sourceType : UIImagePickerController.SourceType = .camera
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
let parent: ImagePicker
init(_ parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
if let uiImage = info[.editedImage] as? UIImage {
parent.image = uiImage
} else {
print("no image!")
}
parent.presentationMode.wrappedValue.dismiss()
}
}
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
let picker = UIImagePickerController()
picker.sourceType = sourceType
picker.allowsEditing = true
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
But, when updating to Xcode 12.0 I need to support the iOS 14.0 users by implanting the PHPickerViewController like so:
#available(iOS 14, *)
struct PHPicker : UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return PHPicker.Coordinator(parent1: self)
}
#Binding var image : UIImage?
#Binding var picker : Bool
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.filter = .images
config.selectionLimit = 1
let picker = PHPickerViewController(configuration: config)
picker.delegate = context.coordinator
return picker
}
func updateUIViewController(_ uiView: PHPickerViewController, context: Context) {
}
class Coordinator: NSObject, PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
for img in results {
if img.itemProvider.canLoadObject(ofClass: UIImage.self){
img.itemProvider.loadObject(ofClass: UIImage.self) { (image, err) in
guard let image = image as? UIImage else {return}
self.parent.image = image
self.parent.picker = false
}
} else {
print("error getting image")
}
}
}
var parent : PHPicker
init(parent1: PHPicker) {
parent = parent1
}
}
}
How to I enable the user to edit his image (cutting, changing size, and so on...)?

How to send image from framework to sample project in iOS Swift?

I have implemented document scanner using visionKit. Initially i have faced camera dismiss issue and it fixed. Now i am trying to send image after camera dismiss from framework to sample project.
I have tried using completion handler, but it does not work.
Here is the code for framework:
public class A8Scan: NSObject, VNDocumentCameraViewControllerDelegate {
var imageNew: UIImage?
var statusImage: UIImageView?
private var clientView: UIViewController?
public init(_ viewController:UIViewController){
self.clientView = viewController
}
public func showScanner(imgData: UIImage?){
self.createTaskController(img: imgData)
print("Called Build")
}
private func createTaskController(img: UIImage?){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: {
self.imageNew = img
})
}
public func imageFromFile(result: #escaping (_ image: UIImage?) -> Void){
//the image
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
if imageNew != nil {
result(imageNew)
}
else{
//callback nil so the app does not pause infinitely if
//the error != nil
result(nil)
}
}
func set(image: UIImage) {
self.statusImage?.image = image
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
print("new image::\(newImage.size)")
controller.dismiss(animated: true, completion: nil)
// processImage(newImage)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
public func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
}
Here is the code sample project:
#IBAction func btnAction(_ sender: Any) {
// A8Scan(self).showScanner()
// A8Scan(self).showScanner(imgData: im)
// print("nn", A8Scan(self).showScanner(imgData: im))
A8Scan(self).imageFromFile { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
// p()
}
func p (){
ScannerViewController().imageFromFile{(image: UIImage?) -> Void in
//use the image that was just retrieved
print("image data", image)
}
}
My issue is after camera dismiss from framework it does not send image from framework to sample project.
Any help much appreciated pls....
1-
Add this inside your framework
var callBack((UIImage)-())?
public func show() {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.clientView?.present(scannerViewController,animated:true,completion: nil)
}
2-
let newImage = compressedImage(originalImage)
callBack?(newImage)
3-
Then inside the vc that uses it
let vc = A8Scan(self)
vc.show()
vc.callBack = { [weak self] image in
}
As you are using a middle man (A8Scan) NSObject to present a controller. the A8Scan will get deallocated immediately after presenting the controller which you can confirm by keeping a deinit function inside A8Scan.
A workaround for this is to make your NSObject (A8Scan) as a singleton ie add
static let shared = A8Scan()
var callBack: ((_ image: UIImage?) -> Void)?
remove the existing init method and add the viewcontroller property into the imageFromFile: method, your function will look like this
public func imageFromFile(sender: UIViewController, result: #escaping (_ image: UIImage?) -> Void){
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
self.callBack = result
sender.present(scannerViewController,animated:true,completion: nil)
}
public func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}
let originalImage = scan.imageOfPage(at: 0)
let newImage = compressedImage(originalImage)
imageNew = newImage
set(image: imageNew!)
callBack?(imageNew!)
controller.dismiss(animated: true, completion: nil)
}
and finally in your sample project,
#IBAction func btnAction(_ sender: Any) {
A8Scan.shared.imageFromFile(sender: self) { (image) in
if image != nil {
print(image!)
} else {
print("something went wrong")
}
}
}

How can I assign a delegate for UIVideoEditorController in ReactNative when the module I am working on is just classes

func previewRecording (withFileName fileURL: String) {
if UIVideoEditorController.canEditVideo(atPath: fileURL) {
let rootView = UIApplication.getTopMostViewController()
let editController = UIVideoEditorController()
editController.videoPath = fileURL
// editController.delegate = ?????
rootView?.present(editController, animated: true, completion: nil)
} else {
}
}
^ current code
I have gone around the internet in circles a few times trying to figure this out. What would be the best approach for specifying a delegate for the UIVideoEditorController here? This is a react-native module where there is no ViewController, just utility classes.
Some simple example code I have come across
extension SomeViewController :
UIVideoEditorControllerDelegate,
UINavigationControllerDelegate {
func videoEditorController(_ editor: UIVideoEditorController,
didSaveEditedVideoToPath editedVideoPath: String) {
print("saved!")
}
}
I am just loss about how to make this happen in my module though.
#objc class ScreenRecordCoordinator: NSObject
{
let previewDelegateView = PreviewDelegateView()
// .......
editController.delegate = previewDelegateView
}
class PreviewDelegateView: UIViewController, UINavigationControllerDelegate, UIVideoEditorControllerDelegate {
var isSaved:Bool = false
func videoEditorController(_ editor: UIVideoEditorController, didSaveEditedVideoToPath editedVideoPath: String) {
print("save called")
if(!self.isSaved) {
self.isSaved = true
print("trimmed video saved!")
editor.dismiss(animated: true, completion: {
ReplayFileUtil.replaceItem(at: URL(fileURLWithPath: editor.videoPath), with: URL(fileURLWithPath: editedVideoPath))
self.isSaved = false
})
}
}
}

iOS 11 double tap on image dismisses UIImagePickerController and presenter view controller

We have a photo picker made with UIImagePickerController.
When making double tap (instead of one tap) the photo from gallery.
On iOS 10: UIImagePickerController is dismissed
On iOS 11: UIImagePickerController is dismissed and presenting view controller is dismissed as well :0
Is it iOS 11 bug or we have to adjust something?
Our code:
let vc = UIImagePickerController()
vc.delegate = self
vc.modalPresentationStyle = .overFullScreen
vc.allowsEditing = false
rootVC.present(vc, animated: true) // `rootVC` also presented modally.
Instead of self.dismiss(), use picker.dismiss()
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any])
{
//self.dismiss(animated: true, completion: nil)
picker.dismiss(animated: true, completion: nil)
}
This will only dismiss your picker view and not the view controller.
We end up with solution: Set delegate = nil right after delegate call in didFinishPickingMediaWithInfo.
public class ImagePicker: NSObject {
private lazy var viewController = setupActionSheet()
private var rootViewController: UIViewController?
private var completionHandler: (([String: Any]) -> Void)?
private var cancellationHandler: (() -> Void)?
}
extension ImagePicker {
public func present(on: UIViewController, completionHandler: #escaping (([String: Any]) -> Void)) {
rootViewController = on
self.completionHandler = completionHandler
cancellationHandler = nil
on.presentAnimated(viewController)
}
public func present(on: UIViewController,
completionHandler: #escaping (([String: Any]) -> Void),
cancellationHandler: #escaping (() -> Void)) {
rootViewController = on
self.completionHandler = completionHandler
self.cancellationHandler = cancellationHandler
on.presentAnimated(viewController)
}
}
extension ImagePicker {
private func setupActionSheet() -> UIAlertController {
let actionSheet = UIAlertController(actionSheetWithTitle: LocalizedString.Generic.ImagePicker.addPhoto)
if UIImagePickerController.isSourceTypeAvailable(.camera) {
actionSheet.addDefaultAction(LocalizedString.Generic.ImagePicker.takePhoto) { [weak self] _ in
self?.presentImagePicker(.camera)
}
}
actionSheet.addDefaultAction(LocalizedString.Generic.ImagePicker.selectPhoto) { [weak self] _ in
self?.presentImagePicker(.photoLibrary)
}
actionSheet.addCancelAction(LocalizedString.Generic.ButtonTitle.cancel) { [weak self] _ in
self?.cancellationHandler?()
}
return actionSheet
}
private func presentImagePicker(_ sourceType: UIImagePickerControllerSourceType) {
let vc = UIImagePickerController()
vc.delegate = self
vc.allowsEditing = false
vc.sourceType = sourceType
rootViewController?.present(vc, animated: true)
}
}
extension ImagePicker: UIImagePickerControllerDelegate {
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String: Any]) {
picker.delegate = nil /// It prevents to call delegate when user taps on a few images very fast. seems iOS 11 issue only.
if picker.sourceType == .camera, let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
picker.dismiss(animated: true) {
self.completionHandler?(info)
}
}
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true) {
self.cancellationHandler?()
}
}
}
Usage:
// Somewhere in view controller code.
imagePicker = ImagePicker()
imagePicker?.present(on: self) { [weak self] in
self?.imagePicker = nil
self?.viewModel.addImage($0)
}

Resources