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 :)
Related
I am creating a message app with SwiftUI.
I have made an Image Picker to allow sending photos.
Here is the code of my Image Picker :
import SwiftUI
struct ImagePicker: UIViewControllerRepresentable {
#Binding var image: UIImage?
#Binding var didSet: Bool
private let controller = UIImagePickerController()
var sourceType: UIImagePickerController.SourceType = .photoLibrary
func makeCoordinator() -> Coordinator {
return Coordinator(parent: self)
}
class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
let parent: ImagePicker
init(parent: ImagePicker) {
self.parent = parent
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
parent.image = info[.originalImage] as? UIImage
parent.didSet = true
picker.dismiss(animated: true)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true)
}
}
func makeUIViewController(context: Context) -> some UIViewController {
controller.sourceType = sourceType
controller.allowsEditing = false
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
When a picture is selected, it is shown in a view, so that, the user can see it before sending it.
I would like to implement a button that would unselect the image shown, but I don't know how to do it. I would like to know if there is a way to unselect the image.
Do you have any ideas ?
Thank you.
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...)?
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)
}
How do i prevent users from picking the same image twice in UIImagePickerContoroller to avoid duplication?
I tried doing it with the URLReference but its not working so I'm guessing its not the way.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let url = info[UIImagePickerControllerReferenceURL] as? NSURL{
if photosURL.contains(url){
Utilities.showMessage(message: "photo Uploaded already", sender: self, title: ErrorTitle.FRIENDS, onDismissAction: nil)
} else {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
photos.append(pickedImage)
}
}
}
dismiss(animated: true, completion: nil)
}
thanks,
You should also consider doing the picker.dismiss first and do the other logic with the image afterward. That way, you can prevent the user from tapping an image multiple times and invoking the delegate function several times.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard !picker.isBeingDismissed else {
return
}
picker.dismiss(animated: true) {
if let pickedImage = (info[UIImagePickerController.InfoKey(rawValue: UIImagePickerController.InfoKey.originalImage.rawValue)] as? UIImage) {
// do stuff with the picked image
print("Uesr picked an image \(pickedImage)")
}
}
}
swift 4
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
if let capturedImage = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage {
// do stuff
}
picker.delegate = nil
picker.dismiss(animated: true, completion: nil)
}
Seems like you haven't appended the url to the photosURL?
try this out:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let url = info[UIImagePickerControllerReferenceURL] as? NSURL{
if photosURL.contains(url){
Utilities.showMessage(message: "photo Uploaded already", sender: self, title: ErrorTitle.FRIENDS, onDismissAction: nil)
} else {
photosURL.append(url)
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
photos.append(pickedImage)
}
}
}
dismiss(animated: true, completion: nil)
}
preselectedAssetIdentifiers
If you have chosen PHPicker while creating a gallery picker in an application you created with SwiftUI, and you want to prevent this picker from selecting the selected photos over and over again, you can apply the following method.
The feature that will prevent the user from selecting the same photo twice is "preselectedAssetIdentifiers"
import Foundation
import PhotosUI
import SwiftUI
struct GalleryPicker: UIViewControllerRepresentable {
func makeCoordinator() -> Coordinator {
return GalleryPicker.Coordinator(parent: self)
}
#Binding var images: [UIImage]
#Binding var picker: Bool
#Binding var preSelected: [String]
func makeUIViewController(context: Context) -> PHPickerViewController {
var configuration = PHPickerConfiguration(photoLibrary: .shared())
configuration.preselectedAssetIdentifiers = preSelected
configuration.filter = .images
configuration.selection = .ordered
configuration.selectionLimit = 0
let picker = PHPickerViewController(configuration: configuration)
picker.delegate = context.coordinator.self
return picker
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
class Coordinator: NSObject, PHPickerViewControllerDelegate {
var parent: GalleryPicker
init(parent: GalleryPicker) {
self.parent = parent
}
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
parent.picker.toggle()
for result in results {
let provider = result.itemProvider
let assetIdentifier = result.assetIdentifier ?? ""
print("asset --> \(assetIdentifier)")
if provider.canLoadObject(ofClass: UIImage.self) {
provider.loadObject(ofClass: UIImage.self) { image, _ in
DispatchQueue.main.async {
self.parent.preSelected.append(assetIdentifier)
self.parent.images.append(image as! UIImage)
}
}
}
}
}
}
}
screenshot
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)
}
}