'Perform action after picking image with async PHPicker

Problem

Want to perform operation BitwiseAND(UIImage1, UIImage2) this is function of OpenCV framework. UIImage1 is already presented in MainVC:

@IBOutlet weak var presentedImage: UIImageView!

So, UIImage2 must be picked additionally, whenever user wants it. Worth to mention :

@IBOutlet weak var presentedImage: UIImageView!

is also picked with the same PHPicker, as UIImage2

Current idea of solving problem...

Created 2 flags:

  • First one to check, if current picked image with PHPicker is for

@IBOutlet weak var presentedImage: UIImageView!

or for "Bitwise's purpose".

This is the flag:

private var isBitwisePick: Bool = false
  • Second "flag" determines type of Bitwise to perform (AND XOR NOR OR)
 private var bitwiseOperationType: OperationTypes.ControllerTypes.BitwiseTypes?
 enum ControllerTypes{
 /*...*/
    enum BitwiseTypes{ case AND,NOR,XOR,OR }
/*.../
}

Attempt of handling user's image picking

/* This is switch-case of callback from other viewcontroller.
In our case only this one matters
*/
case .BITWISE_VC(let bitwise):
               
            self.isBitwisePick = true //Pick image for "Bitwise purpose"
            self.bitwiseOperationType = bitwise // determines type of Bitwise 
 
            // config and present PHPicker
            var config = PHPickerConfiguration()
            config.filter = .images
            config.selectionLimit = 1
            let picker = PHPickerViewController(configuration: config)
            picker.delegate = self
            self.present(picker, animated: true)

PHPickerViewControllerDelegate

extension MainViewController:  PHPickerViewControllerDelegate{
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        dismiss(animated: true)
        print("test before async \(self.isBitwisePick)")
        if let itemProvider = results.first?.itemProvider, itemProvider.canLoadObject(ofClass: UIImage.self){
            let previousImage = self.presentedImage.image
            itemProvider.loadObject(ofClass: UIImage.self){ [weak self] image, error in
                DispatchQueue.main.async { [self] in
                    guard let self = self, let image = image as? UIImage, self.presentedImage.image == previousImage else {
                        return
                    }
                    print("Im in async \(self.isBitwisePick)")
                    if(self.isBitwisePick){
                        switch self.bitwiseOperationType{
                            case .AND:
                                self.presentedImage.image = OpenCVWrapper.bitwiseAND(self.presentedImage.image, with: image)
                                break
                            default:
                                print("the same for XOR OR NOR")
                            break
                        }
                    }
                    else{ self.presentedImage.image = image }
                }
            }
            self.isBitwisePick = false
        }
    }
}

With the line

print(" test before async (self.isBitwisePick)")

I can see, that flag changed and its value is true

However... The line:

print("Im in async (self.isBitwisePick)") prints value : false

I've just started with swift, read some posts and docs about solutions like DispatchQueue or DispatchGroup... But i couldn't fix it out. That's why I'm asking for your help and tips. Thank You for your time :)



Solution 1:[1]

Your data structure should be something like

private var firstImage: UIImage?
private var secondImage: UIImage?

then you can override setters or apply some other mechanism like

private var firstImage: UIImage? { didSet { reevaluateImagesSet() } }
private var secondImage: UIImage? { didSet { reevaluateImagesSet() } }

private func reevaluateImagesSet() {
    imageView1.image = firstImage
    imageView2.image = secondImage

    if let firstImage = firstImage, let secondImage = secondImage {
        applyCombinedImage(firstImage, secondImage)
    }
}

private func applyCombinedImage(_ firstImage: UIImage, _ secondImage: UIImage) {
    // do the bitwise operation of yours in here
}

Now the only problem is how to define which image you are setting. You could use a boolean flag or create a custom class which makes things like so:

   @IBAction private func selectFirstImagePressed() {
        let picker = MyPicker(controller: self)
        self.currentPicker = picker
        picker.selectImage { image in
            self.firstImage = image
        }
   }

   @IBAction private func selectSecondImagePressed() {
        let picker = MyPicker(controller: self)
        self.currentPicker = picker
        picker.selectImage { image in
            self.secondImage = image
        }
   }

This class MyPicker then corresponds to PHPickerViewControllerDelegate and implements the logic to only return an image in a provided closure. I hope you will have no problems implementing it. It is mostly extracting the code that you already have.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Matic Oblak