13

In my SwiftUI app, I have an image in my asset catalog with an aspect ratio of 1:1. In my code, I have an Image view with a different aspect ratio that clips the image to the new size:

Image("My Image")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300, height: 250)
    .clipped()

image clipped to aspect ratio

But when I attach a context menu to this image (with the contextMenu modifier), the original aspect ratio is still there, but with transparent padding:

image clipped to aspect ratio with context menu

How do I keep the image clipped to the new frame inside the context menu, so there's no padding?

Wilson Gramer
  • 702
  • 8
  • 23

2 Answers2

14

On iOS 15, please see the accepted post. This solution works on iOS 14.


I was able to solve this by adding a .contentShape(Rectangle()) modifier to the image:

Image("My Image")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300, height: 250)
    .clipped()
    .contentShape(Rectangle())
    .contextMenu {
        Text("Menu Item")
    }

image clipped to aspect ratio with context menu - correct behavior

Wilson Gramer
  • 702
  • 8
  • 23
  • 2
    This was such a beautiful solution. I was able to use content shape to change the context menu preview to Circle() as well. Thanks! – Adit Gupta Oct 09 '20 at 12:50
  • yip, this question and answer is still useful. helped me, thanks – Muhammad Ahmod Feb 17 '21 at 10:12
  • I don't believe this solution is working in iOS 15. Haven't tried it before that. If you use a Circle shape you will see it doesn't work. And in your code if you remove contentShape on iOS 15 it doesn't make any difference. I wonder if anybody can make this work with a shape that is not rectangular. – alionthego Nov 14 '21 at 23:21
  • @alionthego You're right, they updated the `contentShape` API on iOS 15. I just accepted your answer, thanks for sharing! – Wilson Gramer Nov 15 '21 at 03:10
10

I believe the correct solution that will work in all scenarios is to set the first argument (kind) in the contentShape:

func contentShape<S>(_ kind: ContentShapeKinds, _ shape: S)

Set that to .contextMenuPreview and this will work for all shapes:

Image("leaf")
    .resizable()
    .aspectRatio(contentMode: .fill)
    .frame(width: 300, height: 300)
    .clipShape(Circle())
    .contentShape(ContentShapeKinds.contextMenuPreview, Circle())
    .contextMenu {
        Text("Menu Item")
    }

enter image description here

alionthego
  • 8,508
  • 9
  • 52
  • 125