In my application I found a memory leak when I used the UIImagePickerController, I thought it was my application, but searching for a solution I found an Apple's sample and I also found that this sample has the same memory leak.
You can find the example in the following URL.
https://developer.apple.com/library/content/samplecode/PhotoPicker/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010196
According to the UIImagePickerController documentation:
https://developer.apple.com/documentation/uikit/uiimagepickercontroller
In point 5, thy said that you have to dismiss the image picker using your delegate object, in the Apple's sample the UIImagePickerDelegate is doing the dismiss.
The issue is that the memory leak is wasting approximately 21 MB of memory when you select an image and work with it.
Used Memory without Memory Leak
Used Memory with Memory Leak
Memory Leak
This is the code to present the UIImagePickerController:
#IBAction func showImagePickerForPhotoPicker(_ sender: UIBarButtonItem) {
showImagePicker(sourceType: UIImagePickerControllerSourceType.photoLibrary, button: sender)
}
fileprivate func showImagePicker(sourceType: UIImagePickerControllerSourceType, button: UIBarButtonItem) {
// If the image contains multiple frames, stop animating.
if (imageView?.isAnimating)! {
imageView?.stopAnimating()
}
if capturedImages.count > 0 {
capturedImages.removeAll()
}
imagePickerController.sourceType = sourceType
imagePickerController.modalPresentationStyle =
(sourceType == UIImagePickerControllerSourceType.camera) ?
UIModalPresentationStyle.fullScreen : UIModalPresentationStyle.popover
let presentationController = imagePickerController.popoverPresentationController
presentationController?.barButtonItem = button // Display popover from the UIBarButtonItem as an anchor.
presentationController?.permittedArrowDirections = UIPopoverArrowDirection.any
if sourceType == UIImagePickerControllerSourceType.camera {
// The user wants to use the camera interface. Set up our custom overlay view for the camera.
imagePickerController.showsCameraControls = false
// Apply our overlay view containing the toolar to take pictures in various ways.
overlayView?.frame = (imagePickerController.cameraOverlayView?.frame)!
imagePickerController.cameraOverlayView = overlayView
}
present(imagePickerController, animated: true, completion: {
// Done presenting.
})
}
And this is the code in the delegate to dismiss the UIImagePickerController:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage]
capturedImages.append(image as! UIImage)
if !cameraTimer.isValid {
// Timer is done firing so Finish up until the user stops the timer from taking photos.
finishAndUpdate()
} else {
dismiss(animated: true, completion: nil)
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: {
// Done cancel dismiss of image picker.
})
}
fileprivate func finishAndUpdate() {
dismiss(animated: true, completion: { [weak self] in
guard let `self` = self else {
return
}
if `self`.capturedImages.count > 0 {
if self.capturedImages.count == 1 {
// Camera took a single picture.
`self`.imageView?.image = `self`.capturedImages[0]
} else {
// Camera took multiple pictures; use the list of images for animation.
`self`.imageView?.animationImages = `self`.capturedImages
`self`.imageView?.animationDuration = 5 // Show each captured photo for 5 seconds.
`self`.imageView?.animationRepeatCount = 0 // Animate forever (show all photos).
`self`.imageView?.startAnimating()
}
// To be ready to start again, clear the captured images array.
`self`.capturedImages.removeAll()
}
})
}
I'm still looking for a solution, any help will be appreciated.
I had the same issue. So did a ton of other people. Dating back to 2008. Pretty crazy.
http://iphonedevsdk.com/forum/iphone-sdk-development/3816-uiimagepickercontroller-memory-issues.html
UIImagePickerController leaking memory after launch and during taking a picture. Makes app crash after taking more than a 100 pictures
UIImagePickerController Memory Leak
Unfortunately, the best answer I could find was to use a singleton, which sucks. [i.e. Intentionally retain an instance of UIImagePickerController so you just access that every single time you want to select an image.] This is what others have suggested.
Further, I just spent an hour writing this answer with code that uses a singleton, and I just could not avoid a memory leak [although I thought I had for a second]. Maybe I'm just doing it incorrectly - feel free to try. But I won't post my dysfunctional code as an answer.
The best answer [which is how I solved my problem] is to use a third party pod/library. I used ImagePicker, and it is quick, fast, FREE, beautiful, and NO memory leaks! [MIT License]
Check it out here:
https://github.com/hyperoslo/ImagePicker
Related
I would like to edit/compress video after selection like it was in UIImagePickerController with allowsEditing = true.
Still the new PHPickerViewController doesn't have this property and Apple says that there is no such property anymore here. But there are apps in App Store that pushing "allowsEditing controller" after selecting asset from PHPickerViewController
Here is my PHPickerViewController implementation:
func openImagePicker() {
if #available(iOS 14, *) {
var configuration = PHPickerConfiguration()
configuration.preferredAssetRepresentationMode = .automatic
let picker = PHPickerViewController(configuration: configuration)
// Set the delegate
picker.delegate = self
// Present the picker
present(picker, animated: true)
} else {
// Fallback on earlier versions
imagePicker?.photoGalleryAsscessRequest()
}
}
extension EditController: PHPickerViewControllerDelegate {
#available(iOS 14, *)
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
self.dismiss(animated: true)
}
}
I investigated this issue but indeed there is no support for like as you mention allowsediting.
For example related to your question preset values for video edits was depreciated to UIImagePickerController.ImageURLExportPreset.compatible but There is no support for automatic compression. The picker will always pass the original video/image and it is up to the app to do the necessary compressions or edits.
You can check this Apple Document: imageExportPreset.
Apple specifically mentions that we should be using this new one instead of the older UIImagePickerViewController. If someone wonder more : Meet the new Photos picker
There are 3 UIViewControllers under NavigationController and tabBarController.
Push the viewControllers like this: A->B->C
In viewController C, add the following codes to let user to select image from photo library.
func btnClicked() {
let imagePickerController = UIImagePickerController()
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
self.present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage{
topIamge.image = image
}
picker.dismiss(animated: false, completion: nil)
}
Problem is that after the user select an image or cancel the selection, then press the left bar button going back to B, an error Unbalanced calls to begin/end appearance transitions for shows up, if continue going back to A, the error is printed out again.
Did I make anything wrong?
P.S. There is no error if I press the back button without presenting UIImagePickerController.
Thx.
I'm sure it doesn't related UIImagePickerController
Check these solution :
https://stackoverflow.com/a/12230777/6131436
https://stackoverflow.com/a/20925686/6131436
I know there are similar questions available on SO.
The reason why I'm starting a new topic.
I want to take multiple photos and save them into an array for later usage (when user finished taking photos)
I already read the API about showsCameraControl, takePicture() and cameraOverlayView
I managed already to capture multiple photos but I have always to confirm every photo with the "Retake/Use Photo" screen. When tipping on Use Photo, the App saves it into the array.
The same way is already working in the Workflow App from DeskConnect. They have a workflow "Take Photo" where you can choose how many photos you want to take. And they are (in my eyes) using Apple's default controls
Any help is appreciated.
Doesn't matter if it's a solution to avoid "Retake/Use Photo" or to create a custom overly view. For me it is important to keep Apple's default controls so the user feels comfortable with the well known controls.
(I also already took a look on the tutorials Apple provides).
My code so far
var imageArray = [UIImage]()
override func viewDidLoad() {
super.viewDidLoad()
openCamera()
}
func openCamera() {
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = false
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
imageArray.append(pickedImage)
print(imageArray.count)
}
self.dismiss(animated: true, completion: nil)
openCamera()
}
I'm creating an app that will capture a photo and reorient it based on the gyroscope data at the time of photo capture.
I've created functions to capture the gyro data from the phone in:
startUpdates()
and to stop capturing the data in:
stopUpdates()
I tried adding this into the UIImagePickerController:
if UIImagePickerController.isSourceTypeAvailable(.camera) {
guard ptInitials.text != "" else {
missingIdentifier(text: "Please add an identifier prior to taking a photo")
return}
startUpdates()
imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.allowsEditing = false
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.cameraCaptureMode = .photo
imagePicker.modalPresentationStyle = .fullScreen
present(imagePicker,
animated: true,
completion: nil)
}
This starts the gyro capture as the image capture process begins.
I have it passing the gyro data to an optional double, livePhotoAxis: Double?, while it is measuring this inside the "startUpdates()" function.
I was attempting to get it to "stop" capturing the data once the picture is taken so the last known gyro data would be kept in my variable and able to pass into other functions;
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
stopUpdates()
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
saveImageToDocuments(image: chosenImage, fileNameWithExtension: "image.jpg")
dismiss(animated: true, completion: nil
)
}
However, the problem is that the stopUpdates() method isn't called until after the user "confirms" they like their snapped photo (vs retaking it).
From what I've read, there is a private API with uiImagePickerControllerUserDidCaptureItem that captures the exact moment the photo is taken. I could use NSNotification to try and find this and call StopUpdates() once the photo is actually taken.
Is this correct that this is a private API? Can I use that to capture this moment or will my app get rejected?
Likewise, is there a better way to turn off my gyro updates at the exact moment of photo capture?
Thanks!
I added this code and it fixed the problem, but I still don't know if this is a private API and will be allowed in the app store or not? Or if there is a better way to accomplish this?
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "_UIImagePickerControllerUserDidCaptureItem"), object:nil, queue:nil, using: { note in
self.stopUpdates()
})
Update: This doesn't fire the EXACT moment of photo capture. It fires when the photo is "locked in". There are a few seconds (2ish) between the time the take photo button is pressed and the photo locks into place. As a result, if you move the phone during that time, the gyro data will be inaccurate.
Any thoughts on how to improve on this?
First off I want to say that I'm by no means an experienced Swift programmer, so if I am missing anything for someone to help me debug my issue - then I'm sorry about that. Just drop a comment and I'll add whatever is necessary to help you help me.
I'm following Apple's guide on how to develop iOS apps with Swift (as seen here).
But for some reason I'm getting this warning:
Instance method 'imagePickerController(picker:didFinishPickingMediaWithInfo:)' nearly matches optional requirement 'imagePickerController(_:didFinishPickingMediaWithInfo:)' of protocol 'UIImagePickerControllerDelegate'
I've attempted to suppress the issue by both using #nonobjc and setting it to private. The problem is that the new image isn't taking the old image's place. Instead it gives me this error in the output:
[Generic] Creating an image format with an unkown type is an error.
This is the code that concerns my tapGesture and the imagePicking:
// MARK: UIImagePickerControllerDelegate
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// The info dictionary contains multiple representations of the image, and this uses the original.
let selectedImage = info[UIImagePickerControllerOriginalImage] as! UIImage
// Set photoImageView to display the selected image.
photoImageView.image = selectedImage
// Dismiss the picker.
dismiss(animated: true, completion: nil)
}
// MARK: Actions
#IBAction func selectTapGestureFromPhotoLibrary(_ sender: UITapGestureRecognizer) {
// Hide the keyboard
nameTextField.resignFirstResponder()
// UIImagePickerController allows users to pick media from their photo library!
let imagePickerController = UIImagePickerController()
// Only allow photos to be picked, not taken
imagePickerController.sourceType = .photoLibrary
imagePickerController.delegate = self
present(imagePickerController, animated: true, completion: nil)
}
If my question isn't clear enough I need help figuring out why the image in my app isn't changing - even though I've followed the guide. I've already noticed some issues that I assume has to do with Xcode having been updated, but not the guide.
It might be worth mentioning that I have already set the info.plist to allow me to access all the necessary security privileges.
I'm running iOS 10.1 and Xcode 8.1
Found a similar issue that together with #Larme's comment helped me solve it:
Similar issue
The code is correct, Please check and sure that you have connect all the necessary outlets.