Search code examples
swiftimageswiftuiview

"Invisible" touchable area outside of real image view


So I have this issue since a long time... I even submitted an Apple TSI but did not get an answer yet. So in short I give an image in a view a certain height & width and because I want that the ratio stays similar it should scale up, which works. BUT now when the initial image, scaled up to fit with the width of the available space, the rest of the image which is invisible (which it should be) but not deleted! When i add a tap gesture recognizer it still recognizes a tap in the whole area of the scaled image even outside of the view itself.

Here an example code I wrote. Try it out and you will see, that the card is clickable above itself more that just a few pixels. Take for testing any image you would like that is much higher than the available space like a portrait screenshot.

struct ContentView: View {
    var exampleImageName: String = "testimage"
    @State var clicked: Bool = false
    @State var removeImageCode: Bool = false
    var body: some View {
        VStack (alignment: .leading){
            ZStack{
                VStack (alignment: .leading, spacing: 12){
                    if(!removeImageCode){
                        Image(exampleImageName)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(maxHeight: 125)
                            .clipped()
                    }
                    HStack (alignment: .top){
                        VStack(alignment: .leading,spacing: 4) {
                            Text("Click on card to change color")
                                .font(.headline)
                                .fontDesign(.rounded)
                            Text("With the image you can also click above the card...")
                                .font(.system(size: 12))
                                .fontWeight(.regular)
                                .fontDesign(.rounded)
                        }
                        Spacer()
                        
                    }
                    .padding(.horizontal)
                    .padding(.vertical)
                }
                .background(clicked ? .green : .red)
                .cornerRadius(10)
                .shadow(radius: 2)
            }
            .onTapGesture {
                clicked.toggle()
            }
            Toggle("Remove image code", isOn: $removeImageCode)
        }
        .padding(.horizontal)
    }
}

I tried nearly everything. Extracting, adding and removing specific styles etc. I am sure the error lies here, because when i take an image that fits in the given area from the proportions the clickable area is the area of the view as it should.

Hope I explained it understandable, else do not hesiate to ask for more information. :)


Solution

  • I'm not able to say whether it's expected behavior or not, but I found a couple of workarounds.

    1. You can add .allowsHitTesting(false) to the image. The image is still receptive to taps because it is contained inside the ZStack, which has the onTapGesture.
        Image(exampleImageName)
            .resizable()
            .aspectRatio(contentMode: .fill)
            .frame(maxHeight: 125)
            .clipped()
            .allowsHitTesting(false) // <-- HERE
    
    1. Alternatively, add .contentShape to the ZStack:
        ZStack{
            // Omitted
        }
        .contentShape(Rectangle()) // <-- HERE
        .onTapGesture {
            clicked.toggle()
        }