Skip to main content

View Modifiers

Definition

View Modifiers sind Methoden in SwiftUI, die eine View transformieren und eine modifizierte Version zurückgeben. Sie ermöglichen es, das Aussehen, das Verhalten und das Layout von Views zu ändern. Modifiers können aneinandergereiht werden (method chaining), um komplexe Styles zu erstellen.

Wichtig: Modifiers geben immer eine neue View zurück - sie ändern nicht die Original-View.

Basic Syntax

// Einzelner Modifier
Text("Hello")
.font(.title)

// Mehrere Modifiers (Chaining)
Text("Hello, World!")
.font(.title)
.foregroundColor(.blue)
.padding()
.background(Color.yellow)
.cornerRadius(10)

Styling Modifiers

Font und Text

Text("Hello, World!")
.font(.largeTitle) // Font-Stil
.fontWeight(.bold) // Font-Gewicht
.foregroundColor(.blue) // Text-Farbe
.italic() // Kursiv
.underline() // Unterstrichen
.strikethrough() // Durchgestrichen
.textCase(.uppercase) // UPPERCASE
.kerning(2) // Buchstaben-Abstand
.tracking(1) // Letter spacing
.baselineOffset(5) // Baseline verschieben

Colors und Backgrounds

Text("Styled Text")
.foregroundColor(.white) // Text-Farbe
.background(Color.blue) // Hintergrund-Farbe
.background(.blue.gradient) // Gradient-Hintergrund
.foregroundStyle(.linearGradient(
colors: [.red, .blue],
startPoint: .leading,
endPoint: .trailing
))

// Mit Material-Effekt (iOS 15+)
Text("Blurred Background")
.padding()
.background(.ultraThinMaterial)

Borders und Shapes

Text("Bordered")
.padding()
.border(Color.blue, width: 2) // Border

Text("Outlined")
.padding()
.overlay(
RoundedRectangle(cornerRadius: 10)
.stroke(Color.blue, lineWidth: 2)
)

Text("Background Shape")
.padding()
.background(
RoundedRectangle(cornerRadius: 10)
.fill(Color.blue)
)

Layout Modifiers

Padding

Text("Padded")
.padding() // Alle Seiten (Standard: 16)
.padding(.horizontal, 20) // Links und rechts
.padding(.vertical, 10) // Oben und unten
.padding(.leading, 30) // Nur links
.padding(.top, 20) // Nur oben

// Kombiniert
Text("Custom Padding")
.padding(.horizontal, 20)
.padding(.vertical, 10)

Frame

// Feste Größe
Text("Fixed Size")
.frame(width: 200, height: 100)

// Minimale und maximale Größe
Text("Min-Max Frame")
.frame(minWidth: 100, maxWidth: 300)
.frame(minHeight: 50, maxHeight: 200)

// Volle Breite
Text("Full Width")
.frame(maxWidth: .infinity)

// Zentriert in vollem Bereich
Text("Centered")
.frame(maxWidth: .infinity, maxHeight: .infinity)

// Mit Alignment
Text("Leading")
.frame(width: 200, alignment: .leading)

Text("Trailing")
.frame(width: 200, alignment: .trailing)

Positioning

Text("Positioned")
.position(x: 100, y: 100) // Absolute Position

Text("Offset")
.offset(x: 20, y: 30) // Relative Verschiebung

Text("Aligned")
.alignmentGuide(.leading) { d in
d[.leading]
}

Visual Effects

Opacity und Blur

Text("Semi-transparent")
.opacity(0.5) // 0.0 bis 1.0

Text("Blurred")
.blur(radius: 3) // Unschärfe-Effekt

Shadow

Text("With Shadow")
.shadow(radius: 5) // Einfacher Schatten

Text("Custom Shadow")
.shadow(
color: .black.opacity(0.3),
radius: 5,
x: 2,
y: 2
)

// Mehrere Schatten
Text("Multiple Shadows")
.shadow(color: .red, radius: 2, x: 1, y: 1)
.shadow(color: .blue, radius: 5, x: -1, y: -1)

Rotation und Scale

Text("Rotated")
.rotationEffect(.degrees(45)) // Rotation in Grad

Text("Scaled")
.scaleEffect(1.5) // Gleichmäßig skaliert

Text("Scaled Separately")
.scaleEffect(x: 2, y: 1) // Unterschiedliche Skalierung

// Mit Anchor Point
Text("Rotated from Corner")
.rotationEffect(.degrees(45), anchor: .topLeading)

Clip und Mask

// Clip Shape
Image("photo")
.resizable()
.frame(width: 200, height: 200)
.clipShape(Circle()) // Runde Form
.clipShape(RoundedRectangle(cornerRadius: 20))

// Corner Radius
Text("Rounded Corners")
.padding()
.background(Color.blue)
.cornerRadius(10)

// Clip mit Maske
Text("Masked")
.mask(
LinearGradient(
gradient: Gradient(colors: [.clear, .black]),
startPoint: .top,
endPoint: .bottom
)
)

Interactive Modifiers

Buttons und Taps

Text("Tappable")
.onTapGesture {
print("Tapped")
}

// Mit Count
Text("Double Tap")
.onTapGesture(count: 2) {
print("Double tapped")
}

// Long Press
Text("Long Press")
.onLongPressGesture {
print("Long pressed")
}

Gestures

Text("Draggable")
.gesture(
DragGesture()
.onChanged { value in
print("Dragging: \(value.translation)")
}
.onEnded { value in
print("Drag ended")
}
)

// Magnification (Pinch)
Text("Pinchable")
.gesture(
MagnificationGesture()
.onChanged { scale in
print("Scale: \(scale)")
}
)

Disabled und Hidden

Button("Action") {
performAction()
}
.disabled(isProcessing) // Button deaktivieren

Text("Conditional")
.opacity(isVisible ? 1 : 0) // Sichtbarkeit

// Oder direkt mit if
if isVisible {
Text("Visible")
}

Animation Modifiers

Basic Animation

Text("Animated")
.scaleEffect(isLarge ? 1.5 : 1.0)
.animation(.default, value: isLarge)

// Custom Animation
Text("Custom Animation")
.offset(y: isUp ? -100 : 0)
.animation(.spring(response: 0.5, dampingFraction: 0.6), value: isUp)

// Different Animation Types
Text("Easing")
.offset(x: offset)
.animation(.easeInOut(duration: 1.0), value: offset)

Text("Spring")
.scaleEffect(scale)
.animation(.spring(), value: scale)

Transition

// Fade Transition
if isVisible {
Text("Fading")
.transition(.opacity)
}

// Slide Transition
if isVisible {
Text("Sliding")
.transition(.slide)
}

// Scale Transition
if isVisible {
Text("Scaling")
.transition(.scale)
}

// Custom Combined Transition
if isVisible {
Text("Combined")
.transition(
.asymmetric(
insertion: .scale.combined(with: .opacity),
removal: .slide
)
)
}

Conditional Modifiers

// Mit Ternary Operator
Text("Hello")
.foregroundColor(isActive ? .blue : .gray)
.font(isLarge ? .title : .body)

// Mit Custom Modifier Extension
extension View {
@ViewBuilder
func `if`<Transform: View>(
_ condition: Bool,
transform: (Self) -> Transform
) -> some View {
if condition {
transform(self)
} else {
self
}
}
}

// Verwendung
Text("Conditional Style")
.if(isHighlighted) { view in
view.background(Color.yellow)
}

Environment Modifiers

// Environment Values
Text("Sized Text")
.environment(\.sizeCategory, .extraLarge)

// Color Scheme
Text("Dark Mode")
.environment(\.colorScheme, .dark)

// Layout Direction
Text("RTL")
.environment(\.layoutDirection, .rightToLeft)

// In Container
VStack {
Text("Child 1")
Text("Child 2")
Text("Child 3")
}
.font(.headline) // Gilt für alle Kinder
.foregroundColor(.blue) // Environment Modifier

Custom View Modifiers

// Custom Modifier
struct CardModifier: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color.white)
.cornerRadius(10)
.shadow(radius: 5)
}
}

// Extension für einfachere Verwendung
extension View {
func cardStyle() -> some View {
modifier(CardModifier())
}
}

// Verwendung
Text("Card Style")
.cardStyle()

// Parametrisierter Custom Modifier
struct BorderedCaption: ViewModifier {
let color: Color
let width: CGFloat

func body(content: Content) -> some View {
content
.font(.caption)
.padding(10)
.overlay(
RoundedRectangle(cornerRadius: 5)
.stroke(color, lineWidth: width)
)
}
}

extension View {
func borderedCaption(color: Color = .blue, width: CGFloat = 2) -> some View {
modifier(BorderedCaption(color: color, width: width))
}
}

// Verwendung
Text("Bordered")
.borderedCaption(color: .red, width: 3)

Practical Examples

Button Style

Button("Action") {
performAction()
}
.font(.headline)
.foregroundColor(.white)
.padding(.horizontal, 30)
.padding(.vertical, 15)
.background(Color.blue)
.cornerRadius(10)
.shadow(radius: 5)

Card View

VStack(alignment: .leading, spacing: 10) {
Text("Title")
.font(.headline)
Text("Description text")
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color.white)
.cornerRadius(15)
.shadow(color: .black.opacity(0.1), radius: 10)
.padding()

Profile Image

Image("profile")
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 100, height: 100)
.clipShape(Circle())
.overlay(Circle().stroke(Color.white, lineWidth: 4))
.shadow(radius: 10)

Badge

Text("5")
.font(.caption)
.foregroundColor(.white)
.padding(6)
.background(Color.red)
.clipShape(Circle())
.offset(x: 20, y: -20)

Best Practices

  1. Reihenfolge ist wichtig: Modifiers werden von oben nach unten angewendet
// Unterschiedliche Ergebnisse
Text("Hello")
.padding()
.background(Color.blue) // Hintergrund um Padding

Text("Hello")
.background(Color.blue)
.padding() // Padding außerhalb des Hintergrunds
  1. Wiederverwendbare Modifier: Erstelle Custom Modifiers für häufige Styles
  2. Performance: Zu viele Modifier können Performance beeinträchtigen
  3. Environment Modifier: Nutze sie für Container statt jeden Child einzeln zu stylen
  4. Animation Value: Gib immer einen konkreten Wert an bei .animation(_:value:)

Common Modifier Chains

Styled Text Block

Text("Lorem ipsum dolor sit amet")
.font(.body)
.foregroundColor(.primary)
.lineLimit(3)
.multilineTextAlignment(.leading)
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)

Interactive Card

VStack {
// Content
}
.padding()
.background(Color.white)
.cornerRadius(12)
.shadow(radius: isPressed ? 2 : 5)
.scaleEffect(isPressed ? 0.95 : 1.0)
.animation(.spring(), value: isPressed)
.onTapGesture {
isPressed.toggle()
}
  • Custom View Modifiers: Eigene wiederverwendbare Modifiers
  • Environment Values: System-weite Werte
  • Animations: Modifier für Animationen
  • Gestures: Interaktive Modifiers