add in icon pxd file

This commit is contained in:
Trey t
2026-01-21 17:59:20 -06:00
parent d97dec44b2
commit 3a135743f8
28 changed files with 347 additions and 8245 deletions

View File

@@ -0,0 +1,287 @@
//
// SportsIconImageGenerator.swift
// SportsTime
//
// Generates a 1024x1024 PNG with randomly scattered sports icons.
//
import SwiftUI
import UIKit
struct SportsIconImageGenerator {
/// All sports icons used in the animated background
private static let sportsIcons: [String] = [
// Sports balls
"football.fill",
"basketball.fill",
"baseball.fill",
"hockey.puck.fill",
"soccerball",
"tennisball.fill",
"volleyball.fill",
// Sports figures
"figure.run",
"figure.baseball",
"figure.basketball",
"figure.hockey",
"figure.soccer",
// Venues/events
"sportscourt.fill",
"stadium.fill",
"trophy.fill",
"ticket.fill",
// Travel/navigation
"mappin.circle.fill",
"car.fill",
"map.fill",
"flag.checkered"
]
/// Generates a 1024x1024 PNG image with randomly scattered sports icons
/// - Returns: A UIImage with transparent background containing the icons (exactly 1024x1024 pixels)
static func generateImage() -> UIImage {
let size = CGSize(width: 1024, height: 1024)
let maxIconSize: CGFloat = 150
let minIconSize: CGFloat = 40
let minSpacing: CGFloat = 80 // Minimum distance between icon centers
// Use scale 1.0 to ensure exact pixel dimensions (not retina scaled)
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
let renderer = UIGraphicsImageRenderer(size: size, format: format)
// Generate well-distributed positions using Poisson disk sampling
let positions = generateScatteredPositions(
in: size,
minSpacing: minSpacing,
targetCount: 45
)
let image = renderer.image { context in
// Clear background (transparent)
UIColor.clear.setFill()
context.fill(CGRect(origin: .zero, size: size))
// Shuffle icons to randomize which icon appears where
let shuffledIcons = sportsIcons.shuffled()
// Draw icons at scattered positions
for (index, position) in positions.enumerated() {
let iconName = shuffledIcons[index % shuffledIcons.count]
let targetSize = CGFloat.random(in: minIconSize...maxIconSize)
let rotation = CGFloat.random(in: -30...30) * .pi / 180
// Create SF Symbol image at a large point size for quality
let config = UIImage.SymbolConfiguration(pointSize: 100, weight: .regular)
if let symbolImage = UIImage(systemName: iconName, withConfiguration: config) {
// Tint with warm orange
let tintedImage = symbolImage.withTintColor(
UIColor(Theme.warmOrange),
renderingMode: .alwaysOriginal
)
// Calculate proportional size maintaining aspect ratio
let originalSize = tintedImage.size
let aspectRatio = originalSize.width / originalSize.height
let drawSize: CGSize
if aspectRatio > 1 {
// Wider than tall - constrain by width
drawSize = CGSize(width: targetSize, height: targetSize / aspectRatio)
} else {
// Taller than wide - constrain by height
drawSize = CGSize(width: targetSize * aspectRatio, height: targetSize)
}
// Save graphics state for rotation
context.cgContext.saveGState()
// Position at the scattered point
context.cgContext.translateBy(x: position.x, y: position.y)
context.cgContext.rotate(by: rotation)
// Draw the icon centered at origin with correct aspect ratio
let drawRect = CGRect(
x: -drawSize.width / 2,
y: -drawSize.height / 2,
width: drawSize.width,
height: drawSize.height
)
tintedImage.draw(in: drawRect)
context.cgContext.restoreGState()
}
}
}
return image
}
/// Generates well-distributed points using a grid-based approach with jitter
/// This ensures icons are scattered across the entire image, not clumped together
private static func generateScatteredPositions(
in size: CGSize,
minSpacing: CGFloat,
targetCount: Int
) -> [CGPoint] {
var positions: [CGPoint] = []
// Calculate grid dimensions to achieve roughly targetCount points
let gridSize = Int(ceil(sqrt(Double(targetCount))))
let cellWidth = size.width / CGFloat(gridSize)
let cellHeight = size.height / CGFloat(gridSize)
// Margin from edges
let margin: CGFloat = 40
for row in 0..<gridSize {
for col in 0..<gridSize {
// Base position at center of grid cell
let baseCenterX = (CGFloat(col) + 0.5) * cellWidth
let baseCenterY = (CGFloat(row) + 0.5) * cellHeight
// Add random jitter within the cell (up to 40% of cell size)
let jitterX = CGFloat.random(in: -cellWidth * 0.4...cellWidth * 0.4)
let jitterY = CGFloat.random(in: -cellHeight * 0.4...cellHeight * 0.4)
var x = baseCenterX + jitterX
var y = baseCenterY + jitterY
// Clamp to stay within margins
x = max(margin, min(size.width - margin, x))
y = max(margin, min(size.height - margin, y))
// Randomly skip some cells for more organic feel (keep ~80%)
if Double.random(in: 0...1) < 0.85 {
positions.append(CGPoint(x: x, y: y))
}
}
}
// Shuffle to randomize draw order (affects overlap)
return positions.shuffled()
}
/// Generates PNG data from the image
static func generatePNGData() -> Data? {
let image = generateImage()
return image.pngData()
}
}
// MARK: - SwiftUI View for generating and sharing
struct SportsIconImageGeneratorView: View {
@State private var generatedImage: UIImage?
@State private var isGenerating = false
@State private var showShareSheet = false
var body: some View {
VStack(spacing: 16) {
// Preview of generated image
if let image = generatedImage {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 300, maxHeight: 300)
.background(
// Checkerboard pattern to show transparency
CheckerboardPattern()
)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.secondary.opacity(0.3), lineWidth: 1)
)
}
// Generate button
Button {
generateNewImage()
} label: {
HStack {
if isGenerating {
ProgressView()
.tint(.white)
} else {
Image(systemName: "dice.fill")
}
Text(generatedImage == nil ? "Generate Image" : "Regenerate")
}
.frame(maxWidth: .infinity)
.padding()
.background(Theme.warmOrange)
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
.disabled(isGenerating)
// Share button (only visible when image exists)
if generatedImage != nil {
ShareLink(
item: Image(uiImage: generatedImage!),
preview: SharePreview("Sports Icons", image: Image(uiImage: generatedImage!))
) {
HStack {
Image(systemName: "square.and.arrow.up")
Text("Share via AirDrop")
}
.frame(maxWidth: .infinity)
.padding()
.background(Color.blue)
.foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
}
}
}
.padding()
}
private func generateNewImage() {
isGenerating = true
// Generate on background thread to avoid UI freeze
DispatchQueue.global(qos: .userInitiated).async {
let image = SportsIconImageGenerator.generateImage()
DispatchQueue.main.async {
withAnimation {
generatedImage = image
isGenerating = false
}
}
}
}
}
// MARK: - Checkerboard Pattern for transparency preview
private struct CheckerboardPattern: View {
var body: some View {
Canvas { context, size in
let tileSize: CGFloat = 10
let rows = Int(ceil(size.height / tileSize))
let cols = Int(ceil(size.width / tileSize))
for row in 0..<rows {
for col in 0..<cols {
let isLight = (row + col) % 2 == 0
let rect = CGRect(
x: CGFloat(col) * tileSize,
y: CGFloat(row) * tileSize,
width: tileSize,
height: tileSize
)
context.fill(
Path(rect),
with: .color(isLight ? Color.gray.opacity(0.2) : Color.gray.opacity(0.3))
)
}
}
}
}
}
#Preview {
SportsIconImageGeneratorView()
}

View File

@@ -28,8 +28,8 @@ struct SettingsView: View {
// Theme Selection
themeSection
// UI Design Style
designStyleSection
// Home Screen Animations
animationsSection
// Sports Preferences
sportsSection
@@ -37,6 +37,9 @@ struct SettingsView: View {
// Travel Preferences
travelSection
// Icon Generator
iconGeneratorSection
// About
aboutSection
@@ -170,54 +173,30 @@ struct SettingsView: View {
.listRowBackground(Theme.cardBackground(colorScheme))
}
// MARK: - Design Style Section
// MARK: - Animations Section
private var designStyleSection: some View {
private var animationsSection: some View {
Section {
ForEach(UIDesignStyle.allCases) { style in
Button {
withAnimation(.easeInOut(duration: 0.2)) {
DesignStyleManager.shared.setStyle(style)
Toggle(isOn: Binding(
get: { DesignStyleManager.shared.animationsEnabled },
set: { DesignStyleManager.shared.animationsEnabled = $0 }
)) {
Label {
VStack(alignment: .leading, spacing: 2) {
Text("Animated Background")
.font(.body)
.foregroundStyle(.primary)
Text("Show animated sports graphics on home screen")
.font(.caption)
.foregroundStyle(.secondary)
}
} label: {
HStack(spacing: 12) {
// Icon with accent color
ZStack {
Circle()
.fill(style.accentColor.opacity(0.15))
.frame(width: 36, height: 36)
Image(systemName: style.iconName)
.font(.system(size: 16))
.foregroundStyle(style.accentColor)
}
VStack(alignment: .leading, spacing: 2) {
Text(style.rawValue)
.font(.body)
.foregroundStyle(.primary)
Text(style.description)
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(1)
}
Spacer()
if DesignStyleManager.shared.currentStyle == style {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(style.accentColor)
.font(.title3)
}
}
.contentShape(Rectangle())
} icon: {
Image(systemName: "sparkles")
.foregroundStyle(Theme.warmOrange)
}
.buttonStyle(.plain)
}
} header: {
Text("Home Screen Style")
} footer: {
Text("Choose a visual aesthetic for the home screen.")
Text("Home Screen")
}
.listRowBackground(Theme.cardBackground(colorScheme))
}
@@ -304,6 +283,35 @@ struct SettingsView: View {
.listRowBackground(Theme.cardBackground(colorScheme))
}
// MARK: - Icon Generator Section
private var iconGeneratorSection: some View {
Section {
NavigationLink {
SportsIconImageGeneratorView()
.navigationTitle("Icon Generator")
.navigationBarTitleDisplayMode(.inline)
} label: {
Label {
VStack(alignment: .leading, spacing: 2) {
Text("Sports Icon Generator")
.font(.body)
.foregroundStyle(.primary)
Text("Create shareable icon images")
.font(.caption)
.foregroundStyle(.secondary)
}
} icon: {
Image(systemName: "photo.badge.plus")
.foregroundStyle(Theme.warmOrange)
}
}
} header: {
Text("Creative Tools")
}
.listRowBackground(Theme.cardBackground(colorScheme))
}
// MARK: - Reset Section
private var resetSection: some View {