- 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
82 lines
2.5 KiB
Swift
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)
|
|
}
|
|
}
|
|
}
|