Skip to main content

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​

  1. Use .resizable() for scaling: Always add when you need to control image size
  2. Consider aspect ratios: Use .aspectRatio() to maintain proportions
  3. Optimize for different screen sizes: Use appropriate sizing strategies
  4. Clip overflow content: Use .clipped() when using .fill content mode
  5. Provide accessibility: Add accessibility labels for screen readers
  6. Use SF Symbols when possible: They automatically adapt to user settings
  7. 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