Search code examples
iosimageswiftui

SwiftUI clipped image still tappable outside clipped area


I am showing an image from Assets and this is the code:

struct ImageTest: View {
    
    var body: some View {
        
        ZStack {
            
            Image("testImage")
                .resizable()
                .scaledToFill()
                .frame(width:400)
                .frame(height:400)
                .clipped()
                .border(.red)
                .onTapGesture {
                    print("TAPPED \(Date())")
                }   
        }   
    }
}

Problem: Image is landscape. I am able to make it scale to fill, but, after clipping it the clipped portion of the image still triggers the on tap action. So, my expectation was to NOT be able to trigger the on tap gesture while outside the square red border (the area I clip the image in...). When I tap to the left or right of the red square, the on tap still triggers.

How can I make it so the clipped area is respected even in the tap action? In other works, I want the image to truly be clipped and affect nothing else once clipped.

enter image description here

I was then told that this is a duplicate question. Referred to: SwiftUI clipped image still accepts tap gesture

So then I changed my code to this:

struct ImageTest: View {
    
    var body: some View {
        ZStack {
            Image("testImage")
                .resizable()
                .scaledToFill()
                .frame(width:400)
                .frame(height:400)
                .clipped()
                .contentShape(Rectangle())
                .border(.red)
                .onTapGesture {
                    print("TAPPED \(Date())")
                }
        }
    }
    
}

Problem still exists. What it did, it eliminated most of the bleeding tappable area, but I am still able to trigger the on tap action when clicking on left or right of red border. What did change is I cannot click as far as I could before to the left or right of the red border, but I still trigger the on tap, just not as far from the border as I used to.


Solution

  • You said that adding .contentShape nearly fixes it, but there is still a small margin around the image which is sensitive to tapping.

    I think this is normal behavior for an iOS device. If you search for "button tappable outside frame" then you will find reports of similar issues, for example Why tap Gesture of Button get triggered even outside of its frame?. It seems there is a margin of tolerance that allows taps to be triggered if they are close to but not inside a frame, probably so that tapping with a finger does not need to be super-precise.

    If you really want to eliminate this margin, you can reduce the size of the rectangle being set as the content shape by applying an .inset to it. The larger the inset, the smaller the margin.

    Image(.image2)
        .resizable()
        .scaledToFill()
        .frame(width:400)
        .frame(height:400)
        .clipped()
        .contentShape(Rectangle().inset(by: 10)) // 👈 HERE
        .border(.red)
        .onTapGesture {
            print("TAPPED \(Date())")
        }
    

    Of course, if you make the inset too big then taps will not even be triggered when made just inside the frame either. But maybe you would prefer it this way, than to allow taps to be triggered when just outside the frame.