From 1ecbbbf9eedc791c7aacddd93b0b0f4f719c1042 Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 31 Dec 2025 14:20:36 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- .../Theme/BrandingConfig+Baccarat.swift | 40 ---- Baccarat/Baccarat/Theme/BrandingConfig.swift | 30 +++ ...g+Blackjack.swift => BrandingConfig.swift} | 23 +- .../Development/BrandingPreviewView.swift | 130 ------------ .../Views/Development/IconGeneratorView.swift | 196 ------------------ CasinoKit/Sources/CasinoKit/Exports.swift | 2 + .../Views/Branding/AppIconView.swift | 67 +++--- .../Views/Branding}/BrandingPreviewView.swift | 112 ++++++---- .../Views/Branding}/IconGeneratorView.swift | 54 +++-- .../Views/Branding/IconRenderer.swift | 2 +- .../Views/Branding/LaunchScreenView.swift | 23 +- 11 files changed, 189 insertions(+), 490 deletions(-) delete mode 100644 Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift create mode 100644 Baccarat/Baccarat/Theme/BrandingConfig.swift rename Blackjack/Blackjack/Theme/{BrandingConfig+Blackjack.swift => BrandingConfig.swift} (60%) delete mode 100644 Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift delete mode 100644 Blackjack/Blackjack/Views/Development/IconGeneratorView.swift rename {Baccarat/Baccarat/Views/Development => CasinoKit/Sources/CasinoKit/Views/Branding}/BrandingPreviewView.swift (50%) rename {Baccarat/Baccarat/Views/Development => CasinoKit/Sources/CasinoKit/Views/Branding}/IconGeneratorView.swift (81%) diff --git a/Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift b/Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift deleted file mode 100644 index 240138d..0000000 --- a/Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// BrandingConfig+Baccarat.swift -// Baccarat -// -// Baccarat-specific branding configurations for AppIconView and LaunchScreenView. -// - -import SwiftUI -import CasinoKit - -extension AppIconConfig { - /// Baccarat game icon configuration. - static let baccarat = AppIconConfig( - title: "BACCARAT", - iconSymbol: "suit.spade.fill" - ) -} - -extension LaunchScreenConfig { - /// Baccarat game launch screen configuration. - static let baccarat = LaunchScreenConfig( - title: "BACCARAT", - tagline: "The Classic Casino Card Game", - iconSymbols: ["suit.spade.fill", "suit.heart.fill"] - ) -} - -// MARK: - For Development Preview Comparison - -extension AppIconConfig { - /// Blackjack config for side-by-side comparison in dev previews. - static let blackjack = AppIconConfig( - title: "BLACKJACK", - subtitle: "21", - iconSymbol: "suit.club.fill", - primaryColor: Color(red: 0.05, green: 0.35, blue: 0.15), - secondaryColor: Color(red: 0.03, green: 0.2, blue: 0.1) - ) -} - diff --git a/Baccarat/Baccarat/Theme/BrandingConfig.swift b/Baccarat/Baccarat/Theme/BrandingConfig.swift new file mode 100644 index 0000000..85f8008 --- /dev/null +++ b/Baccarat/Baccarat/Theme/BrandingConfig.swift @@ -0,0 +1,30 @@ +// +// BrandingConfig.swift +// Baccarat +// +// App-specific branding configurations for icons and launch screens. +// + +import SwiftUI +import CasinoKit + +// MARK: - App Icon Configuration + +extension AppIconConfig { + /// Baccarat app icon configuration. + static let baccarat = AppIconConfig( + title: "BACCARAT", + iconSymbol: "suit.spade.fill" + ) +} + +// MARK: - Launch Screen Configuration + +extension LaunchScreenConfig { + /// Baccarat launch screen configuration. + static let baccarat = LaunchScreenConfig( + title: "BACCARAT", + tagline: "The Classic Casino Card Game", + iconSymbols: ["suit.spade.fill", "suit.heart.fill"] + ) +} diff --git a/Blackjack/Blackjack/Theme/BrandingConfig+Blackjack.swift b/Blackjack/Blackjack/Theme/BrandingConfig.swift similarity index 60% rename from Blackjack/Blackjack/Theme/BrandingConfig+Blackjack.swift rename to Blackjack/Blackjack/Theme/BrandingConfig.swift index d56716e..3c827ff 100644 --- a/Blackjack/Blackjack/Theme/BrandingConfig+Blackjack.swift +++ b/Blackjack/Blackjack/Theme/BrandingConfig.swift @@ -1,15 +1,17 @@ // -// BrandingConfig+Blackjack.swift +// BrandingConfig.swift // Blackjack // -// Blackjack-specific branding configurations for AppIconView and LaunchScreenView. +// App-specific branding configurations for icons and launch screens. // import SwiftUI import CasinoKit +// MARK: - App Icon Configuration + extension AppIconConfig { - /// Blackjack game icon configuration. + /// Blackjack app icon configuration. static let blackjack = AppIconConfig( title: "BLACKJACK", subtitle: "21", @@ -19,8 +21,10 @@ extension AppIconConfig { ) } +// MARK: - Launch Screen Configuration + extension LaunchScreenConfig { - /// Blackjack game launch screen configuration. + /// Blackjack launch screen configuration. static let blackjack = LaunchScreenConfig( title: "BLACKJACK", subtitle: "21", @@ -30,14 +34,3 @@ extension LaunchScreenConfig { secondaryColor: Color(red: 0.03, green: 0.2, blue: 0.1) ) } - -// MARK: - For Development Preview Comparison - -extension AppIconConfig { - /// Baccarat config for side-by-side comparison in dev previews. - static let baccarat = AppIconConfig( - title: "BACCARAT", - iconSymbol: "suit.spade.fill" - ) -} - diff --git a/Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift b/Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift deleted file mode 100644 index becec81..0000000 --- a/Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// BrandingPreviewView.swift -// Blackjack -// -// Development view for previewing and exporting app icons and launch screens. -// Access this during development to generate icon assets. -// - -import SwiftUI -import CasinoKit - -/// Preview view for app branding assets. -/// Use this during development to preview and export icons. -struct BrandingPreviewView: View { - var body: some View { - TabView { - // App Icon Preview - ScrollView { - VStack(spacing: Design.Spacing.xxxLarge) { - Text("App Icon") - .font(.largeTitle.bold()) - - AppIconView(config: .blackjack, size: 300) - .clipShape(.rect(cornerRadius: 300 * 0.22)) - .shadow(radius: Design.Shadow.radiusXLarge) - - Text("All Sizes") - .font(.title2.bold()) - - LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: Design.Spacing.xLarge) { - ForEach([180, 120, 87, 60, 40], id: \.self) { size in - VStack { - AppIconView(config: .blackjack, size: CGFloat(size)) - .clipShape(.rect(cornerRadius: CGFloat(size) * 0.22)) - Text("\(size)px") - .font(.caption) - .foregroundStyle(.secondary) - } - } - } - - instructionsSection - } - .padding() - } - .tabItem { - Label("Icon", systemImage: "app.fill") - } - - // Launch Screen Preview - LaunchScreenView(config: .blackjack) - .tabItem { - Label("Launch", systemImage: "rectangle.portrait.fill") - } - - // Other Games Preview - ScrollView { - VStack(spacing: Design.Spacing.xxxLarge) { - Text("Other Game Icons") - .font(.largeTitle.bold()) - - HStack(spacing: Design.Spacing.xLarge) { - VStack { - AppIconView(config: .baccarat, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Baccarat") - .font(.caption) - } - - VStack { - AppIconView(config: .poker, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Poker") - .font(.caption) - } - - VStack { - AppIconView(config: .roulette, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Roulette") - .font(.caption) - } - } - - Text("These show how the same pattern works for other games") - .font(.callout) - .foregroundStyle(.secondary) - } - .padding() - } - .tabItem { - Label("Others", systemImage: "square.grid.2x2") - } - } - } - - private var instructionsSection: some View { - VStack(alignment: .leading, spacing: Design.Spacing.medium) { - Text("How to Export Icons") - .font(.headline) - - VStack(alignment: .leading, spacing: Design.Spacing.small) { - Text("Option 1: Screenshot from Preview") - .font(.subheadline.bold()) - Text("• Run the preview in Xcode") - Text("• Screenshot the 1024px icon") - Text("• Use an online tool to generate all sizes") - } - - VStack(alignment: .leading, spacing: Design.Spacing.small) { - Text("Option 2: Use IconRenderer in Code") - .font(.subheadline.bold()) - Text("• Call IconRenderer.renderAppIcon(config: .blackjack)") - Text("• Save the resulting UIImage to files") - Text("• Add to Assets.xcassets/AppIcon") - } - } - .font(.callout) - .foregroundStyle(.secondary) - .frame(maxWidth: .infinity, alignment: .leading) - .padding() - .background(Color.gray.opacity(Design.Opacity.subtle)) - .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) - } -} - -#Preview { - BrandingPreviewView() -} - diff --git a/Blackjack/Blackjack/Views/Development/IconGeneratorView.swift b/Blackjack/Blackjack/Views/Development/IconGeneratorView.swift deleted file mode 100644 index 207fcf4..0000000 --- a/Blackjack/Blackjack/Views/Development/IconGeneratorView.swift +++ /dev/null @@ -1,196 +0,0 @@ -// -// IconGeneratorView.swift -// Blackjack -// -// Development tool to generate and export app icon images. -// Run this view, tap the button, then find the icons in the Files app. -// - -import SwiftUI -import CasinoKit - -/// A development view that generates and saves app icon images. -/// After running, find the icons in Files app → On My iPhone → Blackjack -struct IconGeneratorView: View { - @State private var status: String = "Tap the button to generate icons" - @State private var isGenerating = false - @State private var generatedIcons: [GeneratedIcon] = [] - - // Development view: hardcoded sizes acceptable - private let previewSize: CGFloat = 200 - private let iconCornerRadiusRatio: CGFloat = 0.22 - - var body: some View { - NavigationStack { - ScrollView { - VStack(spacing: Design.Spacing.xxLarge) { - // Preview - AppIconView(config: .blackjack, size: previewSize) - .clipShape(.rect(cornerRadius: previewSize * iconCornerRadiusRatio)) - .shadow(radius: 10) - - Text("App Icon Preview") - .font(.headline) - - // Generate button - Button { - Task { - await generateIcons() - } - } label: { - HStack { - if isGenerating { - ProgressView() - .tint(.white) - } - Text(isGenerating ? "Generating..." : "Generate & Save Icons") - } - .font(.headline) - .foregroundStyle(.white) - .frame(maxWidth: .infinity) - .padding() - .background(isGenerating ? Color.gray : Color.blue) - .clipShape(.rect(cornerRadius: 12)) - } - .disabled(isGenerating) - .padding(.horizontal) - - // Status - Text(status) - .font(.callout) - .foregroundStyle(.secondary) - .multilineTextAlignment(.center) - .padding(.horizontal) - - // Generated icons - if !generatedIcons.isEmpty { - VStack(alignment: .leading, spacing: 12) { - Text("Generated Icons:") - .font(.headline) - - ForEach(generatedIcons) { icon in - HStack { - Image(systemName: "checkmark.circle.fill") - .foregroundStyle(.green) - Text(icon.filename) - .font(.caption.monospaced()) - Spacer() - Text("\(Int(icon.size))px") - .font(.caption) - .foregroundStyle(.secondary) - } - } - } - .padding() - .background(Color.green.opacity(Design.Opacity.subtle)) - .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) - .padding(.horizontal) - } - - // Instructions - instructionsSection - } - .padding(.vertical) - } - .navigationTitle("Icon Generator") - } - } - - private var instructionsSection: some View { - VStack(alignment: .leading, spacing: 12) { - Text("After generating:") - .font(.headline) - - VStack(alignment: .leading, spacing: Design.Spacing.small) { - instructionRow(number: 1, text: "Open Files app on your device/simulator") - instructionRow(number: 2, text: "Navigate to: On My iPhone → Blackjack") - instructionRow(number: 3, text: "Find the AppIcon-1024.png file") - instructionRow(number: 4, text: "AirDrop or share to your Mac") - instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon") - } - - Divider() - - Text("Alternative: Use an online tool") - .font(.subheadline.bold()) - Text("Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically.") - .font(.caption) - .foregroundStyle(.secondary) - } - .padding() - .background(Color.gray.opacity(Design.Opacity.subtle)) - .clipShape(.rect(cornerRadius: Design.CornerRadius.medium)) - .padding(.horizontal) - } - - private func instructionRow(number: Int, text: String) -> some View { - HStack(alignment: .top, spacing: 8) { - Text("\(number).") - .font(.callout.bold()) - .foregroundStyle(.blue) - Text(text) - .font(.callout) - } - } - - @MainActor - private func generateIcons() async { - isGenerating = true - generatedIcons = [] - status = "Generating icons..." - - let sizes: [(CGFloat, String)] = [ - (1024, "AppIcon-1024"), - (180, "AppIcon-180"), - (120, "AppIcon-120"), - (87, "AppIcon-87"), - (80, "AppIcon-80"), - (60, "AppIcon-60"), - (40, "AppIcon-40") - ] - - let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] - - for (size, name) in sizes { - // Render the icon - let view = AppIconView(config: .blackjack, size: size) - let renderer = ImageRenderer(content: view) - renderer.scale = 1.0 - - if let uiImage = renderer.uiImage, - let data = uiImage.pngData() { - let filename = "\(name).png" - let fileURL = documentsPath.appending(path: filename) - - do { - try data.write(to: fileURL) - generatedIcons.append(GeneratedIcon(filename: filename, size: size)) - } catch { - status = "Error saving \(filename): \(error.localizedDescription)" - } - } - - // Small delay for UI feedback - try? await Task.sleep(for: .milliseconds(100)) - } - - if generatedIcons.count == sizes.count { - status = "✅ All icons saved to Documents folder!\nOpen Files app to find them." - } else { - status = "⚠️ Some icons failed to generate" - } - - isGenerating = false - } -} - -struct GeneratedIcon: Identifiable { - let id = UUID() - let filename: String - let size: CGFloat -} - -#Preview { - IconGeneratorView() -} - diff --git a/CasinoKit/Sources/CasinoKit/Exports.swift b/CasinoKit/Sources/CasinoKit/Exports.swift index 086a44b..94a6c23 100644 --- a/CasinoKit/Sources/CasinoKit/Exports.swift +++ b/CasinoKit/Sources/CasinoKit/Exports.swift @@ -78,6 +78,8 @@ // - AppIconView, AppIconConfig // - LaunchScreenView, LaunchScreenConfig, StaticLaunchScreenView // - IconRenderer, IconExportView +// - IconGeneratorView, GeneratedIconInfo (development tool for exporting icons) +// - BrandingPreviewView (development tool for previewing all branding assets) // MARK: - Theme // - CasinoTheme (protocol) diff --git a/CasinoKit/Sources/CasinoKit/Views/Branding/AppIconView.swift b/CasinoKit/Sources/CasinoKit/Views/Branding/AppIconView.swift index 091a9b9..045dd90 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Branding/AppIconView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Branding/AppIconView.swift @@ -33,27 +33,21 @@ public struct AppIconConfig: Sendable { self.accentColor = accentColor } - // MARK: - Example Preset Configurations - // Game-specific presets should be defined in the respective apps as extensions. + // MARK: - Example Configuration (for previews only) - /// Poker game icon configuration (example preset). - public static let poker = AppIconConfig( - title: "POKER", - iconSymbol: "suit.diamond.fill", - accentColor: .red - ) - - /// Roulette game icon configuration (example preset). - public static let roulette = AppIconConfig( - title: "ROULETTE", - iconSymbol: "circle.grid.3x3.fill", - primaryColor: Color(red: 0.4, green: 0.1, blue: 0.1), - secondaryColor: Color(red: 0.25, green: 0.05, blue: 0.05) + /// Example configuration for CasinoKit previews. + /// Apps should define their own configs in `BrandingConfig.swift`. + public static let example = AppIconConfig( + title: "CASINO", + iconSymbol: "suit.diamond.fill" ) } /// A customizable app icon view for casino games. /// Render this view to create your app icon assets. +/// +/// **Important**: This view generates a full-bleed square icon. iOS applies its own +/// superellipse mask, so decorative borders are inset to avoid clipping at the edges. public struct AppIconView: View { let config: AppIconConfig let size: CGFloat @@ -63,16 +57,20 @@ public struct AppIconView: View { self.size = size } - private var cornerRadius: CGFloat { size * 0.22 } + // iOS clips app icons with a superellipse mask. Inset decorative elements + // to prevent clipping. The inset is approximately 4% of the icon size. + private var borderInset: CGFloat { size * 0.04 } + private var insetCornerRadius: CGFloat { (size - borderInset * 2) * 0.18 } private var iconSize: CGFloat { size * 0.35 } private var titleSize: CGFloat { size * 0.12 } private var subtitleSize: CGFloat { size * 0.25 } - private var borderWidth: CGFloat { size * 0.02 } + private var borderWidth: CGFloat { size * 0.015 } public var body: some View { ZStack { - // Background gradient - RoundedRectangle(cornerRadius: cornerRadius) + // Background gradient - full bleed, no rounded corners + // iOS will apply its own superellipse mask + Rectangle() .fill( LinearGradient( colors: [config.primaryColor, config.secondaryColor], @@ -85,8 +83,8 @@ public struct AppIconView: View { DiamondPatternOverlay(size: size) .opacity(0.08) - // Gold border - RoundedRectangle(cornerRadius: cornerRadius) + // Decorative border - inset to avoid iOS mask clipping + RoundedRectangle(cornerRadius: insetCornerRadius) .strokeBorder( LinearGradient( colors: [ @@ -99,6 +97,7 @@ public struct AppIconView: View { ), lineWidth: borderWidth ) + .padding(borderInset) // Content VStack(spacing: size * 0.03) { @@ -179,24 +178,24 @@ private struct DiamondPatternOverlay: View { } } -// MARK: - Preview +// MARK: - Previews -#Preview("Poker Icon") { - AppIconView(config: .poker, size: 512) +#Preview("App Icon") { + AppIconView(config: .example, size: 512) + .clipShape(.rect(cornerRadius: 512 * 0.22)) .padding() .background(Color.gray) } -#Preview("Roulette Icon") { - AppIconView(config: .roulette, size: 512) - .padding() - .background(Color.gray) -} - -#Preview("All Icons") { - HStack(spacing: 20) { - AppIconView(config: .poker, size: 200) - AppIconView(config: .roulette, size: 200) +#Preview("All Sizes") { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 20) { + ForEach([180, 120, 87, 60, 40], id: \.self) { size in + VStack { + AppIconView(config: .example, size: CGFloat(size)) + .clipShape(.rect(cornerRadius: CGFloat(size) * 0.22)) + Text("\(size)px").font(.caption) + } + } } .padding() .background(Color.gray) diff --git a/Baccarat/Baccarat/Views/Development/BrandingPreviewView.swift b/CasinoKit/Sources/CasinoKit/Views/Branding/BrandingPreviewView.swift similarity index 50% rename from Baccarat/Baccarat/Views/Development/BrandingPreviewView.swift rename to CasinoKit/Sources/CasinoKit/Views/Branding/BrandingPreviewView.swift index 94e6bee..a0e3074 100644 --- a/Baccarat/Baccarat/Views/Development/BrandingPreviewView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Branding/BrandingPreviewView.swift @@ -1,18 +1,47 @@ // // BrandingPreviewView.swift -// Baccarat +// CasinoKit // // Development view for previewing and exporting app icons and launch screens. // Access this during development to generate icon assets. // import SwiftUI -import CasinoKit /// Preview view for app branding assets. /// Use this during development to preview and export icons. -struct BrandingPreviewView: View { - var body: some View { +public struct BrandingPreviewView: View { + let iconConfig: AppIconConfig + let launchConfig: LaunchScreenConfig + let appName: String + + /// Other game configs for comparison preview. + let otherGames: [(name: String, config: AppIconConfig)] + + // Development view: fixed sizes acceptable + private let largePreviewSize: CGFloat = 300 + private let comparisonIconSize: CGFloat = 150 + private let iconCornerRadiusRatio: CGFloat = 0.22 + + /// Creates a branding preview view. + /// - Parameters: + /// - iconConfig: The app icon configuration for this game. + /// - launchConfig: The launch screen configuration for this game. + /// - appName: The app name for display purposes. + /// - otherGames: Other game configs for side-by-side comparison (optional). + public init( + iconConfig: AppIconConfig, + launchConfig: LaunchScreenConfig, + appName: String, + otherGames: [(name: String, config: AppIconConfig)] = [] + ) { + self.iconConfig = iconConfig + self.launchConfig = launchConfig + self.appName = appName + self.otherGames = otherGames + } + + public var body: some View { TabView { // App Icon Preview ScrollView { @@ -20,8 +49,8 @@ struct BrandingPreviewView: View { Text("App Icon") .font(.largeTitle.bold()) - AppIconView(config: .baccarat, size: 300) - .clipShape(.rect(cornerRadius: 300 * 0.22)) + AppIconView(config: iconConfig, size: largePreviewSize) + .clipShape(.rect(cornerRadius: largePreviewSize * iconCornerRadiusRatio)) .shadow(radius: 20) Text("All Sizes") @@ -30,8 +59,8 @@ struct BrandingPreviewView: View { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100))], spacing: 20) { ForEach([180, 120, 87, 60, 40], id: \.self) { size in VStack { - AppIconView(config: .baccarat, size: CGFloat(size)) - .clipShape(.rect(cornerRadius: CGFloat(size) * 0.22)) + AppIconView(config: iconConfig, size: CGFloat(size)) + .clipShape(.rect(cornerRadius: CGFloat(size) * iconCornerRadiusRatio)) Text("\(size)px") .font(.caption) .foregroundStyle(.secondary) @@ -48,48 +77,38 @@ struct BrandingPreviewView: View { } // Launch Screen Preview - LaunchScreenView(config: .baccarat) + LaunchScreenView(config: launchConfig) .tabItem { Label("Launch", systemImage: "rectangle.portrait.fill") } - // Other Games Preview - ScrollView { - VStack(spacing: 32) { - Text("Other Game Icons") - .font(.largeTitle.bold()) - - HStack(spacing: 20) { - VStack { - AppIconView(config: .blackjack, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Blackjack") - .font(.caption) + // Other Games Preview (if provided) + if !otherGames.isEmpty { + ScrollView { + VStack(spacing: 32) { + Text("Other Game Icons") + .font(.largeTitle.bold()) + + LazyVGrid(columns: [GridItem(.adaptive(minimum: 160))], spacing: 20) { + ForEach(otherGames, id: \.name) { game in + VStack { + AppIconView(config: game.config, size: comparisonIconSize) + .clipShape(.rect(cornerRadius: comparisonIconSize * iconCornerRadiusRatio)) + Text(game.name) + .font(.caption) + } + } } - VStack { - AppIconView(config: .poker, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Poker") - .font(.caption) - } - - VStack { - AppIconView(config: .roulette, size: 150) - .clipShape(.rect(cornerRadius: 150 * 0.22)) - Text("Roulette") - .font(.caption) - } + Text("These show how the same pattern works for other games") + .font(.callout) + .foregroundStyle(.secondary) } - - Text("These show how the same pattern works for other games") - .font(.callout) - .foregroundStyle(.secondary) + .padding() + } + .tabItem { + Label("Others", systemImage: "square.grid.2x2") } - .padding() - } - .tabItem { - Label("Others", systemImage: "square.grid.2x2") } } } @@ -110,7 +129,7 @@ struct BrandingPreviewView: View { VStack(alignment: .leading, spacing: 8) { Text("Option 2: Use IconRenderer in Code") .font(.subheadline.bold()) - Text("• Call IconRenderer.renderAppIcon(config: .baccarat)") + Text("• Call IconRenderer.renderAppIcon(config: .\(appName.lowercased()))") Text("• Save the resulting UIImage to files") Text("• Add to Assets.xcassets/AppIcon") } @@ -125,6 +144,9 @@ struct BrandingPreviewView: View { } #Preview { - BrandingPreviewView() + BrandingPreviewView( + iconConfig: .example, + launchConfig: .example, + appName: "Casino" + ) } - diff --git a/Baccarat/Baccarat/Views/Development/IconGeneratorView.swift b/CasinoKit/Sources/CasinoKit/Views/Branding/IconGeneratorView.swift similarity index 81% rename from Baccarat/Baccarat/Views/Development/IconGeneratorView.swift rename to CasinoKit/Sources/CasinoKit/Views/Branding/IconGeneratorView.swift index a76c889..cd575c2 100644 --- a/Baccarat/Baccarat/Views/Development/IconGeneratorView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Branding/IconGeneratorView.swift @@ -1,28 +1,43 @@ // // IconGeneratorView.swift -// Baccarat +// CasinoKit // // Development tool to generate and export app icon images. // Run this view, tap the button, then find the icons in the Files app. // import SwiftUI -import CasinoKit /// A development view that generates and saves app icon images. -/// After running, find the icons in Files app → On My iPhone → Baccarat -struct IconGeneratorView: View { +/// After running, find the icons in Files app → On My iPhone → [App Name] +public struct IconGeneratorView: View { + let config: AppIconConfig + let appName: String + @State private var status: String = "Tap the button to generate icons" @State private var isGenerating = false - @State private var generatedIcons: [GeneratedIcon] = [] + @State private var generatedIcons: [GeneratedIconInfo] = [] - var body: some View { + // Development view: fixed sizes acceptable + private let previewSize: CGFloat = 200 + private let iconCornerRadiusRatio: CGFloat = 0.22 + + /// Creates a new icon generator view. + /// - Parameters: + /// - config: The app icon configuration to use for rendering. + /// - appName: The app name for display in instructions (e.g., "Blackjack", "Baccarat"). + public init(config: AppIconConfig, appName: String) { + self.config = config + self.appName = appName + } + + public var body: some View { NavigationStack { ScrollView { VStack(spacing: 24) { // Preview - AppIconView(config: .baccarat, size: 200) - .clipShape(.rect(cornerRadius: 200 * 0.22)) + AppIconView(config: config, size: previewSize) + .clipShape(.rect(cornerRadius: previewSize * iconCornerRadiusRatio)) .shadow(radius: 10) Text("App Icon Preview") @@ -99,7 +114,7 @@ struct IconGeneratorView: View { VStack(alignment: .leading, spacing: 8) { instructionRow(number: 1, text: "Open Files app on your device/simulator") - instructionRow(number: 2, text: "Navigate to: On My iPhone → Baccarat") + instructionRow(number: 2, text: "Navigate to: On My iPhone → \(appName)") instructionRow(number: 3, text: "Find the AppIcon-1024.png file") instructionRow(number: 4, text: "AirDrop or share to your Mac") instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon") @@ -149,7 +164,7 @@ struct IconGeneratorView: View { for (size, name) in sizes { // Render the icon - let view = AppIconView(config: .baccarat, size: size) + let view = AppIconView(config: config, size: size) let renderer = ImageRenderer(content: view) renderer.scale = 1.0 @@ -160,7 +175,7 @@ struct IconGeneratorView: View { do { try data.write(to: fileURL) - generatedIcons.append(GeneratedIcon(filename: filename, size: size)) + generatedIcons.append(GeneratedIconInfo(filename: filename, size: size)) } catch { status = "Error saving \(filename): \(error.localizedDescription)" } @@ -180,13 +195,18 @@ struct IconGeneratorView: View { } } -struct GeneratedIcon: Identifiable { - let id = UUID() - let filename: String - let size: CGFloat +/// Information about a generated icon file. +public struct GeneratedIconInfo: Identifiable, Sendable { + public let id = UUID() + public let filename: String + public let size: CGFloat + + public init(filename: String, size: CGFloat) { + self.filename = filename + self.size = size + } } #Preview { - IconGeneratorView() + IconGeneratorView(config: .example, appName: "Casino") } - diff --git a/CasinoKit/Sources/CasinoKit/Views/Branding/IconRenderer.swift b/CasinoKit/Sources/CasinoKit/Views/Branding/IconRenderer.swift index 59f5de7..fa7fe32 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Branding/IconRenderer.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Branding/IconRenderer.swift @@ -134,6 +134,6 @@ public struct IconExportView: View { } #Preview("Icon Export") { - IconExportView(config: .poker) + IconExportView(config: .example) } diff --git a/CasinoKit/Sources/CasinoKit/Views/Branding/LaunchScreenView.swift b/CasinoKit/Sources/CasinoKit/Views/Branding/LaunchScreenView.swift index 8c44758..7e1f20d 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Branding/LaunchScreenView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Branding/LaunchScreenView.swift @@ -38,15 +38,14 @@ public struct LaunchScreenConfig: Sendable { self.showLoadingIndicator = showLoadingIndicator } - // MARK: - Example Preset Configurations - // Game-specific presets should be defined in the respective apps as extensions. + // MARK: - Example Configuration (for previews only) - /// Poker game launch screen configuration (example preset). - public static let poker = LaunchScreenConfig( - title: "POKER", - tagline: "Texas Hold'em", - iconSymbols: ["suit.diamond.fill", "suit.club.fill"], - accentColor: .red + /// Example configuration for CasinoKit previews. + /// Apps should define their own configs in `BrandingConfig.swift`. + public static let example = LaunchScreenConfig( + title: "CASINO", + tagline: "Your Game Tagline", + iconSymbols: ["suit.diamond.fill", "suit.heart.fill"] ) } @@ -321,13 +320,13 @@ public struct StaticLaunchScreenView: View { } } -// MARK: - Preview +// MARK: - Previews -#Preview("Poker Launch") { - LaunchScreenView(config: .poker) +#Preview("Launch Screen") { + LaunchScreenView(config: .example) } #Preview("Static Launch") { - StaticLaunchScreenView(config: .poker) + StaticLaunchScreenView(config: .example) }