1

I'm practicing swiftUI by building a small camera album app that accesses the photo album to allow the user to select and load a photo in the app. Then, the uploaded photos are displayed so the user selects a photo as the primary photo. The problem is with the .onLongPressGesture() modifier, an action sheet is shown when the user long presses a photo to ask the user to update the selected or delete it. The deletion works fine. However, if the user selects "update photo" then photo album is displayed to select a new photo, and when a photo is selected it is not shown and the old photo is still displayed. When the same photo is long pressed again the new photo is finally shown and the action sheet is shown again. I need the photo to be updated once the user selects a new photo.

Please, any suggestions to solve this issue? The code is shown below, thanks:

class EnvImage: ObservableObject {
  @Published var id: UUID
  @Published var imagesArray: [UserImages]

  init() {
    id = UUID()
    imagesArray = []
    self.objectWillChange.send()
  }
}

 class UserImages: Identifiable {
  let id: UUID
  var image: Image
  var isSelected: Bool
  var isLongPressed: Bool

  init(image: Image) {
    id = UUID()
    self.image = image
    isSelected = false
    isLongPressed = false
  }
}

Usage:

struct ContentView: View {
@StateObject var envImage = EnvImage()

@State private var photo: UIImage = UIImage(systemName: "person.circle")!
@State private var isSelectingPhoto = false
@State private var isShowingActionSheet = false
@State private var isPlusPressed = false
@State private var selectedCardImage: UserImages?
var imageSize: CGFloat = 150
var body: some View {
    VStack {
        
        if let selectedCardImage {
            selectedCardImage.image
                .resizable()
                .scaledToFit()
                .frame(width: imageSize, height: imageSize)
                .cornerRadius(20)
                .padding()
        } else {
            Image(systemName: "person.circle")
                .resizable()
                .scaledToFit()
                .frame(width: imageSize, height: imageSize)
                .cornerRadius(20)
                .padding()
        }
        
        HStack {
            ScrollView(.horizontal) {
                HStack {
                    ForEach(envImage.imagesArray, id: \.id) { image in
                        image.image
                            .resizable()
                            .scaledToFit()
                            .frame(width: imageSize, height: imageSize)
                            .cornerRadius(20)
                            .onTapGesture() { // works fine
                                onTapGesture(image: image) // works fine
                            }
                            .onLongPressGesture(minimumDuration: 1) { // issue: must be longe pressed twice to see photo gets updated
                                onLongGesture(image: image)
                            }                                
                            .padding(10)
                    }
                }
                .padding()
            }
            
            Spacer()
            
            if envImage.imagesArray.count < 3 {
                Button {
                    isSelectingPhoto = true
                    isPlusPressed = true
                } label: {
                    Image(systemName: "plus.circle")
                        .font(.system(size: 40))
                }
            }
        }
    }
    .sheet(isPresented: $isSelectingPhoto) {
        PhotoPicker(vCardImage: $photo)
    }
    .actionSheet(isPresented: $isShowingActionSheet) {
        ActionSheet(title: Text("what would you like to do?"), buttons: [
                                                    .default(Text("update photo"), action: {
                                                        isSelectingPhoto = true
                                                    }),
                                                    .destructive(Text("delete photo"), action: {
                                                        for (index,image) in envImage.imagesArray.enumerated() where image.isLongPressed == true {
                                                            deleteImage(index: index, imageToDelete: image)
                                                        }
                                                    }),
                                                    .cancel()
                                                    ])
    }
    .onChange(of: photo) { _ in
        if isPlusPressed {
            envImage.imagesArray.append(UserImages(image: Image(uiImage: photo)))
            isPlusPressed = false
        } else {
            for image in envImage.imagesArray where image.isLongPressed == true {
                if let tempSelectedCard = selectedCardImage, tempSelectedCard.id == image.id {
                    image.image = Image(uiImage: photo)
                    selectedCardImage = image
                } else {
                    image.image = Image(uiImage: photo)
                }
                    image.isLongPressed = false
                    break
            }
        }
    }
    .padding()
    .preferredColorScheme(.light)
}

func onTapGesture(image: UserImages) {
    selectedCardImage = image
    image.isSelected = true
    for others in envImage.imagesArray where others.id != image.id {
        others.isSelected = false
    }
}

func onLongGesture(image: UserImages) {
    image.isLongPressed = true
    for others in envImage.imagesArray where others.id != image.id {
        others.isLongPressed = false
    }
    isShowingActionSheet = true
}
}

func deleteImage(index: Int, imageToDelete: UserImages) {
    if let tempSelectedCardImage = selectedCardImage, tempSelectedCardImage.id == imageToDelete.id {
        print("same image to delete")
        envImage.imagesArray.remove(at: index)
        selectedCardImage = nil
    } else if let selectedCardImage, selectedCardImage.id != imageToDelete.id {
        print("different image to delete, and selectedCardImage not empty")
        envImage.imagesArray.remove(at: index)
    } else if selectedCardImage == nil {
        print("selectedCardImage is empty")
        envImage.imagesArray.remove(at: index)
    }
}

Thanks!

Edit:

I'm editing the question because I found a temporary solution.

The .onLongPressGesture works as expected only if the value of the .image is assigned to an @State Image variable. As a result, I declared @State private var otherImage = Image(systemName: "person.circle"), and changed the the image using the code inside the for-loop block of the .onChange(of: photo) function as shown below:

.onChange(of: photo) { _ in
        if isPlusPressed {
            envImage.imagesArray.append(UserImages(image: Image(uiImage: photo)))
            isPlusPressed = false
        }
        else {
            for image in envImage.imagesArray where image.isLongPressed == true {
                if image.isSelected {
                    print("image is selected")
                    image.image = Image(uiImage: photo)
                    selectedCardImage = image
                    otherImage = image.image
                } else {
                    image.image = Image(uiImage: photo)
                    print("other image is selected")
                    otherImage = image.image
                }
                break
            }
        }
    }
    .padding()
    .preferredColorScheme(.light)

Then, inside the body I gave the otherImage the following modifiers:

otherImage
    .resizable()
    .scaledToFit()
    .frame(width: 1, height: 1)
    .hidden()

It is not ideal solution and not sure why using EnvImage and UserImages do not work as expected!! Any ideas please?

abs8090
  • 111
  • 2
  • 9

0 Answers0