import SwiftUI
struct CardModifier: ViewModifier {
var backgroundColor: Color = .white
var cornerRadius: CGFloat = 12
var shadowRadius: CGFloat = 5
func body(content: Content) -> some View {
content
.padding()
.background(backgroundColor)
.cornerRadius(cornerRadius)
.shadow(color: .black.opacity(0.1), radius: shadowRadius, x: 0, y: 2)
}
}
struct LoadingModifier: ViewModifier {
var isLoading: Bool
func body(content: Content) -> some View {
ZStack {
content
.disabled(isLoading)
.blur(radius: isLoading ? 3 : 0)
if isLoading {
ProgressView()
.scaleEffect(1.5)
}
}
}
}
struct PrimaryButtonStyle: ViewModifier {
var isEnabled: Bool = true
func body(content: Content) -> some View {
content
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.padding()
.background(isEnabled ? Color.blue : Color.gray)
.cornerRadius(10)
.opacity(isEnabled ? 1.0 : 0.6)
}
}
// Extension for convenience
extension View {
func cardStyle(
backgroundColor: Color = .white,
cornerRadius: CGFloat = 12,
shadowRadius: CGFloat = 5
) -> some View {
modifier(CardModifier(
backgroundColor: backgroundColor,
cornerRadius: cornerRadius,
shadowRadius: shadowRadius
))
}
func loading(_ isLoading: Bool) -> some View {
modifier(LoadingModifier(isLoading: isLoading))
}
func primaryButtonStyle(enabled: Bool = true) -> some View {
modifier(PrimaryButtonStyle(isEnabled: enabled))
}
}
struct PostCardView: View {
let post: Post
@State private var isLoading = false
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text(post.title)
.font(.headline)
Text(post.body)
.font(.body)
.lineLimit(3)
HStack {
Text(post.author.name)
.font(.caption)
Spacer()
Text(post.createdAt, style: .date)
.font(.caption)
.foregroundColor(.secondary)
}
Button("Read More") {
isLoading = true
// Navigate to detail
}
.primaryButtonStyle(enabled: !isLoading)
}
.cardStyle()
.loading(isLoading)
}
}
View modifiers encapsulate reusable styling and behavior in SwiftUI. Creating custom modifiers keeps views clean and promotes consistency. I define modifiers by conforming to the ViewModifier protocol, implementing body(content:) that transforms the content view. Common use cases include card styles, loading overlays, and form field styling. Extension methods on View provide convenient .cardStyle() syntax. Modifiers compose—apply multiple to build complex styles from simple building blocks. For conditional modifiers, I use the ternary operator or helper extensions. This pattern reduces code duplication and makes design system changes trivial—update one modifier to change all instances.