I am new to programming in general and was having trouble with memory leak problems with the following code specifically involving the "imagePickerController." The leaks only occur after the UIimagePickerController is dismissed after selecting an image. Thank you for any help fixing this.
let imagePicked = UIImagePickerController()
#IBAction func addPhoto(_ sender: UIBarButtonItem) {
let pickerController = UIImagePickerController()
pickerController.delegate = self
pickerController.allowsEditing = false
imagePicked.delegate = self
let alertController = UIAlertController(title: "Add New Image", message: "Choose From", preferredStyle: .actionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: .default) { (action) in
pickerController.sourceType = .camera
self.present(pickerController, animated: true, completion: nil)
}
let photosLibraryAction = UIAlertAction(title: "Photos Library", style: .default) { (action) in
pickerController.sourceType = .photoLibrary
self.present(pickerController, animated: true, completion: nil)
}
let cancelAction = UIAlertAction(title: "Cancel", style: .destructive, handler: nil)
alertController.addAction(cameraAction)
alertController.addAction(photosLibraryAction)
alertController.addAction(cancelAction)
present(alertController, animated: true, completion: nil)
}
#objc func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
var newImageView = UIImageView()
newImageView = UIImageView(image: image)
newImageView.contentMode = .scaleAspectFit
newImageView.center = textView.center
textView.addSubview(newImageView)
}
dismiss(animated: true, completion: nil)
}
EDIT ONE:
Thank you I have removed #objc in front of didFinishPickingMediaWithInfo as well as the extra UIImagePickerController object, but I am still having the same problem. I have also tried picker.dismiss(animated: true, completion: nil) but that is still not fixing the problem.
And yes I am pretty sure it is a memory leak error as I've ran the tester and after putting pictures into the app from the photos library or the camera I am getting this:
picture 1
picture 2
Also after adding several pictures the app will crash with "Terminated due to memory problem"
Related
I want to offer the option to use either the photo library or the camera, but my alert to select either photos or use a camera doesn't show up when I add the present actionSheet. The compiled application goes straight to photo album without giving me the option to select camera or photo library.
This is the log from my Xcode 11:
2020-06-15 23:05:22.931477-0400 MeMe[6298:3505455] Attempt to present
on which is waiting for a delayed presention of
to complete 2020-06-15
23:05:22.931653-0400 MeMe[6298:3505455] Attempt to present
on
which is waiting for a delayed presention of to complete error in connection_block_invoke_2:
Connection interrupted
This is my code:
import UIKit
class ViewController: UIViewController, UIImagePickerControllerDelegate,
UINavigationControllerDelegate {
// OUTLETS *******************************************
// Image
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
}
// ACTIONS *******************************************
// Button
#IBAction func pickImage(_ sender: Any) {
let imagePickerController = UIImagePickerController()
// set imagePickerController as delegate
imagePickerController.delegate = self
// provide actionSheet to display the camera and photo options
let actionSheet = UIAlertController(title: "Source", message: "Take a picture or select a photo", preferredStyle: .actionSheet)
// add camera action to imagePickerController
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler:{(action:UIAlertAction) in
// check if the camera is available
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
}
else {
print("Camera not available")
}
}))
self.present(imagePickerController, animated: true, completion: nil)
// add photos action to imagePickerController
actionSheet.addAction(UIAlertAction(title: "Photos", style: .default, handler:{(action:UIAlertAction) in imagePickerController.sourceType = .photoLibrary}))
self.present(imagePickerController, animated: true, completion: nil)
// cancel
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
// assign image to imageView
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
imageView.image = image
picker.dismiss(animated: true, completion: nil)
}
// dismiss the image selector
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
}
You're trying to present the actionSheet after presenting imagePickerController which is not possible. You need to present the imagePickerController in the UIAlertAction's handler block. Here's the code:
#IBAction func pickImage(_ sender: Any) {
let imagePickerController = UIImagePickerController()
// set imagePickerController as delegate
imagePickerController.delegate = self
// provide actionSheet to display the camera and photo options
let actionSheet = UIAlertController(title: "Source", message: "Take a picture or select a photo", preferredStyle: .actionSheet)
// add camera action to imagePickerController
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler:{(action:UIAlertAction) in
// check if the camera is available
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
}
else {
print("Camera not available")
}
self.present(imagePickerController, animated: true, completion: nil)
}))
// add photos action to imagePickerController
actionSheet.addAction(UIAlertAction(title: "Photos", style: .default, handler:{(action:UIAlertAction) in
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
// cancel
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
#IBAction func pickImage(_ sender: Any) {
let imagePickerController = UIImagePickerController()
imagePickerController.delegate = self
let actionSheet = UIAlertController(title: "Source", message: "Take a picture or select a photo", preferredStyle: .actionSheet)
actionSheet.addAction(UIAlertAction(title: "Camera", style: .default, handler:{(action:UIAlertAction) in
if UIImagePickerController.isSourceTypeAvailable(.camera) {
imagePickerController.sourceType = .camera
self.present(imagePickerController, animated: true, completion: nil)
} else {
print("Camera not available")
}
}))
actionSheet.addAction(UIAlertAction(title: "Photos", style: .default, handler:{(action:UIAlertAction) in
imagePickerController.sourceType = .photoLibrary
self.present(imagePickerController, animated: true, completion: nil)
}))
// cancel
actionSheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(actionSheet, animated: true, completion: nil)
}
I've tried to save UIImage on UIViewcontroller named secondViewController, But didn't work.
Code snippet for saving image is attached.
#IBAction func takePhoto(_ sender: Any) {
cameraSession.stopRunning()
UIImageWriteToSavedPhotosAlbum(self.cameraImageView.image!, nil, nil, nil)
}
On SecondViewController, UIImage has been saved to cameraImageView.image,
But when check camera roll, nothing saved.
Does anyone know why?
Add Permission in your info.plist
"Privacy - Photo Library Additions Usage Description"
After permission granted try to save image.
#IBAction func takePhoto(_ sender: Any) {
cameraSession.stopRunning()
UIImageWriteToSavedPhotosAlbum(self.cameraImageView.image!, nil, nil, nil)
}
hey I added full code to capture image using UIImagePickerController and also added code to import a photo from photo gallery, hope it will helpful for you
import UIImagePickerControllerDelegateand create a variable to assign UIImagePickerController var imagePicker = UIImagePickerController()
and set imagePicker.delegate = self.
Create a action sheet to display options for 'Camera' and 'Photo library'.
On your button click action:
#IBAction func buttonOnClick(_ sender: UIButton)
{
self.btnEdit.setTitleColor(UIColor.white, for: .normal)
self.btnEdit.isUserInteractionEnabled = true
let alert = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in
self.openCamera()
}))
alert.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { _ in
self.openGallary()
}))
alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil))
/*If you want work actionsheet on ipad
then you have to use popoverPresentationController to present the actionsheet,
otherwise app will crash on iPad */
switch UIDevice.current.userInterfaceIdiom {
case .pad:
alert.popoverPresentationController?.sourceView = sender
alert.popoverPresentationController?.sourceRect = sender.bounds
alert.popoverPresentationController?.permittedArrowDirections = .up
default:
break
}
self.present(alert, animated: true, completion: nil)
}
func openCamera() {
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.camera))
{
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
else
{
let alert = UIAlertController(title: "Warning", message: "You don't have camera", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
func openGallary() {
imagePicker.sourceType = UIImagePickerControllerSourceType.photoLibrary
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
I am trying to create protocol where I can open UIimagePickerController with camera or Media Library according to user's choice.
here is some code:
import UIKit
protocol PFImagePickerProtocol: UIImagePickerControllerDelegate,UINavigationControllerDelegate where Self: UIViewController {
func didSelectImage(image: UIImage?, error: Bool)
func didCancelledImageSelection()
}
extension PFImagePickerProtocol {
func openImageSelector(withCorp cropEnabled:Bool) {
let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)
let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
self.openImagePicker(withCorp: cropEnabled, sourceType: .camera)
}
let library = UIAlertAction(title: "Photo Library", style: .default) { (action) in
self.openImagePicker(withCorp: cropEnabled, sourceType: .photoLibrary)
}
alertController.addAction(camera)
alertController.addAction(library)
self.present(alertController, animated: true, completion: nil)
}
private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType) {
let pickerVc = UIImagePickerController()
pickerVc.allowsEditing = cropEnabled
pickerVc.sourceType = sourceType
pickerVc.delegate = self //is this the problem?
self.present(pickerVc, animated: true, completion: nil)
}
}
extension PFImagePickerProtocol{
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
didCancelledImageSelection()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
didSelectImage(image: image, error: false)
return
} else {
didSelectImage(image: nil, error: true)
}
self.dismiss(animated: true, completion: nil)
}
}
as I run the code. function 'didFinishPickingMediaWithInfo' is not called.
I found this answer useful. but if there anything that can solved this problem. kindly share it here.
feel free to comment on code.
Please write your code as below:
protocol PFImagePickerProtocol {
func didSelectImage(image: UIImage?, error: Bool)
func didCancelledImageSelection()
}
And write your extensions as below that contains the delegate methods:
extension YourViewController {
func openImageSelector(withCorp cropEnabled:Bool) {
let alertController = UIAlertController(title: "Action Sheet", message: "What would you like to do?", preferredStyle: .actionSheet)
let camera = UIAlertAction(title: "Camera", style: .default) { (action) in
self.openImagePicker(withCorp: cropEnabled, sourceType: .camera)
}
let library = UIAlertAction(title: "Photo Library", style: .default) { (action) in
self.openImagePicker(withCorp: cropEnabled, sourceType: .photoLibrary)
}
alertController.addAction(camera)
alertController.addAction(library)
self.present(alertController, animated: true, completion: nil)
}
private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType) {
let pickerVc = UIImagePickerController()
pickerVc.allowsEditing = cropEnabled
pickerVc.sourceType = sourceType
pickerVc.delegate = self //This will set your picker delegate to view controller class & the below extension conforms the delegates.
self.present(pickerVc, animated: true, completion: nil)
}
}
extension YourViewController: UIImagePickerControllerDelegate,UINavigationControllerDelegate{
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
self.dismiss(animated: true, completion: nil)
didCancelledImageSelection()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
didSelectImage(image: image, error: false)
return
} else {
didSelectImage(image: nil, error: true)
}
self.dismiss(animated: true, completion: nil)
}
}
I think then you can write all of the above code in one single view controller like AbstractViewController which is a sub class of UIViewController, and all of your other view controllers that have this functionality have their super class as AbstractViewController.
class AbstractViewController: UIViewController {
// Do above code here
}
class OtherViewController: AbstractViewController {
// Classes that needs to implement the image picker functionality
}
class PFImagePickerManager
typealias PFImagePickerTarget = UIImagePickerControllerDelegate & UINavigationControllerDelegate
class PFImagePickerManager {
static var shared: PFImagePickerManager = PFImagePickerManager()
var target: PFImagePickerTarget!
private init() {}
func openImageSelector(target: PFImagePickerTarget, shouldCrop: Bool) {
self.target = target
let alertController = UIAlertController(title: PFConstants.PFImagePicker.actionSheetTitle, message: kEmptyStr, preferredStyle: .actionSheet)
let camera = UIAlertAction(title: PFConstants.PFImagePicker.camera, style: .default) { (action) in
self.openImagePicker(withCorp: shouldCrop, sourceType: .camera)
}
let library = UIAlertAction(title: PFConstants.PFImagePicker.photoLibrary, style: .default) { (action) in
self.openImagePicker(withCorp: shouldCrop, sourceType: .photoLibrary)
}
let cancel = UIAlertAction(title: PFConstants.PFImagePicker.cancel, style: .cancel) { (action) in
}
alertController.addAction(camera)
alertController.addAction(library)
alertController.addAction(cancel)
if let vc = target as? PFBaseViewController {
vc.present(alertController, animated: true, completion: nil)
}
}
private func openImagePicker(withCorp cropEnabled:Bool, sourceType: UIImagePickerController.SourceType) {
let pickerVc = UIImagePickerController()
pickerVc.allowsEditing = cropEnabled
pickerVc.sourceType = sourceType
pickerVc.delegate = target
if sourceType == .photoLibrary {
pickerVc.navigationBar.tintColor = UIColor.appThemePrimaryColor()
}
if let vc = target as? PFBaseViewController {
vc.present(pickerVc, animated: true, completion: nil)
}
}
}
you need to extend PFImagePickerTarget
extension YourViewController: PFImagePickerTarget {
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
PFViewUtility.dispatchOnMainThread {
self.VMCreateGR.changeDatafor(.image, data: image)
self.tblInputs.reloadData()
}
} else {
self.view.makeToast("Error while selecting image. Please try again.")
}
picker.dismiss(animated: true, completion: nil)
}
}
and to initiate image picker in ViewController
class AnyViewController: UIViewController {
// In some method like viewDidLoad call the below line
PFImagePickerManager.shared.openImageSelector(target: self, shouldCrop: true)
}
Going Crazy。
Using UIImagePickerController to take picture,
After presenting the pickerController, cosole prints
[MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles and [MC] Reading from public effective user settings.
App crashes after dismiss pickerController for 1-2 seconds.
and the console says -[AVCaptureSession release]: message sent to deallocated instance 0x1c4201080
Privacy keys already added:
and the related code sample:
func seletedImage() {
let alertAction = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
alertAction.addAction(UIAlertAction.init(title: "拍照", style: .default, handler: { [weak self] (alertCamera) in
self?.showImagePicker(sourceType: .camera)
}))
alertAction.addAction(UIAlertAction.init(title: "从相册中选择", style:.default, handler: { [weak self] (alertPhpto) in
self?.showImagePicker(sourceType: .photoLibrary)
}))
alertAction.addAction(UIAlertAction.init(title: "取消", style: .cancel, handler: nil))
self.present(alertAction, animated: true, completion: nil)
}
func showImagePicker(sourceType: UIImagePickerControllerSourceType) {
if UIImagePickerController.isSourceTypeAvailable(sourceType) == false {
if sourceType == .camera { print("未授予使用相机权限") }
if sourceType == .photoLibrary { print("未授予访问相册权限") }
return
}
let picker: UIImagePickerController = UIImagePickerController()
picker.delegate = self as UIImagePickerControllerDelegate & UINavigationControllerDelegate
picker.sourceType = sourceType
picker.allowsEditing = false
weak var weakself = self
weakself?.present(picker, animated: true, completion: nil)
}
am I missing some other important configurations?
maybe something related to GPUImage?
any help is really really appreciate.
PS. it crashes even I don't take a picture and just cancel directly. the photoLibrary type works fine.
Please handle a case that camera is not available . Please use below code
func SelectFromCamera() {
//Camera
let cameraMediaType = AVMediaTypeVideo
AVCaptureDevice.requestAccess(forMediaType: cameraMediaType) { granted in
if granted {
print("Granted access to \(cameraMediaType)")
if UIImagePickerController.isSourceTypeAvailable(
UIImagePickerControllerSourceType.camera) {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType =
UIImagePickerControllerSourceType.camera
imagePicker.mediaTypes = ["public.image"]
// imagePicker.allowsEditing = false
self.present(imagePicker, animated: true,
completion: nil)
}
} else {
//open app setting if not authorize to access camera
print("Denied access to \(cameraMediaType)")
}
}
}
How do I transfer a image to another view controller? For example this code takes a picture by using the camera functionality, the user confirms the image and displays it in the image view or can be selected from the gallery, how can I segue this to another custom class once the image has been chosen?
Here is the code in the ViewController.swift
#IBAction func btnImagePicker1Clicked(sender: AnyObject)
{
let alert:UIAlertController=UIAlertController(title: "Choose Image", message: nil, preferredStyle: UIAlertControllerStyle.ActionSheet)
let cameraAction = UIAlertAction(title: "Camera", style: UIAlertActionStyle.Default)
{
UIAlertAction in
self.openCamera()
}
let gallaryAction = UIAlertAction(title: "Gallery", style: UIAlertActionStyle.Default)
{
UIAlertAction in
self.openGallary()
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel)
{
UIAlertAction in
}
// Add the actions
SKIP2.hidden = true
BUTTON2.hidden = false
picker?.delegate = self
alert.addAction(cameraAction)
alert.addAction(gallaryAction)
alert.addAction(cancelAction)
// Present the controller
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(alert, animated: true, completion: nil)
}
else
{
popover=UIPopoverController(contentViewController: alert)
popover!.presentPopoverFromRect(btnClickMe.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
func openCamera1()
{
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
picker!.sourceType = UIImagePickerControllerSourceType.Camera
self .presentViewController(picker!, animated: true, completion: nil)
}
else
{
openGallary1()
}
}
func openGallary1()
{
picker!.sourceType = UIImagePickerControllerSourceType.PhotoLibrary
if UIDevice.currentDevice().userInterfaceIdiom == .Phone
{
self.presentViewController(picker!, animated: true, completion: nil)
}
else
{
popover=UIPopoverController(contentViewController: picker!)
popover!.presentPopoverFromRect(btnClickMe.frame, inView: self.view, permittedArrowDirections: UIPopoverArrowDirection.Any, animated: true)
}
}
func imagePickerController1(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject])
{
picker .dismissViewControllerAnimated(true, completion: nil)
imageView.image=info[UIImagePickerControllerOriginalImage] as? UIImage
}
func imagePickerControllerDidCancel1(picker: UIImagePickerController)
{
print("picker cancel.")
}
}