SwiftUI Modifiers Guide
Basic Modifiers
Layout Modifiers
// Frame and sizing
Text("Hello")
.frame(width: 200, height: 100)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.frame(minWidth: 100, idealWidth: 200, maxWidth: 300)
// Padding and spacing
VStack {
Text("Content")
}
.padding() // All sides
.padding(.horizontal, 20) // Horizontal only
.padding(.top, 10) // Specific side
.padding(EdgeInsets(top: 10, leading: 20, bottom: 10, trailing: 20))
// Alignment and positioning
Text("Aligned Text")
.frame(maxWidth: .infinity, alignment: .leading)
.multilineTextAlignment(.center)
.position(x: 100, y: 100) // Absolute positioning
.offset(x: 10, y: -5) // Relative positioning
Visual Modifiers
// Colors and backgrounds
Text("Colored Text")
.foregroundColor(.blue)
.foregroundStyle(.linearGradient(colors: [.blue, .purple], startPoint: .leading, endPoint: .trailing))
.background(.yellow)
.background(
RoundedRectangle(cornerRadius: 10)
.fill(.blue.opacity(0.3))
)
// Borders and shapes
Text("Bordered Text")
.border(.red, width: 2)
.overlay(
RoundedRectangle(cornerRadius: 8)
.stroke(.blue, lineWidth: 1)
)
.clipShape(RoundedRectangle(cornerRadius: 10))
.mask(RoundedRectangle(cornerRadius: 10))
// Shadows and effects
Text("Shadowed Text")
.shadow(color: .black.opacity(0.3), radius: 5, x: 2, y: 2)
.blur(radius: 2)
.brightness(0.1)
.contrast(1.2)
.saturation(1.5)
Advanced Modifiers
Conditional Modifiers
// Conditional application
struct ConditionalModifierView: View {
@State private var isHighlighted = false
var body: some View {
Text("Conditional Text")
.foregroundColor(isHighlighted ? .red : .blue)
.scaleEffect(isHighlighted ? 1.2 : 1.0)
.animation(.easeInOut, value: isHighlighted)
.onTapGesture {
isHighlighted.toggle()
}
}
}
// Using ViewModifier for complex conditions
struct HighlightModifier: ViewModifier {
let isHighlighted: Bool
func body(content: Content) -> some View {
content
.foregroundColor(isHighlighted ? .red : .blue)
.scaleEffect(isHighlighted ? 1.1 : 1.0)
.animation(.easeInOut(duration: 0.3), value: isHighlighted)
}
}
extension View {
func highlight(_ isHighlighted: Bool) -> some View {
modifier(HighlightModifier(isHighlighted: isHighlighted))
}
}
// Usage
Text("Custom Modifier")
.highlight(true)
Custom ViewModifiers
// Creating reusable modifiers
struct CardModifier: ViewModifier {
let cornerRadius: CGFloat
let shadowRadius: CGFloat
func body(content: Content) -> some View {
content
.background(.white)
.cornerRadius(cornerRadius)
.shadow(radius: shadowRadius)
}
}
struct GradientBackgroundModifier: ViewModifier {
let colors: [Color]
func body(content: Content) -> some View {
content
.background(
LinearGradient(
colors: colors,
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
}
}
// Extension for easy usage
extension View {
func cardStyle(cornerRadius: CGFloat = 10, shadowRadius: CGFloat = 5) -> some View {
modifier(CardModifier(cornerRadius: cornerRadius, shadowRadius: shadowRadius))
}
func gradientBackground(_ colors: [Color] = [.blue, .purple]) -> some View {
modifier(GradientBackgroundModifier(colors: colors))
}
}
// Usage
VStack {
Text("Card Content")
}
.cardStyle()
.gradientBackground([.orange, .red])
Animation Modifiers
// Basic animations
Text("Animated Text")
.scaleEffect(isExpanded ? 1.5 : 1.0)
.animation(.easeInOut(duration: 0.5), value: isExpanded)
.animation(.spring(response: 0.5, dampingFraction: 0.6), value: isExpanded)
// Staggered animations
struct StaggeredAnimationView: View {
@State private var animate = false
var body: some View {
VStack(spacing: 10) {
ForEach(0..<5, id: \.self) { index in
Text("Item \(index)")
.opacity(animate ? 1 : 0)
.offset(x: animate ? 0 : -50)
.animation(
.easeInOut(duration: 0.5)
.delay(Double(index) * 0.1),
value: animate
)
}
}
.onAppear {
animate = true
}
}
}
// Custom animation curves
Text("Custom Animation")
.scaleEffect(isScaled ? 2.0 : 1.0)
.animation(
.interpolatingSpring(
mass: 1,
stiffness: 100,
damping: 10,
initialVelocity: 0
),
value: isScaled
)
Modifier Tricks and Tips
Chaining Modifiers Efficiently
// Good: Logical grouping
Text("Well Organized")
.font(.title)
.foregroundColor(.blue)
.padding()
.background(.yellow)
.cornerRadius(10)
// Better: Using custom modifiers
Text("Better Organized")
.titleStyle()
.cardStyle()
// Extension for common text styles
extension View {
func titleStyle() -> some View {
self
.font(.title)
.foregroundColor(.blue)
.padding()
}
}
Performance Optimization
// Avoid expensive modifiers in loops
struct OptimizedListView: View {
let items: [String]
var body: some View {
LazyVStack {
ForEach(items, id: \.self) { item in
Text(item)
.modifier(ExpensiveModifier()) // Apply once per item
}
}
}
}
// Use conditional modifiers sparingly
struct ConditionalOptimization: View {
@State private var condition = false
var body: some View {
Text("Optimized")
.modifier(OptimizedModifier(condition: condition))
}
}
struct OptimizedModifier: ViewModifier {
let condition: Bool
func body(content: Content) -> some View {
if condition {
content
.foregroundColor(.red)
.scaleEffect(1.2)
} else {
content
.foregroundColor(.blue)
.scaleEffect(1.0)
}
}
}
Advanced Techniques
// Modifier composition
struct ComposedModifier: ViewModifier {
func body(content: Content) -> some View {
content
.modifier(FirstModifier())
.modifier(SecondModifier())
.modifier(ThirdModifier())
}
}
// Environment-aware modifiers
struct AdaptiveModifier: ViewModifier {
@Environment(\.colorScheme) var colorScheme
func body(content: Content) -> some View {
content
.foregroundColor(colorScheme == .dark ? .white : .black)
.background(colorScheme == .dark ? .black : .white)
}
}
// Size-aware modifiers
struct ResponsiveModifier: ViewModifier {
@Environment(\.horizontalSizeClass) var horizontalSizeClass
func body(content: Content) -> some View {
content
.font(horizontalSizeClass == .compact ? .body : .title2)
.padding(horizontalSizeClass == .compact ? 10 : 20)
}
}
Debugging Modifiers
// Debug modifier to visualize layout
struct DebugModifier: ViewModifier {
let color: Color
func body(content: Content) -> some View {
content
.border(color, width: 2)
.background(color.opacity(0.1))
}
}
extension View {
func debug(_ color: Color = .red) -> some View {
modifier(DebugModifier(color: color))
}
}
// Usage for debugging
VStack {
Text("Debug this")
}
.debug(.blue)
.padding()
.debug(.green)
Accessibility Modifiers
// Comprehensive accessibility
Button("Delete") {
// Action
}
.accessibilityLabel("Delete selected item")
.accessibilityHint("Double tap to confirm")
.accessibilityAddTraits(.isDestructive)
.accessibilityRemoveTraits(.isButton)
// Custom accessibility actions
Text("Important Info")
.accessibilityAction(named: "Read Aloud") {
// Text-to-speech
}
.accessibilityAction(named: "Share") {
// Share action
}
.accessibilityElement(children: .combine)