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
- 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
- Wiederverwendbare Modifier: Erstelle Custom Modifiers für häufige Styles
- Performance: Zu viele Modifier können Performance beeinträchtigen
- Environment Modifier: Nutze sie für Container statt jeden Child einzeln zu stylen
- 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()
}
Related Topics
- Custom View Modifiers: Eigene wiederverwendbare Modifiers
- Environment Values: System-weite Werte
- Animations: Modifier für Animationen
- Gestures: Interaktive Modifiers