Luis Ramirez
Luis Ramirez

Reputation: 1634

SwiftUI tap gesture selecting wrong item

So I'm trying to create a custom image picker something like instagram but way more basic. This is how I created the screen using this.

struct NewPostScreen: View {
@StateObject var manager = SelectNewPostScreenManager()

let columns = [GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1), GridItem(.flexible(), spacing: 1)]

var body: some View {

    ScrollView {
        VStack(spacing: 1) {
            Image(uiImage: manager.selectedPhoto?.uiImage ?? UIImage(named: "placeholder-image")!)
                .resizable()
                .scaledToFit()
                .frame(width: 350, height: 350)
                .id(1)
            LazyVGrid(columns: columns, spacing: 1) {
                ForEach(manager.allPhotos) { photo in
                    Image(uiImage: photo.uiImage)
                        .resizable()
                        .scaledToFill()
                        .frame(maxWidth: UIScreen.main.bounds.width/3, minHeight: UIScreen.main.bounds.width/3, maxHeight: UIScreen.main.bounds.width/3)
                        .clipped()
                        .onTapGesture {
                            manager.selectedPhoto = photo
                        }
                }
            }
        }
    }
}
}

The UI looks good and everything but sometimes when I click an image using the tapGesture it gives me an incorrect selectedPhoto for my manager. Here is how my manager looks and how I fetch the photos from the library.

 class SelectNewPostScreenManager: ObservableObject {
@Environment(\.dismiss) var dismiss
@Published var selectedPhoto: Photo?
@Published var allPhotos: [Photo] = []

init() {
    fetchPhotos()
}

private func assetsFetchOptions() -> PHFetchOptions {
    let fetchOptions = PHFetchOptions()
    let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: false)
    fetchOptions.sortDescriptors = [sortDescriptor]
    return fetchOptions
}

func fetchPhotos() {
    print("Fetching Photos")
    let options = assetsFetchOptions()
    let allAssets = PHAsset.fetchAssets(with: .image, options: options)
    
    DispatchQueue.global(qos: . background).async {
        allAssets.enumerateObjects { asset, count, _ in
            let imageManager = PHImageManager.default()
            let targetSize = CGSize(width: 250, height: 250)
            let options = PHImageRequestOptions()
            options.isSynchronous = true
            
            imageManager.requestImage(for: asset, targetSize: targetSize, contentMode: .aspectFill, options: options) { image, info in
                guard let image = image else { return }
                let photo = Photo(uiImage: image)
                DispatchQueue.main.async {
                    self.allPhotos.append(photo)
                }
            }
        }
    }
}
}

This is how my photo object looks like as well.

struct Photo: Identifiable {
let id = UUID()
let uiImage: UIImage
}

I have no clue to why the tap gesture is not selecting the right item. Ive spent a couple of hours trying to figure out to why this is happening. I might just end up using the UIImagePickerController instead lol.

Anyways if someone can copy and paste this code into a new project of Xcode and run it on your actual device instead of the simulator. Let me know if its happening to you as well.

I was running it on an iPhone X.

Upvotes: 10

Views: 1525

Answers (1)

yawnobleix
yawnobleix

Reputation: 1342

The problem is that the image gesture are extending beyond your defined frame, I am sure there are many ways to fix this, but I solved it by adding the contentShape modifier

Please replace your image code with the following

Image(uiImage: photo.uiImage)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width/3, height: UIScreen.main.bounds.width/3)
.clipped()
.contentShape(Path(CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width/3, height: UIScreen.main.bounds.width/3)))
.onTapGesture {
    manager.selectedPhoto = photo
}

contentShape define the hit area for the gesture

Upvotes: 22

Related Questions