// // ShareCardSportBackground.swift // SportsTime // // New visual language for share cards: atmospheric gradients, sport-specific linework, // and strong edge shading for depth. // import SwiftUI struct ShareCardSportBackground: View { let sports: Set let theme: ShareTheme private var primarySport: Sport? { sports.sorted { $0.rawValue < $1.rawValue }.first } var body: some View { ZStack { baseLayer glowLayer patternLayer edgeShadeLayer } } private var baseLayer: some View { LinearGradient( colors: [ theme.gradientColors.first ?? .black, theme.midGradientColor, theme.gradientColors.last ?? .black ], startPoint: .topLeading, endPoint: .bottomTrailing ) .overlay { LinearGradient( colors: [ .black.opacity(0.20), .clear, .black.opacity(0.30) ], startPoint: .top, endPoint: .bottom ) } } private var glowLayer: some View { GeometryReader { geo in ZStack { Circle() .fill(theme.accentColor.opacity(0.24)) .frame(width: geo.size.width * 0.95) .offset(x: -geo.size.width * 0.30, y: -geo.size.height * 0.35) Circle() .fill(theme.textColor.opacity(0.10)) .frame(width: geo.size.width * 0.72) .offset(x: geo.size.width * 0.35, y: -geo.size.height * 0.10) Ellipse() .fill(theme.accentColor.opacity(0.16)) .frame(width: geo.size.width * 1.20, height: geo.size.height * 0.45) .offset(y: geo.size.height * 0.42) } .blur(radius: 58) } } @ViewBuilder private var patternLayer: some View { switch primarySport { case .mlb?: BaseballStitchPattern() .stroke(theme.textColor.opacity(0.11), lineWidth: 1.8) case .nba?, .wnba?: CourtArcPattern() .stroke(theme.textColor.opacity(0.10), lineWidth: 1.6) case .nhl?: IceShardPattern() .stroke(theme.textColor.opacity(0.10), lineWidth: 1.4) case .nfl?, .mls?, .nwsl?, nil: PitchBandPattern() .fill(theme.textColor.opacity(0.09)) } } private var edgeShadeLayer: some View { ZStack { RadialGradient( colors: [.clear, .black.opacity(0.45)], center: .center, startRadius: 120, endRadius: 980 ) LinearGradient( colors: [.black.opacity(0.42), .clear, .black.opacity(0.42)], startPoint: .top, endPoint: .bottom ) } } } private struct BaseballStitchPattern: Shape { func path(in rect: CGRect) -> Path { var path = Path() let rowStep: CGFloat = 150 let colStep: CGFloat = 220 var y: CGFloat = -80 while y < rect.maxY + 120 { var x: CGFloat = -60 while x < rect.maxX + 180 { let start = CGPoint(x: x, y: y) let mid = CGPoint(x: x + 85, y: y + 26) let end = CGPoint(x: x + 170, y: y + 62) path.move(to: start) path.addQuadCurve(to: mid, control: CGPoint(x: x + 48, y: y - 22)) path.addQuadCurve(to: end, control: CGPoint(x: x + 122, y: y + 70)) x += colStep } y += rowStep } return path } } private struct CourtArcPattern: Shape { func path(in rect: CGRect) -> Path { var path = Path() let spacing: CGFloat = 170 var y: CGFloat = -80 while y < rect.maxY + 120 { var x: CGFloat = -80 while x < rect.maxX + 120 { let circleRect = CGRect(x: x, y: y, width: 132, height: 132) path.addEllipse(in: circleRect) path.move(to: CGPoint(x: x - 20, y: y + 66)) path.addLine(to: CGPoint(x: x + 152, y: y + 66)) x += spacing } y += spacing } return path } } private struct IceShardPattern: Shape { func path(in rect: CGRect) -> Path { var path = Path() let spacing: CGFloat = 92 var baseX: CGFloat = -120 while baseX < rect.maxX + 200 { path.move(to: CGPoint(x: baseX, y: -80)) path.addLine(to: CGPoint(x: baseX + 44, y: rect.maxY * 0.26)) path.addLine(to: CGPoint(x: baseX - 26, y: rect.maxY * 0.52)) path.addLine(to: CGPoint(x: baseX + 30, y: rect.maxY + 100)) baseX += spacing } var baseY: CGFloat = -60 while baseY < rect.maxY + 160 { path.move(to: CGPoint(x: -80, y: baseY)) path.addLine(to: CGPoint(x: rect.maxX + 80, y: baseY + 50)) baseY += spacing } return path } } private struct PitchBandPattern: Shape { func path(in rect: CGRect) -> Path { var path = Path() let stripeWidth: CGFloat = 54 let stripeGap: CGFloat = 34 let diagonalLength = rect.width + rect.height + 240 var x: CGFloat = -rect.height - 120 while x < rect.width + rect.height + 120 { path.addRect( CGRect( x: x, y: -140, width: stripeWidth, height: diagonalLength ) ) x += stripeWidth + stripeGap } return path.applying(CGAffineTransform(rotationAngle: .pi / 3.8)) } } #Preview("Share Background") { ShareCardSportBackground(sports: [.nfl], theme: .midnight) .frame(width: 420, height: 720) }