Image
Definition​
Image
is a view in SwiftUI that displays an image. Images can come from the asset catalog, system symbols (SF Symbols), or external sources.
Basic Syntax​
Image("imageName") // Image from Assets
Image(systemName: "star") // SF Symbol
Common Modifiers​
Image("imageName")
.resizable() // Enables resizing
.aspectRatio(contentMode: .fit) // Aspect ratio: .fit or .fill
.frame(width: 100, height: 100) // Sets fixed width and height
.clipShape(Circle()) // Clips image to shape
.cornerRadius(10) // Rounded corners
.shadow(color: .black, radius: 5, x: 0, y: 2) // Adds shadow
.opacity(0.8) // Transparency
.brightness(0.1) // Adjusts brightness
.contrast(1.2) // Adjusts contrast
.saturation(0.5) // Adjusts saturation
.rotationEffect(.degrees(45)) // Rotates the image
.scaleEffect(1.5) // Scales the image
.blur(radius: 2) // Applies blur effect
.overlay(
Text("Overlay")
.foregroundColor(.white)
) // Adds overlay
.border(Color.gray, width: 2) // Adds border
Aspect Ratio and Resizing​
// Fit - scales to fit within bounds while maintaining aspect ratio
Image("landscape")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 100)
// Fill - scales to fill bounds, may crop image
Image("landscape")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 100)
.clipped() // Clips overflow
// Fixed aspect ratio
Image("portrait")
.resizable()
.aspectRatio(3/4, contentMode: .fit)
SF Symbols​
// Basic SF Symbol
Image(systemName: "heart")
.font(.title)
.foregroundColor(.red)
// SF Symbol with custom size
Image(systemName: "star.fill")
.font(.system(size: 24, weight: .bold))
.foregroundColor(.yellow)
// SF Symbol with hierarchical rendering
Image(systemName: "battery.75")
.symbolRenderingMode(.hierarchical)
.foregroundColor(.green)
.font(.largeTitle)
// Multicolor SF Symbol
Image(systemName: "person.crop.circle.fill")
.symbolRenderingMode(.multicolor)
.font(.system(size: 50))
Image Shapes and Clipping​
// Circle clip
Image("profile")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
// Rounded rectangle
Image("photo")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 150, height: 100)
.clipShape(RoundedRectangle(cornerRadius: 15))
// Custom shape
Image("background")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 200, height: 200)
.clipShape(Ellipse())
Image Effects​
// Filters and adjustments
Image("nature")
.resizable()
.aspectRatio(contentMode: .fit)
.brightness(0.2)
.contrast(1.1)
.saturation(1.3)
.hueRotation(.degrees(15))
// Shadow effects
Image("icon")
.resizable()
.frame(width: 60, height: 60)
.shadow(color: .black.opacity(0.3), radius: 5, x: 2, y: 2)
// Blur effect
Image("background")
.resizable()
.aspectRatio(contentMode: .fill)
.blur(radius: 3)
.overlay(
Rectangle()
.fill(Color.black.opacity(0.3))
)
Async Image Loading​
// For iOS 15+
AsyncImage(url: URL(string: "https://example.com/image.jpg")) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
} placeholder: {
ProgressView()
.frame(width: 200, height: 200)
}
// With error handling
AsyncImage(url: URL(string: "https://example.com/image.jpg")) { phase in
switch phase {
case .empty:
ProgressView()
case .success(let image):
image
.resizable()
.aspectRatio(contentMode: .fit)
case .failure(_):
Image(systemName: "photo")
.foregroundColor(.gray)
@unknown default:
EmptyView()
}
}
.frame(width: 200, height: 200)
Interactive Images​
// Tappable image
Image("button")
.resizable()
.frame(width: 80, height: 80)
.onTapGesture {
print("Image tapped!")
}
// Draggable image
@State private var dragOffset = CGSize.zero
Image("draggable")
.resizable()
.frame(width: 100, height: 100)
.offset(dragOffset)
.gesture(
DragGesture()
.onChanged { value in
dragOffset = value.translation
}
.onEnded { _ in
dragOffset = .zero
}
)
Image Overlays and Backgrounds​
// Text overlay
Image("hero")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 200)
.overlay(
VStack {
Spacer()
Text("Hero Title")
.font(.title)
.fontWeight(.bold)
.foregroundColor(.white)
.padding()
}
)
.clipped()
// Gradient overlay
Image("sunset")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(height: 300)
.overlay(
LinearGradient(
gradient: Gradient(colors: [.clear, .black.opacity(0.8)]),
startPoint: .center,
endPoint: .bottom
)
)
.clipped()
Image Loading States​
struct ImageLoader: View {
let imageName: String
@State private var isLoaded = false
var body: some View {
ZStack {
if isLoaded {
Image(imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.transition(.opacity)
} else {
RoundedRectangle(cornerRadius: 8)
.fill(Color.gray.opacity(0.3))
.overlay(
ProgressView()
)
}
}
.onAppear {
// Simulate loading delay
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
withAnimation {
isLoaded = true
}
}
}
}
}
Best Practices​
- Use .resizable() for scaling: Always add when you need to control image size
- Consider aspect ratios: Use .aspectRatio() to maintain proportions
- Optimize for different screen sizes: Use appropriate sizing strategies
- Clip overflow content: Use .clipped() when using .fill content mode
- Provide accessibility: Add accessibility labels for screen readers
- Use SF Symbols when possible: They automatically adapt to user settings
- Handle loading states: Show placeholders for async images
Common Use Cases​
- Profile pictures and avatars
- Product images in e-commerce
- Background images and hero sections
- Icon and button graphics
- Gallery and photo viewers
- App logos and branding