Taking a photo and displaying it on one of many UIImageView's

Hi, I have a VC that has multiple UIImageView's with a button for each UIImageView. As I can't declare imagePickerController multiple times, I added a sentence at the end of the imagePickerController stating:         imageTaken = image, which imageTaken has been declared as a variable.

All is good till now, when I take a picture, although I'm using the DispatchQueue.main.sync, all the lines are function as executed at the same time. The reason I know this is that the first time I take a pic, nothing is stored in the UIImageView, the consecutive times, the second I click the button, the image shows in the UIImageView, then it goes to the camera, if I click cancel, then the image shows up from the previous click. As far as I understand, all the lines in the function are executed at the same time, that's why the image stays in the variable, but it doesn't show up in the UIImageView because it didn't have time to be saved in the variable before the line was executed. Can someone help please? If what I'm doing is wrong, all I want to do is to take multiple pictures to be shown on different UIImageViews on the same VC. Thanks

    @IBAction func takeClientIdCardFrontPic() {         DispatchQueue.main.async {         let picker = UIImagePickerController()             picker.sourceType = .camera             picker.delegate = self             self.present(picker, animated: true)             self.clientIdCardFrontImage.image = self.imageTaken         }     }

extension ReportViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {         picker.dismiss(animated: true, completion: nil)     }

    

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {         picker.dismiss(animated: true, completion: nil)         guard let image = info[UIImagePickerController.InfoKey.originalImage] as?                 UIImage else{                     return                 }         imageTaken = image     } }

Please format code when you post:

@IBAction func takeClientIdCardFrontPic() {
    DispatchQueue.main.async {
        let picker = UIImagePickerController()
        picker.sourceType = .camera
        picker.delegate = self
        self.present(picker, animated: true)
        self.clientIdCardFrontImage.image = self.imageTaken
    }
}

extension ReportViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
 func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
     picker.dismiss(animated: true, completion: nil)
 }
 
 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
     picker.dismiss(animated: true, completion: nil)
     guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
     imageTaken = image
 }
}

All is good till now, when I take a picture, although I'm using the DispatchQueue.main.sync, all the lines are function as executed at the same time.

Which lines do you mean ?

The reason I know this is that the first time I take a pic, nothing is stored in the UIImageView, the consecutive times, the second I click the button, the image shows in the UIImageView, then it goes to the camera, if I click cancel, then the image shows up from the previous click.

Did you try adding a completionHandler to present ? Try to replace

        self.present(picker, animated: true)
        self.clientIdCardFrontImage.image = self.imageTaken

with

        self.present(picker, animated: true, completion: { self.clientIdCardFrontImage.image = self.imageTaken })

Please, paste code in an answer, with Paste and Match Style and format it with formatter tool.

It is nearly impossible to read in a comment.

@IBAction func takeClientIdCardFrontPic() {
    DispatchQueue.main.async {
        let picker = UIImagePickerController()
        picker.sourceType = .camera
        picker.delegate = self
        print("process 1")
        self.present(picker, animated: true, completion: { self.clientIdCardFrontImage.image = self.imageTaken })
        print("process 2")
    }
}

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    picker.dismiss(animated: true, completion: nil)
    guard let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
    imageTaken = image
    print("process 3")
}

The printed results were :- process 1 process 2 process 3 while they should be :- process 1 process 3 process 2 if they were going in sequence, despite that I used the DispatchQueue.main.async. I think if we figure out why its not waiting for me to take the picture the issue will be resolved

You're wrong.

  • present is sent to another thread
  • execution of calling function is continuing immediately, with print("process 2").
  • It is not waiting because this is how multi thread works.

.

To get

process 1 process 3 process 2

You need to put print("process 2" in the completion handler:

         self.present(picker, animated: true, completion: { 
              self.clientIdCardFrontImage.image = self.imageTaken
              print("process 2")
        })

To check exactly what goes on and that self.imageTaken is updated, add more prints:

         self.present(picker, animated: true, completion: { 
              print("start of completion", self.imageTaken) 
              self.clientIdCardFrontImage.image = self.imageTaken
              print("completion ended")
        })

You should get:

process 1 process 3 start of completion completion ended

Or use async/await pattern (look at tutorial) to get it as you want. Note: you could also use semaphores

Taking a photo and displaying it on one of many UIImageView's
 
 
Q