- Add ShareCardSportBackground with floating sport icons for share cards - Share cards now show sport-specific backgrounds (single or multiple sports) - Achievement collection share respects sport filter selection - Add ability to share individual achievements from detail sheet - Trip wizard ReviewStep highlights missing required fields in red - Add FieldValidation model to TripWizardViewModel Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
81 lines
2.3 KiB
Swift
81 lines
2.3 KiB
Swift
//
|
|
// ShareCardSportBackground.swift
|
|
// SportsTime
|
|
//
|
|
// Sport-specific background with floating league icons for share cards.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
struct ShareCardSportBackground: View {
|
|
let sports: Set<Sport>
|
|
let theme: ShareTheme
|
|
|
|
/// Fixed positions for 12 scattered icons (x, y as percentage, rotation, scale)
|
|
private let iconConfigs: [(x: CGFloat, y: CGFloat, rotation: Double, scale: CGFloat)] = [
|
|
(0.08, 0.08, -20, 0.9),
|
|
(0.92, 0.05, 15, 0.85),
|
|
(0.15, 0.28, 25, 0.8),
|
|
(0.88, 0.22, -10, 0.95),
|
|
(0.05, 0.48, 30, 0.85),
|
|
(0.95, 0.45, -25, 0.9),
|
|
(0.12, 0.68, -15, 0.8),
|
|
(0.90, 0.65, 20, 0.85),
|
|
(0.08, 0.88, 10, 0.9),
|
|
(0.92, 0.85, -30, 0.8),
|
|
(0.50, 0.15, 5, 0.75),
|
|
(0.50, 0.90, -5, 0.75)
|
|
]
|
|
|
|
/// Get icon name for a given index, cycling through sports
|
|
private func iconName(at index: Int) -> String {
|
|
let sportArray = Array(sports).sorted { $0.rawValue < $1.rawValue }
|
|
guard !sportArray.isEmpty else {
|
|
return "sportscourt.fill"
|
|
}
|
|
return sportArray[index % sportArray.count].iconName
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
// Base gradient
|
|
LinearGradient(
|
|
colors: theme.gradientColors,
|
|
startPoint: .top,
|
|
endPoint: .bottom
|
|
)
|
|
|
|
// Scattered sport icons
|
|
GeometryReader { geo in
|
|
ForEach(0..<iconConfigs.count, id: \.self) { index in
|
|
let config = iconConfigs[index]
|
|
Image(systemName: iconName(at: index))
|
|
.font(.system(size: 32 * config.scale))
|
|
.foregroundStyle(theme.accentColor.opacity(0.15))
|
|
.rotationEffect(.degrees(config.rotation))
|
|
.position(
|
|
x: geo.size.width * config.x,
|
|
y: geo.size.height * config.y
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#Preview("Single Sport - MLB") {
|
|
ShareCardSportBackground(
|
|
sports: [.mlb],
|
|
theme: .sunset
|
|
)
|
|
.frame(width: 400, height: 600)
|
|
}
|
|
|
|
#Preview("Multiple Sports") {
|
|
ShareCardSportBackground(
|
|
sports: [.mlb, .nba, .nfl],
|
|
theme: .dark
|
|
)
|
|
.frame(width: 400, height: 600)
|
|
}
|