Bedrock/Sources/Bedrock/Views/Effects/PulsingModifier.swift
Matt Bruce cc650c68d4 Add settings theming guide and fix Color typealias conflicts
- Add SETTINGS_GUIDE.md with comprehensive documentation for creating branded settings screens
- Rename Color.Text to Color.TextColors to avoid conflict with SwiftUI Text view
- Rename Color.Button to Color.ButtonColors to avoid conflict with SwiftUI Button view
- Add accentColor parameter to SettingsSectionHeader for customizable section icons
- Update README and PulsingModifier to use new TextColors typealias
2026-01-04 16:54:04 -06:00

82 lines
2.5 KiB
Swift

//
// PulsingModifier.swift
// Bedrock
//
// Visual hint modifier that pulses to draw attention.
//
import SwiftUI
/// Modifier that adds a pulsing animation to draw attention to interactive elements.
///
/// Use this to highlight buttons, actions, or any UI element that needs user attention.
public struct PulsingModifier: ViewModifier {
let isActive: Bool
let color: Color
let scale: CGFloat
let cornerRadius: CGFloat
@State private var animationAmount: CGFloat = 1.0
public func body(content: Content) -> some View {
content
.overlay {
if isActive {
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(color, lineWidth: Design.LineWidth.medium)
.scaleEffect(animationAmount)
.opacity(2 - animationAmount)
.animation(
.easeInOut(duration: 1.5)
.repeatForever(autoreverses: false),
value: animationAmount
)
.onAppear {
animationAmount = scale
}
}
}
}
}
public extension View {
/// Adds a pulsing animation to draw attention to the view.
/// - Parameters:
/// - isActive: Whether the pulsing animation is active.
/// - color: The color of the pulse (default: white).
/// - scale: How much the pulse expands (default: 1.3).
/// - cornerRadius: Corner radius of the pulse shape (default: large).
func pulsing(
isActive: Bool,
color: Color = .white,
scale: CGFloat = 1.3,
cornerRadius: CGFloat = Design.CornerRadius.large
) -> some View {
modifier(PulsingModifier(
isActive: isActive,
color: color,
scale: scale,
cornerRadius: cornerRadius
))
}
}
#Preview {
ZStack {
Color.Surface.primary.ignoresSafeArea()
VStack(spacing: Design.Spacing.xLarge) {
Button("Tap Here") { }
.padding()
.background(Color.Accent.primary)
.foregroundStyle(.black)
.clipShape(.rect(cornerRadius: Design.CornerRadius.medium))
.pulsing(isActive: true)
Text("Pulsing highlights interactive areas")
.foregroundStyle(Color.TextColors.secondary)
.font(.caption)
}
}
}