How to Crop an Image in SwiftUI
Sep 14, 2024Images are an integral part of any mobile app which significantly impact the user interface and user experience. In SwiftUI, the Image view, by default, renders images at their original size without automatically resizing or cropping them to fit different device screens. However, SwiftUI offers several modifiers to handle images gracefully.
Table of Contents
- How to resize an image in SwiftUI
- How to crop an image in SwiftUI
- How to crop an image in a specific shape in SwiftUI
- How to clip the click area of cropped image in SwiftUI
How to resize an image in SwiftUI
Let's first have a look at how the `Image` view renders an image in SwiftUI and for that we'll take a picture of the beautiful Taj Mahal.
struct ContentView: View {
var body: some View {
Image("taj-mahal")
}
}
Result:
Since the size of the image is greater than the device size, you are only able to see some part of it. To resize this image in SwiftUI, you'll primarily have to use two modifiers:
.resizable()
which scales the image so that it fits in the available space of the view..aspectRatio()
to maintain the aspect ratio of the image.
Only using resizable()
sometimes result in unnecessary stretching depending on the image and the corresponding device screen size, so to avoid such scenarios we also try to maintain the aspect ratio.
struct ContentView: View {
var body: some View {
Image("taj-mahal")
.resizable()
.aspectRatio(contentMode: .fit)
}
}
Result:
Note: If you also want to include the safe area as well, additionally use the .ignoresSafeArea()
modifier.
In the above code, the aspectRatio()
modifier takes a parameter contentMode
which is of type enum that have two cases, .fit
and .fill
.
.fit
scales the image to fit within the frame, preserving the image's original aspect ratio. If the aspect ratio of the frame and the image differs, you might see some empty spaces along one of the axes..fill
scales the image to fill the frame and doesn't leave any empty spaces. It still maintains the aspect ratio and that's why you would see some part of the image will be cropped.
struct ContentView: View {
var body: some View {
Image("taj-mahal")
.resizable()
.aspectRatio(contentMode: .fill)
}
}
Result:
Note: You can also use .scaledToFit()
and .scaledToFill()
modifiers instead of .aspectRatio(contentMode: .fit)
and .aspectRatio(contentMode: .fill)
respectively to achieve the same results.
How to crop an image in SwiftUI
In most of the use cases, you typically require to crop the image in SwiftUI in order to focus more tightly on specific details.
To center crop an image in SwiftUI, you will first have to specify the size of the frame and apply the .clipped()
modifier. This modifier removes the part of the image that lie outside the frame bounds.
struct ContentView: View {
var body: some View {
Image("taj-mahal")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 280)
.clipped()
}
}
Result:
If you don't want to give a fixed width and a height but rather want to crop the image dynamically based on the size of the container, you can make use of GeometryReader.
struct ContentView: View {
var body: some View {
GeometryReader { geometry in
Image("taj-mahal")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: geometry.size.width * 0.7, height: geometry.size.height * 0.4)
.clipped()
}
}
}
In the above code, the frame is defined in such a way that the image takes up 70% of the width and 40% of the height of the container it is in.
Result:
How to crop an image in a specific shape in SwiftUI
If you want to crop the image in a specific shape, you can use the .clipShape()
modifier instead of .clipped()
and pass the Shape view that you want to crop the image in.
struct ContentView: View {
var body: some View {
Image("animal")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 280)
.clipShape(Circle())
}
}
Result:
How to clip the click area of cropped image in SwiftUI
In some of the cases, you also want to perform an action when the cropped image is tapped but what you might observe is, the clipped image is also tappable outside clipped area.
struct ContentView: View {
@State private var count: Int = 0
var body: some View {
VStack {
Image("animal")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 300)
.clipped()
.onTapGesture {
count = count + 1
}
Text("Number of times tapped: \(count)")
}
}
}
Result:
This happens because SwiftUI automatically makes the tappable area a bit larger than the visible frame. This is done to improve user experience, so even if the tap is slightly outside the view, it still works. It helps users avoid missing the tap when they aim close to the edge of the view.
You can control the tappable area of a clipped image by removing the extra margins using the .contentShape()
modifier. It defines the shape of the tappable region and then you can use the inset property to shrink the size of the tappable area by certain units.
struct ContentView: View {
@State private var count: Int = 0
var body: some View {
VStack {
Image("animal")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 300)
.clipped()
.contentShape(Rectangle().inset(by: 10))
.onTapGesture {
count = count + 1
}
Text("Number of times tapped: \(count)")
}
}
}
Result:
Where to go next?
In this article you learned about how to tackle one one of the common challenges faced during development and that is cropping an image in SwiftUI. You learned about how to resize an image and the multiple ways to crop an image in SwiftUI. To further enhance your SwiftUI skills, we strongly recommend delving into core mobile engineering concepts like MVVM in SwiftUI and Dependency Injection in Swift.
To further enhance your insights into working with images in SwiftUI, we would highly recommend exploring articles such as How to Load Image from URL in SwiftUI with AsyncImage and Convert SwiftUI views to PDF using ImageRenderer in SwiftUI.