193 lines
6.9 KiB
Swift
193 lines
6.9 KiB
Swift
//
|
|
// IconGeneratorView.swift
|
|
// Baccarat
|
|
//
|
|
// Development tool to generate and export app icon images.
|
|
// Run this view, tap the button, then find the icons in the Files app.
|
|
//
|
|
|
|
import SwiftUI
|
|
import CasinoKit
|
|
|
|
/// A development view that generates and saves app icon images.
|
|
/// After running, find the icons in Files app → On My iPhone → Baccarat
|
|
struct IconGeneratorView: View {
|
|
@State private var status: String = "Tap the button to generate icons"
|
|
@State private var isGenerating = false
|
|
@State private var generatedIcons: [GeneratedIcon] = []
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
ScrollView {
|
|
VStack(spacing: 24) {
|
|
// Preview
|
|
AppIconView(config: .baccarat, size: 200)
|
|
.clipShape(.rect(cornerRadius: 200 * 0.22))
|
|
.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(0.1))
|
|
.clipShape(.rect(cornerRadius: 12))
|
|
.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: 8) {
|
|
instructionRow(number: 1, text: "Open Files app on your device/simulator")
|
|
instructionRow(number: 2, text: "Navigate to: On My iPhone → Baccarat")
|
|
instructionRow(number: 3, text: "Find the AppIcon-1024.png file")
|
|
instructionRow(number: 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(0.1))
|
|
.clipShape(.rect(cornerRadius: 12))
|
|
.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: .baccarat, size: size)
|
|
let renderer = ImageRenderer(content: view)
|
|
renderer.scale = 1.0
|
|
|
|
if let uiImage = renderer.uiImage,
|
|
let data = uiImage.pngData() {
|
|
let filename = "\(name).png"
|
|
let fileURL = documentsPath.appending(path: filename)
|
|
|
|
do {
|
|
try data.write(to: fileURL)
|
|
generatedIcons.append(GeneratedIcon(filename: filename, size: size))
|
|
} catch {
|
|
status = "Error saving \(filename): \(error.localizedDescription)"
|
|
}
|
|
}
|
|
|
|
// Small delay for UI feedback
|
|
try? await Task.sleep(for: .milliseconds(100))
|
|
}
|
|
|
|
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()
|
|
}
|
|
|