Compare commits

...

3 Commits

Author SHA1 Message Date
6ec98d8bcd updated launch screen, remove this if apple doesn't like it.
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
2025-12-31 14:50:52 -06:00
0c5f27e33e Signed-off-by: Matt Bruce <mbrucedogs@gmail.com> 2025-12-31 14:45:41 -06:00
1ecbbbf9ee Signed-off-by: Matt Bruce <mbrucedogs@gmail.com> 2025-12-31 14:20:36 -06:00
29 changed files with 648 additions and 738 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "AppIcon-1024.png",
"filename" : "AppIcon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

View File

@ -5,16 +5,15 @@
// Created by Matt Bruce on 12/16/25.
//
import SwiftUI
import CasinoKit
@main
struct BaccaratApp: App {
var body: some Scene {
WindowGroup {
// #if DEBUG
// IconGeneratorView()
// #else
ContentView() // your real app root
// #endif
AppLaunchView(config: .baccarat) {
ContentView()
}
}
}
}

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<device id="retina6_12" orientation="portrait" appearance="dark"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
@ -14,31 +13,8 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="♠️ ♥️" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iconsLabel">
<rect key="frame" x="146.66666666666666" y="376" width="100" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="iconHeight"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="40"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="BACCARAT" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="titleLabel">
<rect key="frame" x="97" y="434" width="199" height="41"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="34"/>
<color key="textColor" red="1" green="0.80000000000000004" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="0.058823529411764705" green="0.12156862745098039" blue="0.2196078431372549" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="iconsLabel" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="iconsCenterX"/>
<constraint firstItem="iconsLabel" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" constant="-50" id="iconsCenterY"/>
<constraint firstItem="titleLabel" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="titleCenterX"/>
<constraint firstItem="titleLabel" firstAttribute="top" secondItem="iconsLabel" secondAttribute="bottom" constant="8" id="titleTopToIcons"/>
</constraints>
<color key="backgroundColor" red="0.05" green="0.12" blue="0.22" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>

View File

@ -117,6 +117,7 @@
},
"%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -140,6 +141,7 @@
},
"%lldpx" : {
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -186,6 +188,7 @@
},
"• Add to Assets.xcassets/AppIcon" : {
"comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -208,6 +211,7 @@
}
},
"• Call IconRenderer.renderAppIcon(config: .baccarat)" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -230,6 +234,7 @@
}
},
"• Run the preview in Xcode" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -253,6 +258,7 @@
},
"• Save the resulting UIImage to files" : {
"comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -276,6 +282,7 @@
},
"• Screenshot the 1024px icon" : {
"comment" : "A step in the process of exporting app icons, describing how to take a screenshot of a 1024px icon.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -299,6 +306,7 @@
},
"• Use an online tool to generate all sizes" : {
"comment" : "A bullet point in the \"How to Export Icons\" section, describing how to use an online tool to generate all sizes for an app icon.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -528,6 +536,7 @@
},
"After generating:" : {
"comment" : "A heading for the instructions section of the icon generator view.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -551,6 +560,7 @@
},
"All Sizes" : {
"comment" : "A heading that describes the various sizes of the app icon.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -597,6 +607,7 @@
},
"Alternative: Use an online tool" : {
"comment" : "A section header that suggests using an online tool to generate app icon sizes.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -666,6 +677,7 @@
},
"App Icon" : {
"comment" : "A label displayed above the preview of the app icon.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -689,6 +701,7 @@
},
"App Icon Preview" : {
"comment" : "A header describing the preview of the app icon.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -1240,6 +1253,7 @@
},
"Blackjack" : {
"comment" : "The name of a blackjack game.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -1994,6 +2008,7 @@
},
"Generate & Save Icons" : {
"comment" : "A button label that triggers the generation of app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2017,6 +2032,7 @@
},
"Generated Icons:" : {
"comment" : "A label displayed above the list of generated icon filenames.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2040,6 +2056,7 @@
},
"Generating..." : {
"comment" : "A text that appears while generating icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2247,6 +2264,7 @@
},
"How to Export Icons" : {
"comment" : "A section header explaining how to export app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2337,6 +2355,7 @@
},
"Icon" : {
"comment" : "The title for the tab that displays the app icon preview.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2360,6 +2379,7 @@
},
"Icon Generator" : {
"comment" : "The title of the Icon Generator view.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2520,6 +2540,7 @@
},
"Launch" : {
"comment" : "A tab label for the launch screen preview.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2929,6 +2950,7 @@
},
"Option 1: Screenshot from Preview" : {
"comment" : "A description of one method for exporting app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2952,6 +2974,7 @@
},
"Option 2: Use IconRenderer in Code" : {
"comment" : "A description of how to use the `IconRenderer` in code to generate app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2975,6 +2998,7 @@
},
"Other Game Icons" : {
"comment" : "A label displayed above a section of the BrandingPreviewView",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -2998,6 +3022,7 @@
},
"Others" : {
"comment" : "The tab label for the section that previews branding assets for other games.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -3411,6 +3436,7 @@
},
"Poker" : {
"comment" : "The name of a poker game.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -3640,6 +3666,7 @@
},
"Roulette" : {
"comment" : "The name of a roulette game.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -4326,6 +4353,7 @@
},
"These show how the same pattern works for other games" : {
"comment" : "A description below the section of the view that previews icons for other games.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -4691,6 +4719,7 @@
},
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
"comment" : "A description of an alternative method for generating app icons.",
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {

View File

@ -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)
)
}

View 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"]
)
}

View File

@ -1,130 +0,0 @@
//
// BrandingPreviewView.swift
// Baccarat
//
// 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: 32) {
Text("App Icon")
.font(.largeTitle.bold())
AppIconView(config: .baccarat, size: 300)
.clipShape(.rect(cornerRadius: 300 * 0.22))
.shadow(radius: 20)
Text("All Sizes")
.font(.title2.bold())
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))
Text("\(size)px")
.font(.caption)
.foregroundStyle(.secondary)
}
}
}
instructionsSection
}
.padding()
}
.tabItem {
Label("Icon", systemImage: "app.fill")
}
// Launch Screen Preview
LaunchScreenView(config: .baccarat)
.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)
}
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: 12) {
Text("How to Export Icons")
.font(.headline)
VStack(alignment: .leading, spacing: 8) {
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: 8) {
Text("Option 2: Use IconRenderer in Code")
.font(.subheadline.bold())
Text("• Call IconRenderer.renderAppIcon(config: .baccarat)")
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(0.1))
.clipShape(.rect(cornerRadius: 12))
}
}
#Preview {
BrandingPreviewView()
}

View File

@ -350,6 +350,19 @@ struct SettingsView: View {
.padding(.horizontal)
.padding(.top, Design.Spacing.xSmall)
//
// DEBUG SECTION - Only visible in DEBUG builds
//
#if DEBUG
SheetSection(title: "DEBUG", icon: "ant.fill") {
BrandingDebugRows(
iconConfig: .baccarat,
launchConfig: .baccarat,
appName: "Baccarat"
)
}
#endif
// 11. App Version
Text(appVersionString)
.font(.system(size: Design.BaseFontSize.callout))

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@ -1,7 +1,7 @@
{
"images" : [
{
"filename" : "AppIcon-1024.png",
"filename" : "AppIcon.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"

View File

@ -18,12 +18,9 @@ struct BlackjackApp: App {
var body: some Scene {
WindowGroup {
// #if DEBUG
// IconGeneratorView()
// #else
ContentView() // your real app root
// #endif
AppLaunchView(config: .blackjack) {
ContentView()
}
}
}
}

View File

@ -1,49 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="24127" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="23094" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="dark"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="🃏 ♠️" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iconsLabel">
<rect key="frame" x="147.33333333333334" y="351" width="98.666666666666657" height="50"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="iconHeight"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="40"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="BLACKJACK" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="titleLabel">
<rect key="frame" x="81" y="409" width="215" height="41"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="34"/>
<color key="textColor" red="1" green="0.84313725490196079" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="0.050980392156862744" green="0.34901960784313724" blue="0.14901960784313725" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="iconsLabel" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="iconsCenterX"/>
<constraint firstItem="iconsLabel" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" constant="-50" id="iconsCenterY"/>
<constraint firstItem="titleLabel" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="titleCenterX"/>
<constraint firstItem="titleLabel" firstAttribute="top" secondItem="iconsLabel" secondAttribute="bottom" constant="8" id="titleTopToIcons"/>
</constraints>
<color key="backgroundColor" red="0.05" green="0.35" blue="0.15" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -119,6 +119,7 @@
},
"%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text of the item.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -143,6 +144,7 @@
},
"%lldpx" : {
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -191,6 +193,7 @@
},
"• Add to Assets.xcassets/AppIcon" : {
"comment" : "A bullet point describing how to add an app icon to Xcode's asset catalog.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -215,6 +218,7 @@
},
"• Call IconRenderer.renderAppIcon(config: .blackjack)" : {
"comment" : "A bullet point in the \"Option 2: Use IconRenderer in Code\" section of the BrandingPreviewView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -238,6 +242,7 @@
}
},
"• Run the preview in Xcode" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -261,6 +266,7 @@
},
"• Save the resulting UIImage to files" : {
"comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -284,6 +290,7 @@
}
},
"• Screenshot the 1024px icon" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -307,6 +314,7 @@
},
"• Use an online tool to generate all sizes" : {
"comment" : "A step in the process of exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -828,6 +836,7 @@
},
"After generating:" : {
"comment" : "A heading for instructions on how to use the IconGeneratorView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -852,6 +861,7 @@
},
"All Sizes" : {
"comment" : "A heading that describes the various sizes of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -943,6 +953,7 @@
},
"Alternative: Use an online tool" : {
"comment" : "A section header that suggests using an online tool to generate app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -1013,6 +1024,7 @@
},
"App Icon" : {
"comment" : "A label displayed above the preview of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -1037,6 +1049,7 @@
},
"App Icon Preview" : {
"comment" : "A title for the preview of the app icon.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -1105,6 +1118,7 @@
},
"Baccarat" : {
"comment" : "The name of a casino game.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -2385,9 +2399,6 @@
}
}
}
},
"Deal Splittable Pair (8s)" : {
},
"DEALER" : {
"localizations" : {
@ -3589,6 +3600,7 @@
},
"Generate & Save Icons" : {
"comment" : "A button label that triggers icon generation and saving.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -3613,6 +3625,7 @@
},
"Generated Icons:" : {
"comment" : "A label describing the list of icons that have been successfully generated.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -3637,6 +3650,7 @@
},
"Generating..." : {
"comment" : "A label indicating that the app is currently generating icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -3955,6 +3969,7 @@
},
"How to Export Icons" : {
"comment" : "A section header explaining how to export app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -4045,6 +4060,7 @@
},
"Icon" : {
"comment" : "The label for the tab item representing the app icon preview.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -4069,6 +4085,7 @@
},
"Icon Generator" : {
"comment" : "The title of the Icon Generator view.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -4434,6 +4451,7 @@
},
"Launch" : {
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -5093,6 +5111,7 @@
},
"Option 1: Screenshot from Preview" : {
"comment" : "A description of one method for exporting app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -5117,6 +5136,7 @@
},
"Option 2: Use IconRenderer in Code" : {
"comment" : "A subheading within the instructions section of the BrandingPreviewView.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -5163,6 +5183,7 @@
},
"Other Game Icons" : {
"comment" : "A label displayed above a section of the BrandingPreviewView that shows icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -5187,6 +5208,7 @@
},
"Others" : {
"comment" : "A tab label for the section displaying icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -5464,6 +5486,7 @@
}
},
"Poker" : {
"extractionState" : "stale",
"localizations" : {
"en" : {
"stringUnit" : {
@ -5781,6 +5804,7 @@
},
"Roulette" : {
"comment" : "The name of a roulette card.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -7195,6 +7219,7 @@
},
"These show how the same pattern works for other games" : {
"comment" : "A description below the section of the view that previews icons for other games.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
@ -7471,6 +7496,7 @@
},
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
"comment" : "A description of an alternative method for generating app icons.",
"extractionState" : "stale",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {

View File

@ -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"
)
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -435,28 +435,27 @@ struct SettingsView: View {
// calls the corresponding debug function in GameState.
//
#if DEBUG
if let state = gameState {
SheetSection(title: "DEBUG", icon: "ant.fill") {
SheetSection(title: "DEBUG", icon: "ant.fill") {
// Game-specific debug actions
if let state = gameState {
// Split Testing - deals a pair of 8s
Button {
DebugActionRow(
title: "Deal Splittable Pair (8s)",
icon: "rectangle.split.2x1"
) {
triggerDebugDeal(state: state)
} label: {
HStack {
Text("Deal Splittable Pair (8s)")
.font(.system(size: Design.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: "rectangle.split.2x1")
.font(.system(size: Design.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
}
// Add new debug buttons here:
// Divider().background(Color.orange.opacity(Design.Opacity.hint))
// Button { triggerDebugBlackjack(state: state) } label: { ... }
Divider()
.background(Color.orange.opacity(Design.Opacity.hint))
}
// Branding tools (from CasinoKit)
BrandingDebugRows(
iconConfig: .blackjack,
launchConfig: .blackjack,
appName: "Blackjack"
)
}
#endif

View File

@ -72,12 +72,17 @@
// - SelectableRow (card-like selectable picker row)
// - SelectionIndicator (checkmark circle)
// - BadgePill (capsule badge for values)
// - DebugNavigationRow (debug row with navigation)
// - DebugActionRow (debug row with action)
// - BrandingDebugRows (icon generator + branding preview rows)
// MARK: - Branding
// - 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)

View File

@ -93,6 +93,10 @@
}
}
},
"%lld." : {
"comment" : "A numbered list item with a callout number and accompanying text. The first argument is the number of the item. The second argument is the text describing the item.",
"isCommentAutoGenerated" : true
},
"%lld%%" : {
"comment" : "A text displaying the current volume percentage. The argument is a value between 0.0 (no volume) and 1.0 (full volume).",
"isCommentAutoGenerated" : true,
@ -141,6 +145,10 @@
}
}
},
"%lldpx" : {
"comment" : "A text label displaying the size of an app icon. The argument is the size of the icon in pixels.",
"isCommentAutoGenerated" : true
},
"•" : {
"comment" : "A bullet point indicator.",
"isCommentAutoGenerated" : true,
@ -316,6 +324,10 @@
}
}
},
"1024 × 1024px" : {
"comment" : "A description of the size of the app icon.",
"isCommentAutoGenerated" : true
},
"Ace" : {
"localizations" : {
"en" : {
@ -365,6 +377,10 @@
}
}
},
"After generating:" : {
"comment" : "A heading describing the steps to take after generating app icons.",
"isCommentAutoGenerated" : true
},
"All game data is stored:" : {
"localizations" : {
"en" : {
@ -387,6 +403,10 @@
}
}
},
"App Icon" : {
"comment" : "The title of the section displaying the app icon preview.",
"isCommentAutoGenerated" : true
},
"App Icon Preview" : {
"comment" : "A title for the preview section of the icon export view.",
"isCommentAutoGenerated" : true,
@ -1099,6 +1119,14 @@
}
}
},
"Generate & Save Icon" : {
"comment" : "A button label that triggers icon generation and saving.",
"isCommentAutoGenerated" : true
},
"Generating..." : {
"comment" : "A button label indicating that the app is currently generating icons.",
"isCommentAutoGenerated" : true
},
"Global" : {
"comment" : "Title for the \"Global\" tab in the statistics view.",
"isCommentAutoGenerated" : true
@ -1202,6 +1230,14 @@
}
}
},
"Icon" : {
"comment" : "The label for the tab that displays the app icon preview.",
"isCommentAutoGenerated" : true
},
"Icon Generator" : {
"comment" : "The title of the icon generator view.",
"isCommentAutoGenerated" : true
},
"If you choose to enable iCloud sync:" : {
"localizations" : {
"en" : {
@ -1400,6 +1436,10 @@
}
}
},
"Launch" : {
"comment" : "A tab label for the launch screen preview.",
"isCommentAutoGenerated" : true
},
"Locally on your device using iOS standard storage" : {
"localizations" : {
"en" : {
@ -1578,6 +1618,10 @@
},
"No Session History" : {
},
"Note: iOS uses a single 1024px icon" : {
"comment" : "A note explaining that iOS uses a single 1024px icon.",
"isCommentAutoGenerated" : true
},
"Our apps do not integrate with third-party services that collect user data. We do not share any information with third parties." : {
"localizations" : {
@ -2106,6 +2150,10 @@
}
}
},
"To Export" : {
"comment" : "A section header explaining how to export branding assets.",
"isCommentAutoGenerated" : true
},
"TOTAL" : {
"comment" : "A label displayed alongside the total winnings in the result banner.",
"isCommentAutoGenerated" : true
@ -2148,6 +2196,10 @@
}
}
},
"Use the Icon Generator in Settings → DEBUG to save the 1024px icon to the Files app, then add it to Xcode's Assets.xcassets/AppIcon." : {
"comment" : "Instructions for exporting an app icon.",
"isCommentAutoGenerated" : true
},
"VIP" : {
"localizations" : {
"en" : {
@ -2344,6 +2396,10 @@
"comment" : "A label for the worst session amount in the statistics view.",
"isCommentAutoGenerated" : true
},
"Xcode automatically generates all required sizes from the 1024px source." : {
"comment" : "A footnote explaining that Xcode generates all app icon sizes from the 1024px source image.",
"isCommentAutoGenerated" : true
},
"You can disable iCloud sync at any time in the app settings" : {
"comment" : "Text in the Privacy Policy View explaining how to disable iCloud sync in the app settings.",
"isCommentAutoGenerated" : true,

View File

@ -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,33 @@ public struct AppIconView: View {
self.size = size
}
private var cornerRadius: CGFloat { size * 0.22 }
// Size calculations
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 }
/// Dynamic title size based on text length.
/// Shorter titles get larger fonts, longer titles shrink to fit within the border.
private var titleSize: CGFloat {
let baseSize = size * 0.12
let length = config.title.count
// Scale factor: full size for 6 chars, progressively smaller for longer
let scaleFactor: CGFloat = switch length {
case ...6: 1.0 // "CASINO", "POKER"
case 7: 0.95 //
case 8: 0.85 // "BACCARAT"
case 9: 0.75 // "BLACKJACK"
default: 0.65 // Very long titles
}
return baseSize * scaleFactor
}
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,21 +96,6 @@ public struct AppIconView: View {
DiamondPatternOverlay(size: size)
.opacity(0.08)
// Gold border
RoundedRectangle(cornerRadius: cornerRadius)
.strokeBorder(
LinearGradient(
colors: [
config.accentColor,
config.accentColor.opacity(0.6),
config.accentColor
],
startPoint: .topLeading,
endPoint: .bottomTrailing
),
lineWidth: borderWidth
)
// Content
VStack(spacing: size * 0.03) {
// Icon symbol
@ -179,24 +175,56 @@ 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("Title Scaling") {
let shortTitle = AppIconConfig(title: "POKER", iconSymbol: "suit.diamond.fill")
let mediumTitle = AppIconConfig(title: "BACCARAT", iconSymbol: "suit.spade.fill")
let longTitle = 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)
)
return HStack(spacing: 20) {
VStack {
AppIconView(config: shortTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("6 chars").font(.caption)
}
VStack {
AppIconView(config: mediumTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("8 chars").font(.caption)
}
VStack {
AppIconView(config: longTitle, size: 180)
.clipShape(.rect(cornerRadius: 180 * 0.22))
Text("9 chars").font(.caption)
}
}
.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)
}
}
}
.padding()
.background(Color.gray)

View File

@ -0,0 +1,64 @@
//
// AppLaunchView.swift
// CasinoKit
//
// A wrapper view that shows an animated launch screen before transitioning
// to the main app content. Use this to create seamless animated launches.
//
import SwiftUI
/// A wrapper that shows an animated launch screen before the main content.
///
/// Usage:
/// ```swift
/// AppLaunchView(config: .baccarat) {
/// ContentView()
/// }
/// ```
public struct AppLaunchView<Content: View>: View {
let config: LaunchScreenConfig
let content: () -> Content
@State private var showLaunchScreen = true
/// Creates a launch wrapper.
/// - Parameters:
/// - config: The launch screen configuration.
/// - content: The main app content to show after the launch animation.
public init(
config: LaunchScreenConfig,
@ViewBuilder content: @escaping () -> Content
) {
self.config = config
self.content = content
}
public var body: some View {
ZStack {
// Main content (always rendered underneath)
content()
// Launch screen overlay
if showLaunchScreen {
LaunchScreenView(config: config)
.transition(.opacity)
.zIndex(1)
}
}
.task {
// Wait for launch animation to complete, then fade out
try? await Task.sleep(for: .seconds(2.0))
withAnimation(.easeOut(duration: 0.5)) {
showLaunchScreen = false
}
}
}
}
#Preview {
AppLaunchView(config: .example) {
Text("Main App Content")
.font(.largeTitle)
}
}

View File

@ -0,0 +1,91 @@
//
// BrandingPreviewView.swift
// CasinoKit
//
// Development view for previewing and exporting app icons and launch screens.
// Access this during development to generate icon assets.
//
import SwiftUI
/// Preview view for app branding assets.
/// Use this during development to preview icons and launch screens.
public struct BrandingPreviewView: View {
let iconConfig: AppIconConfig
let launchConfig: LaunchScreenConfig
let appName: String
// Development view: fixed sizes acceptable
private let largePreviewSize: CGFloat = 300
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.
public init(
iconConfig: AppIconConfig,
launchConfig: LaunchScreenConfig,
appName: String
) {
self.iconConfig = iconConfig
self.launchConfig = launchConfig
self.appName = appName
}
public var body: some View {
TabView {
// App Icon Preview
ScrollView {
VStack(spacing: 32) {
Text("App Icon")
.font(.largeTitle.bold())
AppIconView(config: iconConfig, size: largePreviewSize)
.clipShape(.rect(cornerRadius: largePreviewSize * iconCornerRadiusRatio))
.shadow(radius: 20)
Text("1024 × 1024px")
.font(.caption)
.foregroundStyle(.secondary)
instructionsSection
}
.padding()
}
.tabItem {
Label("Icon", systemImage: "app.fill")
}
// Launch Screen Preview
LaunchScreenView(config: launchConfig)
.tabItem {
Label("Launch", systemImage: "rectangle.portrait.fill")
}
}
}
private var instructionsSection: some View {
VStack(alignment: .leading, spacing: 12) {
Text("To Export")
.font(.headline)
Text("Use the Icon Generator in Settings → DEBUG to save the 1024px icon to the Files app, then add it to Xcode's Assets.xcassets/AppIcon.")
.font(.callout)
}
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.background(Color.gray.opacity(0.1))
.clipShape(.rect(cornerRadius: 12))
}
}
#Preview {
BrandingPreviewView(
iconConfig: .example,
launchConfig: .example,
appName: "Casino"
)
}

View File

@ -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 {
@State private var status: String = "Tap the button to generate icons"
@State private var isGenerating = false
@State private var generatedIcons: [GeneratedIcon] = []
/// After running, find the icons in Files app On My iPhone [App Name]
public struct IconGeneratorView: View {
let config: AppIconConfig
let appName: String
var body: some View {
@State private var status: String = "Tap the button to generate the icon"
@State private var isGenerating = false
@State private var generatedIcon: GeneratedIconInfo?
// 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")
@ -31,7 +46,7 @@ struct IconGeneratorView: View {
// Generate button
Button {
Task {
await generateIcons()
await generateIcon()
}
} label: {
HStack {
@ -39,7 +54,7 @@ struct IconGeneratorView: View {
ProgressView()
.tint(.white)
}
Text(isGenerating ? "Generating..." : "Generate & Save Icons")
Text(isGenerating ? "Generating..." : "Generate & Save Icon")
}
.font(.headline)
.foregroundStyle(.white)
@ -58,24 +73,17 @@ struct IconGeneratorView: View {
.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)
}
}
// Generated icon confirmation
if let icon = generatedIcon {
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
Text(icon.filename)
.font(.callout.monospaced())
Spacer()
Text("\(Int(icon.size))px")
.font(.callout)
.foregroundStyle(.secondary)
}
.padding()
.background(Color.green.opacity(0.1))
@ -99,17 +107,17 @@ 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: 3, text: "Find the AppIcon-1024.png file")
instructionRow(number: 2, text: "Navigate to: On My iPhone → \(appName)")
instructionRow(number: 3, text: "Find AppIcon.png (1024×1024)")
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")
Text("Note: iOS uses a single 1024px icon")
.font(.subheadline.bold())
Text("Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically.")
Text("Xcode automatically generates all required sizes from the 1024px source.")
.font(.caption)
.foregroundStyle(.secondary)
}
@ -130,63 +138,50 @@ struct IconGeneratorView: View {
}
@MainActor
private func generateIcons() async {
private func generateIcon() 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")
]
generatedIcon = nil
status = "Generating icon..."
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
for (size, name) in sizes {
// Render the icon
let view = AppIconView(config: .baccarat, 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))
}
// Render the 1024px icon (the only size needed for modern iOS)
let view = AppIconView(config: config, size: 1024)
let renderer = ImageRenderer(content: view)
renderer.scale = 1.0
if generatedIcons.count == sizes.count {
status = "✅ All icons saved to Documents folder!\nOpen Files app to find them."
if let uiImage = renderer.uiImage,
let data = uiImage.pngData() {
let filename = "AppIcon.png"
let fileURL = documentsPath.appending(path: filename)
do {
try data.write(to: fileURL)
generatedIcon = GeneratedIconInfo(filename: filename, size: 1024)
status = "✅ Icon saved to Documents folder!\nOpen Files app to find it."
} catch {
status = "Error saving icon: \(error.localizedDescription)"
}
} else {
status = "⚠️ Some icons failed to generate"
status = "⚠️ Failed to render icon"
}
isGenerating = false
}
}
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")
}

View File

@ -134,6 +134,6 @@ public struct IconExportView: View {
}
#Preview("Icon Export") {
IconExportView(config: .poker)
IconExportView(config: .example)
}

View File

@ -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)
}

View File

@ -0,0 +1,130 @@
//
// DebugSection.swift
// CasinoKit
//
// Reusable debug section for settings views.
// Only visible in DEBUG builds.
//
import SwiftUI
/// A debug row that navigates to a destination view.
public struct DebugNavigationRow<Destination: View>: View {
let title: String
let icon: String
let destination: () -> Destination
public init(
title: String,
icon: String,
@ViewBuilder destination: @escaping () -> Destination
) {
self.title = title
self.icon = icon
self.destination = destination
}
public var body: some View {
NavigationLink {
destination()
} label: {
HStack {
Text(title)
.font(.system(size: CasinoDesign.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: icon)
.font(.system(size: CasinoDesign.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
}
}
}
/// A debug row that performs an action.
public struct DebugActionRow: View {
let title: String
let icon: String
let action: () -> Void
public init(
title: String,
icon: String,
action: @escaping () -> Void
) {
self.title = title
self.icon = icon
self.action = action
}
public var body: some View {
Button(action: action) {
HStack {
Text(title)
.font(.system(size: CasinoDesign.BaseFontSize.body, weight: .medium))
.foregroundStyle(.orange)
Spacer()
Image(systemName: icon)
.font(.system(size: CasinoDesign.BaseFontSize.large))
.foregroundStyle(.orange)
}
.frame(minHeight: CasinoDesign.Size.actionRowMinHeight)
}
}
}
/// Branding debug rows for icon generation and preview.
/// Use these in your app's debug section.
public struct BrandingDebugRows: View {
let iconConfig: AppIconConfig
let launchConfig: LaunchScreenConfig
let appName: String
public init(
iconConfig: AppIconConfig,
launchConfig: LaunchScreenConfig,
appName: String
) {
self.iconConfig = iconConfig
self.launchConfig = launchConfig
self.appName = appName
}
public var body: some View {
DebugNavigationRow(
title: "Icon Generator",
icon: "app.badge.fill"
) {
IconGeneratorView(config: iconConfig, appName: appName)
}
Divider()
.background(Color.orange.opacity(0.3))
DebugNavigationRow(
title: "Branding Preview",
icon: "paintpalette.fill"
) {
BrandingPreviewView(
iconConfig: iconConfig,
launchConfig: launchConfig,
appName: appName
)
}
}
}
#Preview {
NavigationStack {
List {
Section("Debug") {
BrandingDebugRows(
iconConfig: .example,
launchConfig: .example,
appName: "Casino"
)
}
}
}
}