1.0.0 for iOS

This commit is contained in:
2025-12-07 23:58:13 -07:00
parent ff6009f237
commit 766f72dcfa
10 changed files with 66 additions and 17 deletions

View File

@@ -425,7 +425,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -459,7 +459,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = com.atri.dad.MagicCounter.MagicCounter;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;

View File

@@ -112,3 +112,20 @@ struct SettingSlider: View {
}
}
}
/**
* Helper for haptic feedback.
*/
enum Haptics {
static func play(_ style: UIImpactFeedbackGenerator.FeedbackStyle) {
guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return }
let generator = UIImpactFeedbackGenerator(style: style)
generator.impactOccurred()
}
static func notification(_ type: UINotificationFeedbackGenerator.FeedbackType) {
guard UserDefaults.standard.bool(forKey: "hapticFeedbackEnabled") else { return }
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(type)
}
}

View File

@@ -63,7 +63,7 @@ struct ContentView: View {
}
}
.listStyle(.insetGrouped)
.navigationTitle("History")
.navigationTitle("Games")
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: { showSetup = true }) {
@@ -73,7 +73,7 @@ struct ContentView: View {
}
}
.tabItem {
Label("History", systemImage: "clock.fill")
Label("Games", systemImage: "clock.fill")
}
SettingsView()

View File

@@ -76,7 +76,6 @@ struct GameView: View {
.clipShape(Circle())
}
} else {
// Placeholder for balance
Color.clear.frame(width: 40, height: 40)
}
}
@@ -85,9 +84,6 @@ struct GameView: View {
// Players Grid
GeometryReader { geometry in
// Use adaptive grid with minimum width to handle responsiveness
// 320pt minimum ensures 1 column on iPhone Portrait (width ~390)
// and 2 columns on iPhone Landscape (width ~844) or iPad
let columns = [GridItem(.adaptive(minimum: 320), spacing: 24)]
ScrollView {
LazyVGrid(columns: columns, spacing: 24) {
@@ -128,6 +124,9 @@ struct GameView: View {
.presentationDetents([.medium])
}
.onChange(of: gameState) { newState in
if newState.winner != nil {
Haptics.notification(.success)
}
gameManager.updateActiveGame(state: newState)
}
}
@@ -172,6 +171,8 @@ struct PlayerCell: View {
let onCommanderTap: () -> Void
let onScoop: () -> Void
@State private var showScoopConfirmation = false
var body: some View {
ZStack {
RoundedRectangle(cornerRadius: 24)
@@ -192,12 +193,8 @@ struct PlayerCell: View {
.foregroundStyle(.primary)
Spacer()
Menu {
Button(role: .destructive, action: onScoop) {
Label("Scoop", systemImage: "flag.fill")
}
} label: {
Image(systemName: "ellipsis.circle")
Button(action: { showScoopConfirmation = true }) {
Image(systemName: "flag.fill")
.foregroundStyle(.secondary)
.padding(4)
}
@@ -257,20 +254,39 @@ struct PlayerCell: View {
.opacity(player.isEliminated && !isWinner ? 0.8 : 1)
.scaleEffect(isWinner ? 1.05 : 1)
.animation(.spring, value: isWinner)
.alert("Scoop?", isPresented: $showScoopConfirmation) {
Button("Cancel", role: .cancel) { }
Button("Scoop", role: .destructive) {
Haptics.notification(.warning)
onScoop()
}
} message: {
Text("Are you sure you want to scoop?")
}
}
private func adjustLife(by amount: Int) {
if player.isEliminated { return }
Haptics.play(.light)
var newPlayer = player
newPlayer.life += amount
onUpdate(newPlayer)
if newPlayer.isEliminated && !player.isEliminated {
Haptics.notification(.error)
}
}
private func adjustPoison(by amount: Int) {
if player.isEliminated { return }
Haptics.play(.light)
var newPlayer = player
newPlayer.poison = max(0, newPlayer.poison + amount)
onUpdate(newPlayer)
if newPlayer.isEliminated && !player.isEliminated {
Haptics.notification(.error)
}
}
}
@@ -328,11 +344,16 @@ struct CommanderDamageView: View {
}
private func adjustCommanderDamage(attackerId: Int, by amount: Int) {
Haptics.play(.light)
var newPlayer = targetPlayer
var damages = newPlayer.commanderDamages
let current = damages[attackerId] ?? 0
damages[attackerId] = max(0, current + amount)
newPlayer.commanderDamages = damages
onUpdate(newPlayer)
if newPlayer.isEliminated && !targetPlayer.isEliminated {
Haptics.notification(.error)
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -7,10 +7,10 @@
{
"layers" : [
{
"image-name" : "MagicCounter 2.png",
"name" : "MagicCounter 2",
"image-name" : "logo 2.png",
"name" : "logo 2",
"position" : {
"scale" : 0.8,
"scale" : 0.85,
"translation-in-points" : [
0,
0

View File

@@ -10,6 +10,12 @@ import SwiftUI
@main
struct MagicCounterApp: App {
@StateObject private var gameManager = GameManager()
init() {
UserDefaults.standard.register(defaults: [
"hapticFeedbackEnabled": true
])
}
var body: some Scene {
WindowGroup {

View File

@@ -9,6 +9,7 @@ import SwiftUI
struct SettingsView: View {
@AppStorage("accentColorName") private var accentColorName = "Blue"
@AppStorage("hapticFeedbackEnabled") private var hapticFeedbackEnabled = true
private let colors: [(name: String, color: Color)] = [
("Blue", .blue),
@@ -44,6 +45,10 @@ struct SettingsView: View {
var body: some View {
NavigationStack {
Form {
Section("General") {
Toggle("Haptic Feedback", isOn: $hapticFeedbackEnabled)
}
Section("Appearance") {
VStack(alignment: .leading, spacing: 16) {
Text("ACCENT COLOR")