3

I am adapting a Swift Project to SwiftUI. The original project has Drag and Drop on UIImages, drawing and some other manipulations which aren't possible in SwiftUI.

The code below is for explaining the problem only. Working code on: https://github.com/rolisanchez/TestableDragAndDrop

On the project, when a user clicks on a button, a @State setChooseImage is changed to true, opening a sheet in which he is presented with a series of images:

.sheet(isPresented: self.$setChooseImage, onDismiss: nil) {
                        VStack {
                            Button(action: {
                                self.setChooseImage = false
                                self.chosenAssetImage = UIImage(named: "testImage")
                                self.shouldAddImage = true
                            }) {
                                Image("testImage")
                                    .renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.original))

                                Text("Image 1")
                            }
                            Button(action: {
                                self.setChooseImage = false
                                self.chosenAssetImage = UIImage(named: "testImage2")
                                self.shouldAddImage = true
                            }) {
                                Image("testImage2")
                                    .renderingMode(Image.TemplateRenderingMode?.init(Image.TemplateRenderingMode.original))
                                Text("Image 2")
                            }
                        }
                }

After selecting the image, setChooseImage is set back to false, closing the sheet. The Image self.chosenAssetImage is also a @State and is set to the chosen Image. The @State shouldAddImage is also set to true. This chosen UIImage and Bool are used inside a UIView I created to add the UIImages. It is called inside SwiftUI like this:

 DragAndDropRepresentable(shouldAddImage: $shouldAddImage, chosenAssetImage: $chosenAssetImage)

The UIViewRepresentable to add the UIView is the following:

struct DragAndDropRepresentable: UIViewRepresentable {
    @Binding var shouldAddImage: Bool
    @Binding var chosenAssetImage: UIImage?

    func makeUIView(context: Context) -> DragAndDropUIView {
        let view = DragAndDropUIView(frame: CGRect.zero)
        return view
    }

    func updateUIView(_ uiView: DragAndDropUIView, context: UIViewRepresentableContext< DragAndDropRepresentable >) {
        if shouldAddImage  {
            shouldAddImage = false
            guard let image = chosenAssetImage else { return }
            uiView.addNewAssetImage(image: image)
        }
    }
}

I understand updateUIView is called whenever there are some changes that could affect the views. In this case, shouldAddImage became true, thus entering the if loop correctly and calling addNewAssetImage, which inserts the image into the UIView.

The problem here is that updateUIView is called multiple times, and enters the if shouldAddImage loop all those times, before updating the shouldAddImage Binding. This means that it will add the image multiple times to the UIView.

I put a breakpoint before and after the assignment of shouldAddImage = false, and even after passing that line, the value continues to be true.

The behavior I wanted is to change the shouldAddImage immediately to false, so that even if updateUIView is called multiple times, it would only enter the loop once, adding the image only once.

Victor Sanchez
  • 960
  • 2
  • 12
  • 26

0 Answers0