Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
ed1dabfe18
commit
1ecbbbf9ee
@ -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)
|
||||
)
|
||||
}
|
||||
|
||||
30
Baccarat/Baccarat/Theme/BrandingConfig.swift
Normal file
30
Baccarat/Baccarat/Theme/BrandingConfig.swift
Normal file
@ -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"]
|
||||
)
|
||||
}
|
||||
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 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)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("All Icons") {
|
||||
HStack(spacing: 20) {
|
||||
AppIconView(config: .poker, size: 200)
|
||||
AppIconView(config: .roulette, size: 200)
|
||||
}
|
||||
.padding()
|
||||
.background(Color.gray)
|
||||
|
||||
@ -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,37 +77,26 @@ struct BrandingPreviewView: View {
|
||||
}
|
||||
|
||||
// Launch Screen Preview
|
||||
LaunchScreenView(config: .baccarat)
|
||||
LaunchScreenView(config: launchConfig)
|
||||
.tabItem {
|
||||
Label("Launch", systemImage: "rectangle.portrait.fill")
|
||||
}
|
||||
|
||||
// Other Games Preview
|
||||
// Other Games Preview (if provided)
|
||||
if !otherGames.isEmpty {
|
||||
ScrollView {
|
||||
VStack(spacing: 32) {
|
||||
Text("Other Game Icons")
|
||||
.font(.largeTitle.bold())
|
||||
|
||||
HStack(spacing: 20) {
|
||||
LazyVGrid(columns: [GridItem(.adaptive(minimum: 160))], spacing: 20) {
|
||||
ForEach(otherGames, id: \.name) { game in
|
||||
VStack {
|
||||
AppIconView(config: .blackjack, size: 150)
|
||||
.clipShape(.rect(cornerRadius: 150 * 0.22))
|
||||
Text("Blackjack")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +111,7 @@ struct BrandingPreviewView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var instructionsSection: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -134,6 +134,6 @@ public struct IconExportView: View {
|
||||
}
|
||||
|
||||
#Preview("Icon Export") {
|
||||
IconExportView(config: .poker)
|
||||
IconExportView(config: .example)
|
||||
}
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user