Compare commits
No commits in common. "6ec98d8bcde0c98712f4c467cab549f679643d1b" and "ed1dabfe180cc8de0a56c9732fe0b7c6f44c84b1" have entirely different histories.
6ec98d8bcd
...
ed1dabfe18
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "AppIcon.png",
|
"filename" : "AppIcon-1024.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
|||||||
@ -5,15 +5,16 @@
|
|||||||
// Created by Matt Bruce on 12/16/25.
|
// Created by Matt Bruce on 12/16/25.
|
||||||
//
|
//
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import CasinoKit
|
|
||||||
|
|
||||||
@main
|
@main
|
||||||
struct BaccaratApp: App {
|
struct BaccaratApp: App {
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
AppLaunchView(config: .baccarat) {
|
// #if DEBUG
|
||||||
ContentView()
|
// IconGeneratorView()
|
||||||
}
|
// #else
|
||||||
|
ContentView() // your real app root
|
||||||
|
// #endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<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"/>
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<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"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -13,8 +14,31 @@
|
|||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<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"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" red="0.05" green="0.12" blue="0.22" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<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>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
|
|||||||
@ -117,7 +117,6 @@
|
|||||||
},
|
},
|
||||||
"%lld." : {
|
"%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.",
|
"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" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -141,7 +140,6 @@
|
|||||||
},
|
},
|
||||||
"%lldpx" : {
|
"%lldpx" : {
|
||||||
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
|
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -188,7 +186,6 @@
|
|||||||
},
|
},
|
||||||
"• Add to Assets.xcassets/AppIcon" : {
|
"• Add to Assets.xcassets/AppIcon" : {
|
||||||
"comment" : "A step in the process of exporting app icons.",
|
"comment" : "A step in the process of exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -211,7 +208,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"• Call IconRenderer.renderAppIcon(config: .baccarat)" : {
|
"• Call IconRenderer.renderAppIcon(config: .baccarat)" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -234,7 +230,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"• Run the preview in Xcode" : {
|
"• Run the preview in Xcode" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -258,7 +253,6 @@
|
|||||||
},
|
},
|
||||||
"• Save the resulting UIImage to files" : {
|
"• Save the resulting UIImage to files" : {
|
||||||
"comment" : "A step in the process of exporting app icons.",
|
"comment" : "A step in the process of exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -282,7 +276,6 @@
|
|||||||
},
|
},
|
||||||
"• Screenshot the 1024px icon" : {
|
"• Screenshot the 1024px icon" : {
|
||||||
"comment" : "A step in the process of exporting app icons, describing how to take a screenshot of a 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" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -306,7 +299,6 @@
|
|||||||
},
|
},
|
||||||
"• Use an online tool to generate all sizes" : {
|
"• 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.",
|
"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" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -536,7 +528,6 @@
|
|||||||
},
|
},
|
||||||
"After generating:" : {
|
"After generating:" : {
|
||||||
"comment" : "A heading for the instructions section of the icon generator view.",
|
"comment" : "A heading for the instructions section of the icon generator view.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -560,7 +551,6 @@
|
|||||||
},
|
},
|
||||||
"All Sizes" : {
|
"All Sizes" : {
|
||||||
"comment" : "A heading that describes the various sizes of the app icon.",
|
"comment" : "A heading that describes the various sizes of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -607,7 +597,6 @@
|
|||||||
},
|
},
|
||||||
"Alternative: Use an online tool" : {
|
"Alternative: Use an online tool" : {
|
||||||
"comment" : "A section header that suggests using an online tool to generate app icon sizes.",
|
"comment" : "A section header that suggests using an online tool to generate app icon sizes.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -677,7 +666,6 @@
|
|||||||
},
|
},
|
||||||
"App Icon" : {
|
"App Icon" : {
|
||||||
"comment" : "A label displayed above the preview of the app icon.",
|
"comment" : "A label displayed above the preview of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -701,7 +689,6 @@
|
|||||||
},
|
},
|
||||||
"App Icon Preview" : {
|
"App Icon Preview" : {
|
||||||
"comment" : "A header describing the preview of the app icon.",
|
"comment" : "A header describing the preview of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -1253,7 +1240,6 @@
|
|||||||
},
|
},
|
||||||
"Blackjack" : {
|
"Blackjack" : {
|
||||||
"comment" : "The name of a blackjack game.",
|
"comment" : "The name of a blackjack game.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2008,7 +1994,6 @@
|
|||||||
},
|
},
|
||||||
"Generate & Save Icons" : {
|
"Generate & Save Icons" : {
|
||||||
"comment" : "A button label that triggers the generation of app icons.",
|
"comment" : "A button label that triggers the generation of app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2032,7 +2017,6 @@
|
|||||||
},
|
},
|
||||||
"Generated Icons:" : {
|
"Generated Icons:" : {
|
||||||
"comment" : "A label displayed above the list of generated icon filenames.",
|
"comment" : "A label displayed above the list of generated icon filenames.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2056,7 +2040,6 @@
|
|||||||
},
|
},
|
||||||
"Generating..." : {
|
"Generating..." : {
|
||||||
"comment" : "A text that appears while generating icons.",
|
"comment" : "A text that appears while generating icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2264,7 +2247,6 @@
|
|||||||
},
|
},
|
||||||
"How to Export Icons" : {
|
"How to Export Icons" : {
|
||||||
"comment" : "A section header explaining how to export app icons.",
|
"comment" : "A section header explaining how to export app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2355,7 +2337,6 @@
|
|||||||
},
|
},
|
||||||
"Icon" : {
|
"Icon" : {
|
||||||
"comment" : "The title for the tab that displays the app icon preview.",
|
"comment" : "The title for the tab that displays the app icon preview.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2379,7 +2360,6 @@
|
|||||||
},
|
},
|
||||||
"Icon Generator" : {
|
"Icon Generator" : {
|
||||||
"comment" : "The title of the Icon Generator view.",
|
"comment" : "The title of the Icon Generator view.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2540,7 +2520,6 @@
|
|||||||
},
|
},
|
||||||
"Launch" : {
|
"Launch" : {
|
||||||
"comment" : "A tab label for the launch screen preview.",
|
"comment" : "A tab label for the launch screen preview.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2950,7 +2929,6 @@
|
|||||||
},
|
},
|
||||||
"Option 1: Screenshot from Preview" : {
|
"Option 1: Screenshot from Preview" : {
|
||||||
"comment" : "A description of one method for exporting app icons.",
|
"comment" : "A description of one method for exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2974,7 +2952,6 @@
|
|||||||
},
|
},
|
||||||
"Option 2: Use IconRenderer in Code" : {
|
"Option 2: Use IconRenderer in Code" : {
|
||||||
"comment" : "A description of how to use the `IconRenderer` in code to generate app icons.",
|
"comment" : "A description of how to use the `IconRenderer` in code to generate app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -2998,7 +2975,6 @@
|
|||||||
},
|
},
|
||||||
"Other Game Icons" : {
|
"Other Game Icons" : {
|
||||||
"comment" : "A label displayed above a section of the BrandingPreviewView",
|
"comment" : "A label displayed above a section of the BrandingPreviewView",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -3022,7 +2998,6 @@
|
|||||||
},
|
},
|
||||||
"Others" : {
|
"Others" : {
|
||||||
"comment" : "The tab label for the section that previews branding assets for other games.",
|
"comment" : "The tab label for the section that previews branding assets for other games.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -3436,7 +3411,6 @@
|
|||||||
},
|
},
|
||||||
"Poker" : {
|
"Poker" : {
|
||||||
"comment" : "The name of a poker game.",
|
"comment" : "The name of a poker game.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -3666,7 +3640,6 @@
|
|||||||
},
|
},
|
||||||
"Roulette" : {
|
"Roulette" : {
|
||||||
"comment" : "The name of a roulette game.",
|
"comment" : "The name of a roulette game.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -4353,7 +4326,6 @@
|
|||||||
},
|
},
|
||||||
"These show how the same pattern works for other games" : {
|
"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.",
|
"comment" : "A description below the section of the view that previews icons for other games.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -4719,7 +4691,6 @@
|
|||||||
},
|
},
|
||||||
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
|
"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.",
|
"comment" : "A description of an alternative method for generating app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
|
|||||||
40
Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift
Normal file
40
Baccarat/Baccarat/Theme/BrandingConfig+Baccarat.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// 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)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// 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"]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
130
Baccarat/Baccarat/Views/Development/BrandingPreviewView.swift
Normal file
130
Baccarat/Baccarat/Views/Development/BrandingPreviewView.swift
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
@ -1,43 +1,28 @@
|
|||||||
//
|
//
|
||||||
// IconGeneratorView.swift
|
// IconGeneratorView.swift
|
||||||
// CasinoKit
|
// Baccarat
|
||||||
//
|
//
|
||||||
// Development tool to generate and export app icon images.
|
// Development tool to generate and export app icon images.
|
||||||
// Run this view, tap the button, then find the icons in the Files app.
|
// Run this view, tap the button, then find the icons in the Files app.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
import CasinoKit
|
||||||
|
|
||||||
/// A development view that generates and saves app icon images.
|
/// A development view that generates and saves app icon images.
|
||||||
/// After running, find the icons in Files app → On My iPhone → [App Name]
|
/// After running, find the icons in Files app → On My iPhone → Baccarat
|
||||||
public struct IconGeneratorView: View {
|
struct IconGeneratorView: View {
|
||||||
let config: AppIconConfig
|
@State private var status: String = "Tap the button to generate icons"
|
||||||
let appName: String
|
|
||||||
|
|
||||||
@State private var status: String = "Tap the button to generate the icon"
|
|
||||||
@State private var isGenerating = false
|
@State private var isGenerating = false
|
||||||
@State private var generatedIcon: GeneratedIconInfo?
|
@State private var generatedIcons: [GeneratedIcon] = []
|
||||||
|
|
||||||
// Development view: fixed sizes acceptable
|
var body: some View {
|
||||||
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 {
|
NavigationStack {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
VStack(spacing: 24) {
|
VStack(spacing: 24) {
|
||||||
// Preview
|
// Preview
|
||||||
AppIconView(config: config, size: previewSize)
|
AppIconView(config: .baccarat, size: 200)
|
||||||
.clipShape(.rect(cornerRadius: previewSize * iconCornerRadiusRatio))
|
.clipShape(.rect(cornerRadius: 200 * 0.22))
|
||||||
.shadow(radius: 10)
|
.shadow(radius: 10)
|
||||||
|
|
||||||
Text("App Icon Preview")
|
Text("App Icon Preview")
|
||||||
@ -46,7 +31,7 @@ public struct IconGeneratorView: View {
|
|||||||
// Generate button
|
// Generate button
|
||||||
Button {
|
Button {
|
||||||
Task {
|
Task {
|
||||||
await generateIcon()
|
await generateIcons()
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
@ -54,7 +39,7 @@ public struct IconGeneratorView: View {
|
|||||||
ProgressView()
|
ProgressView()
|
||||||
.tint(.white)
|
.tint(.white)
|
||||||
}
|
}
|
||||||
Text(isGenerating ? "Generating..." : "Generate & Save Icon")
|
Text(isGenerating ? "Generating..." : "Generate & Save Icons")
|
||||||
}
|
}
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundStyle(.white)
|
.foregroundStyle(.white)
|
||||||
@ -73,18 +58,25 @@ public struct IconGeneratorView: View {
|
|||||||
.multilineTextAlignment(.center)
|
.multilineTextAlignment(.center)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
|
|
||||||
// Generated icon confirmation
|
// Generated icons
|
||||||
if let icon = generatedIcon {
|
if !generatedIcons.isEmpty {
|
||||||
|
VStack(alignment: .leading, spacing: 12) {
|
||||||
|
Text("Generated Icons:")
|
||||||
|
.font(.headline)
|
||||||
|
|
||||||
|
ForEach(generatedIcons) { icon in
|
||||||
HStack {
|
HStack {
|
||||||
Image(systemName: "checkmark.circle.fill")
|
Image(systemName: "checkmark.circle.fill")
|
||||||
.foregroundStyle(.green)
|
.foregroundStyle(.green)
|
||||||
Text(icon.filename)
|
Text(icon.filename)
|
||||||
.font(.callout.monospaced())
|
.font(.caption.monospaced())
|
||||||
Spacer()
|
Spacer()
|
||||||
Text("\(Int(icon.size))px")
|
Text("\(Int(icon.size))px")
|
||||||
.font(.callout)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color.green.opacity(0.1))
|
.background(Color.green.opacity(0.1))
|
||||||
.clipShape(.rect(cornerRadius: 12))
|
.clipShape(.rect(cornerRadius: 12))
|
||||||
@ -107,17 +99,17 @@ public struct IconGeneratorView: View {
|
|||||||
|
|
||||||
VStack(alignment: .leading, spacing: 8) {
|
VStack(alignment: .leading, spacing: 8) {
|
||||||
instructionRow(number: 1, text: "Open Files app on your device/simulator")
|
instructionRow(number: 1, text: "Open Files app on your device/simulator")
|
||||||
instructionRow(number: 2, text: "Navigate to: On My iPhone → \(appName)")
|
instructionRow(number: 2, text: "Navigate to: On My iPhone → Baccarat")
|
||||||
instructionRow(number: 3, text: "Find AppIcon.png (1024×1024)")
|
instructionRow(number: 3, text: "Find the AppIcon-1024.png file")
|
||||||
instructionRow(number: 4, text: "AirDrop or share to your Mac")
|
instructionRow(number: 4, text: "AirDrop or share to your Mac")
|
||||||
instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon")
|
instructionRow(number: 5, text: "Drag into Xcode's Assets.xcassets/AppIcon")
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
|
|
||||||
Text("Note: iOS uses a single 1024px icon")
|
Text("Alternative: Use an online tool")
|
||||||
.font(.subheadline.bold())
|
.font(.subheadline.bold())
|
||||||
Text("Xcode automatically generates all required sizes from the 1024px source.")
|
Text("Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically.")
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
@ -138,50 +130,63 @@ public struct IconGeneratorView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func generateIcon() async {
|
private func generateIcons() async {
|
||||||
isGenerating = true
|
isGenerating = true
|
||||||
generatedIcon = nil
|
generatedIcons = []
|
||||||
status = "Generating icon..."
|
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]
|
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
|
||||||
|
|
||||||
// Render the 1024px icon (the only size needed for modern iOS)
|
for (size, name) in sizes {
|
||||||
let view = AppIconView(config: config, size: 1024)
|
// Render the icon
|
||||||
|
let view = AppIconView(config: .baccarat, size: size)
|
||||||
let renderer = ImageRenderer(content: view)
|
let renderer = ImageRenderer(content: view)
|
||||||
renderer.scale = 1.0
|
renderer.scale = 1.0
|
||||||
|
|
||||||
if let uiImage = renderer.uiImage,
|
if let uiImage = renderer.uiImage,
|
||||||
let data = uiImage.pngData() {
|
let data = uiImage.pngData() {
|
||||||
let filename = "AppIcon.png"
|
let filename = "\(name).png"
|
||||||
let fileURL = documentsPath.appending(path: filename)
|
let fileURL = documentsPath.appending(path: filename)
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try data.write(to: fileURL)
|
try data.write(to: fileURL)
|
||||||
generatedIcon = GeneratedIconInfo(filename: filename, size: 1024)
|
generatedIcons.append(GeneratedIcon(filename: filename, size: size))
|
||||||
status = "✅ Icon saved to Documents folder!\nOpen Files app to find it."
|
|
||||||
} catch {
|
} catch {
|
||||||
status = "Error saving icon: \(error.localizedDescription)"
|
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 {
|
} else {
|
||||||
status = "⚠️ Failed to render icon"
|
status = "⚠️ Some icons failed to generate"
|
||||||
}
|
}
|
||||||
|
|
||||||
isGenerating = false
|
isGenerating = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a generated icon file.
|
struct GeneratedIcon: Identifiable {
|
||||||
public struct GeneratedIconInfo: Identifiable, Sendable {
|
let id = UUID()
|
||||||
public let id = UUID()
|
let filename: String
|
||||||
public let filename: String
|
let size: CGFloat
|
||||||
public let size: CGFloat
|
|
||||||
|
|
||||||
public init(filename: String, size: CGFloat) {
|
|
||||||
self.filename = filename
|
|
||||||
self.size = size
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
IconGeneratorView(config: .example, appName: "Casino")
|
IconGeneratorView()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,19 +350,6 @@ struct SettingsView: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.padding(.top, Design.Spacing.xSmall)
|
.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
|
// 11. App Version
|
||||||
Text(appVersionString)
|
Text(appVersionString)
|
||||||
.font(.system(size: Design.BaseFontSize.callout))
|
.font(.system(size: Design.BaseFontSize.callout))
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB |
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "AppIcon.png",
|
"filename" : "AppIcon-1024.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
|||||||
@ -18,9 +18,12 @@ struct BlackjackApp: App {
|
|||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
AppLaunchView(config: .blackjack) {
|
// #if DEBUG
|
||||||
ContentView()
|
// IconGeneratorView()
|
||||||
}
|
// #else
|
||||||
|
ContentView() // your real app root
|
||||||
|
// #endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,25 +1,49 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<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="dark"/>
|
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="23084"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="24063"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
|
<!--View Controller-->
|
||||||
<scene sceneID="EHf-IW-A2E">
|
<scene sceneID="EHf-IW-A2E">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<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"/>
|
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||||
<color key="backgroundColor" red="0.05" green="0.35" blue="0.15" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
<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>
|
||||||
</view>
|
</view>
|
||||||
</viewController>
|
</viewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
<point key="canvasLocation" x="52.671755725190835" y="374.64788732394368"/>
|
||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -119,7 +119,6 @@
|
|||||||
},
|
},
|
||||||
"%lld." : {
|
"%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.",
|
"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,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -144,7 +143,6 @@
|
|||||||
},
|
},
|
||||||
"%lldpx" : {
|
"%lldpx" : {
|
||||||
"comment" : "A text label displaying the size of the app icon. The argument is the size of the icon in pixels.",
|
"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,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -193,7 +191,6 @@
|
|||||||
},
|
},
|
||||||
"• Add to Assets.xcassets/AppIcon" : {
|
"• Add to Assets.xcassets/AppIcon" : {
|
||||||
"comment" : "A bullet point describing how to add an app icon to Xcode's asset catalog.",
|
"comment" : "A bullet point describing how to add an app icon to Xcode's asset catalog.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -218,7 +215,6 @@
|
|||||||
},
|
},
|
||||||
"• Call IconRenderer.renderAppIcon(config: .blackjack)" : {
|
"• Call IconRenderer.renderAppIcon(config: .blackjack)" : {
|
||||||
"comment" : "A bullet point in the \"Option 2: Use IconRenderer in Code\" section of the BrandingPreviewView.",
|
"comment" : "A bullet point in the \"Option 2: Use IconRenderer in Code\" section of the BrandingPreviewView.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -242,7 +238,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"• Run the preview in Xcode" : {
|
"• Run the preview in Xcode" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -266,7 +261,6 @@
|
|||||||
},
|
},
|
||||||
"• Save the resulting UIImage to files" : {
|
"• Save the resulting UIImage to files" : {
|
||||||
"comment" : "A step in the process of exporting app icons.",
|
"comment" : "A step in the process of exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -290,7 +284,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"• Screenshot the 1024px icon" : {
|
"• Screenshot the 1024px icon" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -314,7 +307,6 @@
|
|||||||
},
|
},
|
||||||
"• Use an online tool to generate all sizes" : {
|
"• Use an online tool to generate all sizes" : {
|
||||||
"comment" : "A step in the process of exporting app icons.",
|
"comment" : "A step in the process of exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -836,7 +828,6 @@
|
|||||||
},
|
},
|
||||||
"After generating:" : {
|
"After generating:" : {
|
||||||
"comment" : "A heading for instructions on how to use the IconGeneratorView.",
|
"comment" : "A heading for instructions on how to use the IconGeneratorView.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -861,7 +852,6 @@
|
|||||||
},
|
},
|
||||||
"All Sizes" : {
|
"All Sizes" : {
|
||||||
"comment" : "A heading that describes the various sizes of the app icon.",
|
"comment" : "A heading that describes the various sizes of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -953,7 +943,6 @@
|
|||||||
},
|
},
|
||||||
"Alternative: Use an online tool" : {
|
"Alternative: Use an online tool" : {
|
||||||
"comment" : "A section header that suggests using an online tool to generate app icons.",
|
"comment" : "A section header that suggests using an online tool to generate app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1024,7 +1013,6 @@
|
|||||||
},
|
},
|
||||||
"App Icon" : {
|
"App Icon" : {
|
||||||
"comment" : "A label displayed above the preview of the app icon.",
|
"comment" : "A label displayed above the preview of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1049,7 +1037,6 @@
|
|||||||
},
|
},
|
||||||
"App Icon Preview" : {
|
"App Icon Preview" : {
|
||||||
"comment" : "A title for the preview of the app icon.",
|
"comment" : "A title for the preview of the app icon.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1118,7 +1105,6 @@
|
|||||||
},
|
},
|
||||||
"Baccarat" : {
|
"Baccarat" : {
|
||||||
"comment" : "The name of a casino game.",
|
"comment" : "The name of a casino game.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -2399,6 +2385,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Deal Splittable Pair (8s)" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"DEALER" : {
|
"DEALER" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -3600,7 +3589,6 @@
|
|||||||
},
|
},
|
||||||
"Generate & Save Icons" : {
|
"Generate & Save Icons" : {
|
||||||
"comment" : "A button label that triggers icon generation and saving.",
|
"comment" : "A button label that triggers icon generation and saving.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -3625,7 +3613,6 @@
|
|||||||
},
|
},
|
||||||
"Generated Icons:" : {
|
"Generated Icons:" : {
|
||||||
"comment" : "A label describing the list of icons that have been successfully generated.",
|
"comment" : "A label describing the list of icons that have been successfully generated.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -3650,7 +3637,6 @@
|
|||||||
},
|
},
|
||||||
"Generating..." : {
|
"Generating..." : {
|
||||||
"comment" : "A label indicating that the app is currently generating icons.",
|
"comment" : "A label indicating that the app is currently generating icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -3969,7 +3955,6 @@
|
|||||||
},
|
},
|
||||||
"How to Export Icons" : {
|
"How to Export Icons" : {
|
||||||
"comment" : "A section header explaining how to export app icons.",
|
"comment" : "A section header explaining how to export app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -4060,7 +4045,6 @@
|
|||||||
},
|
},
|
||||||
"Icon" : {
|
"Icon" : {
|
||||||
"comment" : "The label for the tab item representing the app icon preview.",
|
"comment" : "The label for the tab item representing the app icon preview.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -4085,7 +4069,6 @@
|
|||||||
},
|
},
|
||||||
"Icon Generator" : {
|
"Icon Generator" : {
|
||||||
"comment" : "The title of the Icon Generator view.",
|
"comment" : "The title of the Icon Generator view.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -4451,7 +4434,6 @@
|
|||||||
},
|
},
|
||||||
"Launch" : {
|
"Launch" : {
|
||||||
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
|
"comment" : "A tab in the BrandingPreviewView that links to the launch screen preview.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -5111,7 +5093,6 @@
|
|||||||
},
|
},
|
||||||
"Option 1: Screenshot from Preview" : {
|
"Option 1: Screenshot from Preview" : {
|
||||||
"comment" : "A description of one method for exporting app icons.",
|
"comment" : "A description of one method for exporting app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -5136,7 +5117,6 @@
|
|||||||
},
|
},
|
||||||
"Option 2: Use IconRenderer in Code" : {
|
"Option 2: Use IconRenderer in Code" : {
|
||||||
"comment" : "A subheading within the instructions section of the BrandingPreviewView.",
|
"comment" : "A subheading within the instructions section of the BrandingPreviewView.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -5183,7 +5163,6 @@
|
|||||||
},
|
},
|
||||||
"Other Game Icons" : {
|
"Other Game Icons" : {
|
||||||
"comment" : "A label displayed above a section of the BrandingPreviewView that shows icons for other games.",
|
"comment" : "A label displayed above a section of the BrandingPreviewView that shows icons for other games.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -5208,7 +5187,6 @@
|
|||||||
},
|
},
|
||||||
"Others" : {
|
"Others" : {
|
||||||
"comment" : "A tab label for the section displaying icons for other games.",
|
"comment" : "A tab label for the section displaying icons for other games.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -5486,7 +5464,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Poker" : {
|
"Poker" : {
|
||||||
"extractionState" : "stale",
|
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
"stringUnit" : {
|
"stringUnit" : {
|
||||||
@ -5804,7 +5781,6 @@
|
|||||||
},
|
},
|
||||||
"Roulette" : {
|
"Roulette" : {
|
||||||
"comment" : "The name of a roulette card.",
|
"comment" : "The name of a roulette card.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -7219,7 +7195,6 @@
|
|||||||
},
|
},
|
||||||
"These show how the same pattern works for other games" : {
|
"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.",
|
"comment" : "A description below the section of the view that previews icons for other games.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -7496,7 +7471,6 @@
|
|||||||
},
|
},
|
||||||
"Upload the 1024px icon to appicon.co or makeappicon.com to generate all sizes automatically." : {
|
"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.",
|
"comment" : "A description of an alternative method for generating app icons.",
|
||||||
"extractionState" : "stale",
|
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
|
|||||||
@ -1,17 +1,15 @@
|
|||||||
//
|
//
|
||||||
// BrandingConfig.swift
|
// BrandingConfig+Blackjack.swift
|
||||||
// Blackjack
|
// Blackjack
|
||||||
//
|
//
|
||||||
// App-specific branding configurations for icons and launch screens.
|
// Blackjack-specific branding configurations for AppIconView and LaunchScreenView.
|
||||||
//
|
//
|
||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import CasinoKit
|
import CasinoKit
|
||||||
|
|
||||||
// MARK: - App Icon Configuration
|
|
||||||
|
|
||||||
extension AppIconConfig {
|
extension AppIconConfig {
|
||||||
/// Blackjack app icon configuration.
|
/// Blackjack game icon configuration.
|
||||||
static let blackjack = AppIconConfig(
|
static let blackjack = AppIconConfig(
|
||||||
title: "BLACKJACK",
|
title: "BLACKJACK",
|
||||||
subtitle: "21",
|
subtitle: "21",
|
||||||
@ -21,10 +19,8 @@ extension AppIconConfig {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Launch Screen Configuration
|
|
||||||
|
|
||||||
extension LaunchScreenConfig {
|
extension LaunchScreenConfig {
|
||||||
/// Blackjack launch screen configuration.
|
/// Blackjack game launch screen configuration.
|
||||||
static let blackjack = LaunchScreenConfig(
|
static let blackjack = LaunchScreenConfig(
|
||||||
title: "BLACKJACK",
|
title: "BLACKJACK",
|
||||||
subtitle: "21",
|
subtitle: "21",
|
||||||
@ -34,3 +30,14 @@ extension LaunchScreenConfig {
|
|||||||
secondaryColor: Color(red: 0.03, green: 0.2, blue: 0.1)
|
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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
130
Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift
Normal file
130
Blackjack/Blackjack/Views/Development/BrandingPreviewView.swift
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
//
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
196
Blackjack/Blackjack/Views/Development/IconGeneratorView.swift
Normal file
196
Blackjack/Blackjack/Views/Development/IconGeneratorView.swift
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
//
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
@ -435,27 +435,28 @@ struct SettingsView: View {
|
|||||||
// calls the corresponding debug function in GameState.
|
// calls the corresponding debug function in GameState.
|
||||||
// ─────────────────────────────────────────────────────────────
|
// ─────────────────────────────────────────────────────────────
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
SheetSection(title: "DEBUG", icon: "ant.fill") {
|
|
||||||
// Game-specific debug actions
|
|
||||||
if let state = gameState {
|
if let state = gameState {
|
||||||
|
SheetSection(title: "DEBUG", icon: "ant.fill") {
|
||||||
// Split Testing - deals a pair of 8s
|
// Split Testing - deals a pair of 8s
|
||||||
DebugActionRow(
|
Button {
|
||||||
title: "Deal Splittable Pair (8s)",
|
|
||||||
icon: "rectangle.split.2x1"
|
|
||||||
) {
|
|
||||||
triggerDebugDeal(state: state)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
// Add new debug buttons here:
|
||||||
.background(Color.orange.opacity(Design.Opacity.hint))
|
// Divider().background(Color.orange.opacity(Design.Opacity.hint))
|
||||||
|
// Button { triggerDebugBlackjack(state: state) } label: { ... }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Branding tools (from CasinoKit)
|
|
||||||
BrandingDebugRows(
|
|
||||||
iconConfig: .blackjack,
|
|
||||||
launchConfig: .blackjack,
|
|
||||||
appName: "Blackjack"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -72,17 +72,12 @@
|
|||||||
// - SelectableRow (card-like selectable picker row)
|
// - SelectableRow (card-like selectable picker row)
|
||||||
// - SelectionIndicator (checkmark circle)
|
// - SelectionIndicator (checkmark circle)
|
||||||
// - BadgePill (capsule badge for values)
|
// - BadgePill (capsule badge for values)
|
||||||
// - DebugNavigationRow (debug row with navigation)
|
|
||||||
// - DebugActionRow (debug row with action)
|
|
||||||
// - BrandingDebugRows (icon generator + branding preview rows)
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: - Branding
|
// MARK: - Branding
|
||||||
// - AppIconView, AppIconConfig
|
// - AppIconView, AppIconConfig
|
||||||
// - LaunchScreenView, LaunchScreenConfig, StaticLaunchScreenView
|
// - LaunchScreenView, LaunchScreenConfig, StaticLaunchScreenView
|
||||||
// - IconRenderer, IconExportView
|
// - IconRenderer, IconExportView
|
||||||
// - IconGeneratorView, GeneratedIconInfo (development tool for exporting icons)
|
|
||||||
// - BrandingPreviewView (development tool for previewing all branding assets)
|
|
||||||
|
|
||||||
// MARK: - Theme
|
// MARK: - Theme
|
||||||
// - CasinoTheme (protocol)
|
// - CasinoTheme (protocol)
|
||||||
|
|||||||
@ -93,10 +93,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"%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%%" : {
|
"%lld%%" : {
|
||||||
"comment" : "A text displaying the current volume percentage. The argument is a value between 0.0 (no volume) and 1.0 (full volume).",
|
"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,
|
"isCommentAutoGenerated" : true,
|
||||||
@ -145,10 +141,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"%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.",
|
"comment" : "A bullet point indicator.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@ -324,10 +316,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"1024 × 1024px" : {
|
|
||||||
"comment" : "A description of the size of the app icon.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"Ace" : {
|
"Ace" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -377,10 +365,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"After generating:" : {
|
|
||||||
"comment" : "A heading describing the steps to take after generating app icons.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"All game data is stored:" : {
|
"All game data is stored:" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -403,10 +387,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"App Icon" : {
|
|
||||||
"comment" : "The title of the section displaying the app icon preview.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"App Icon Preview" : {
|
"App Icon Preview" : {
|
||||||
"comment" : "A title for the preview section of the icon export view.",
|
"comment" : "A title for the preview section of the icon export view.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
@ -1119,14 +1099,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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" : {
|
"Global" : {
|
||||||
"comment" : "Title for the \"Global\" tab in the statistics view.",
|
"comment" : "Title for the \"Global\" tab in the statistics view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -1230,14 +1202,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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:" : {
|
"If you choose to enable iCloud sync:" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1436,10 +1400,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"Launch" : {
|
|
||||||
"comment" : "A tab label for the launch screen preview.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"Locally on your device using iOS standard storage" : {
|
"Locally on your device using iOS standard storage" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -1618,10 +1578,6 @@
|
|||||||
},
|
},
|
||||||
"No Session History" : {
|
"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." : {
|
"Our apps do not integrate with third-party services that collect user data. We do not share any information with third parties." : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -2150,10 +2106,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"To Export" : {
|
|
||||||
"comment" : "A section header explaining how to export branding assets.",
|
|
||||||
"isCommentAutoGenerated" : true
|
|
||||||
},
|
|
||||||
"TOTAL" : {
|
"TOTAL" : {
|
||||||
"comment" : "A label displayed alongside the total winnings in the result banner.",
|
"comment" : "A label displayed alongside the total winnings in the result banner.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
@ -2196,10 +2148,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"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" : {
|
"VIP" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -2396,10 +2344,6 @@
|
|||||||
"comment" : "A label for the worst session amount in the statistics view.",
|
"comment" : "A label for the worst session amount in the statistics view.",
|
||||||
"isCommentAutoGenerated" : true
|
"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" : {
|
"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.",
|
"comment" : "Text in the Privacy Policy View explaining how to disable iCloud sync in the app settings.",
|
||||||
"isCommentAutoGenerated" : true,
|
"isCommentAutoGenerated" : true,
|
||||||
|
|||||||
@ -33,21 +33,27 @@ public struct AppIconConfig: Sendable {
|
|||||||
self.accentColor = accentColor
|
self.accentColor = accentColor
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Example Configuration (for previews only)
|
// MARK: - Example Preset Configurations
|
||||||
|
// Game-specific presets should be defined in the respective apps as extensions.
|
||||||
|
|
||||||
/// Example configuration for CasinoKit previews.
|
/// Poker game icon configuration (example preset).
|
||||||
/// Apps should define their own configs in `BrandingConfig.swift`.
|
public static let poker = AppIconConfig(
|
||||||
public static let example = AppIconConfig(
|
title: "POKER",
|
||||||
title: "CASINO",
|
iconSymbol: "suit.diamond.fill",
|
||||||
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)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A customizable app icon view for casino games.
|
/// A customizable app icon view for casino games.
|
||||||
/// Render this view to create your app icon assets.
|
/// 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 {
|
public struct AppIconView: View {
|
||||||
let config: AppIconConfig
|
let config: AppIconConfig
|
||||||
let size: CGFloat
|
let size: CGFloat
|
||||||
@ -57,33 +63,16 @@ public struct AppIconView: View {
|
|||||||
self.size = size
|
self.size = size
|
||||||
}
|
}
|
||||||
|
|
||||||
// Size calculations
|
private var cornerRadius: CGFloat { size * 0.22 }
|
||||||
private var iconSize: CGFloat { size * 0.35 }
|
private var iconSize: CGFloat { size * 0.35 }
|
||||||
|
private var titleSize: CGFloat { size * 0.12 }
|
||||||
private var subtitleSize: CGFloat { size * 0.25 }
|
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 {
|
public var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
// Background gradient - full bleed, no rounded corners
|
// Background gradient
|
||||||
// iOS will apply its own superellipse mask
|
RoundedRectangle(cornerRadius: cornerRadius)
|
||||||
Rectangle()
|
|
||||||
.fill(
|
.fill(
|
||||||
LinearGradient(
|
LinearGradient(
|
||||||
colors: [config.primaryColor, config.secondaryColor],
|
colors: [config.primaryColor, config.secondaryColor],
|
||||||
@ -96,6 +85,21 @@ public struct AppIconView: View {
|
|||||||
DiamondPatternOverlay(size: size)
|
DiamondPatternOverlay(size: size)
|
||||||
.opacity(0.08)
|
.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
|
// Content
|
||||||
VStack(spacing: size * 0.03) {
|
VStack(spacing: size * 0.03) {
|
||||||
// Icon symbol
|
// Icon symbol
|
||||||
@ -175,56 +179,24 @@ private struct DiamondPatternOverlay: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Preview
|
||||||
|
|
||||||
#Preview("App Icon") {
|
#Preview("Poker Icon") {
|
||||||
AppIconView(config: .example, size: 512)
|
AppIconView(config: .poker, size: 512)
|
||||||
.clipShape(.rect(cornerRadius: 512 * 0.22))
|
|
||||||
.padding()
|
.padding()
|
||||||
.background(Color.gray)
|
.background(Color.gray)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Title Scaling") {
|
#Preview("Roulette Icon") {
|
||||||
let shortTitle = AppIconConfig(title: "POKER", iconSymbol: "suit.diamond.fill")
|
AppIconView(config: .roulette, size: 512)
|
||||||
let mediumTitle = AppIconConfig(title: "BACCARAT", iconSymbol: "suit.spade.fill")
|
.padding()
|
||||||
let longTitle = AppIconConfig(
|
.background(Color.gray)
|
||||||
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) {
|
#Preview("All Icons") {
|
||||||
VStack {
|
HStack(spacing: 20) {
|
||||||
AppIconView(config: shortTitle, size: 180)
|
AppIconView(config: .poker, size: 200)
|
||||||
.clipShape(.rect(cornerRadius: 180 * 0.22))
|
AppIconView(config: .roulette, size: 200)
|
||||||
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()
|
.padding()
|
||||||
.background(Color.gray)
|
.background(Color.gray)
|
||||||
|
|||||||
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,91 +0,0 @@
|
|||||||
//
|
|
||||||
// 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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -134,6 +134,6 @@ public struct IconExportView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Icon Export") {
|
#Preview("Icon Export") {
|
||||||
IconExportView(config: .example)
|
IconExportView(config: .poker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -38,14 +38,15 @@ public struct LaunchScreenConfig: Sendable {
|
|||||||
self.showLoadingIndicator = showLoadingIndicator
|
self.showLoadingIndicator = showLoadingIndicator
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Example Configuration (for previews only)
|
// MARK: - Example Preset Configurations
|
||||||
|
// Game-specific presets should be defined in the respective apps as extensions.
|
||||||
|
|
||||||
/// Example configuration for CasinoKit previews.
|
/// Poker game launch screen configuration (example preset).
|
||||||
/// Apps should define their own configs in `BrandingConfig.swift`.
|
public static let poker = LaunchScreenConfig(
|
||||||
public static let example = LaunchScreenConfig(
|
title: "POKER",
|
||||||
title: "CASINO",
|
tagline: "Texas Hold'em",
|
||||||
tagline: "Your Game Tagline",
|
iconSymbols: ["suit.diamond.fill", "suit.club.fill"],
|
||||||
iconSymbols: ["suit.diamond.fill", "suit.heart.fill"]
|
accentColor: .red
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,13 +321,13 @@ public struct StaticLaunchScreenView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Previews
|
// MARK: - Preview
|
||||||
|
|
||||||
#Preview("Launch Screen") {
|
#Preview("Poker Launch") {
|
||||||
LaunchScreenView(config: .example)
|
LaunchScreenView(config: .poker)
|
||||||
}
|
}
|
||||||
|
|
||||||
#Preview("Static Launch") {
|
#Preview("Static Launch") {
|
||||||
StaticLaunchScreenView(config: .example)
|
StaticLaunchScreenView(config: .poker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,130 +0,0 @@
|
|||||||
//
|
|
||||||
// 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"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user