added in style card reveal styles
Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>
This commit is contained in:
parent
02eecf40ec
commit
88d8c26865
@ -114,6 +114,19 @@ final class GameState: CasinoGameState {
|
|||||||
var isAnimating: Bool = false
|
var isAnimating: Bool = false
|
||||||
var showResultBanner: Bool = false
|
var showResultBanner: Bool = false
|
||||||
|
|
||||||
|
// MARK: - Reveal State
|
||||||
|
/// Whether the game is waiting for a user to reveal a card (Tap or Squeeze style).
|
||||||
|
var isWaitingForReveal: Bool = false
|
||||||
|
|
||||||
|
/// The index of the card currently being revealed (0-3 for initial, 4+ for others).
|
||||||
|
var currentRevealIndex: Int = 0
|
||||||
|
|
||||||
|
/// The progress of a squeeze reveal (0.0 to 1.0).
|
||||||
|
var revealProgress: Double = 0.0
|
||||||
|
|
||||||
|
/// Continuation to resume dealing after a manual reveal.
|
||||||
|
private var revealContinuation: CheckedContinuation<Void, Never>?
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
|
|
||||||
var totalBetAmount: Int {
|
var totalBetAmount: Int {
|
||||||
@ -607,6 +620,41 @@ final class GameState: CasinoGameState {
|
|||||||
return allBetTypes.map { betAmount(for: $0) }.min() ?? 0
|
return allBetTypes.map { betAmount(for: $0) }.min() ?? 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func revealCurrentCard() {
|
||||||
|
guard isWaitingForReveal else { return }
|
||||||
|
|
||||||
|
isWaitingForReveal = false
|
||||||
|
revealProgress = 1.0
|
||||||
|
|
||||||
|
let continuation = revealContinuation
|
||||||
|
revealContinuation = nil
|
||||||
|
continuation?.resume()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the progress of a squeeze reveal.
|
||||||
|
func updateRevealProgress(_ progress: Double) {
|
||||||
|
revealProgress = progress
|
||||||
|
if progress >= 1.0 {
|
||||||
|
revealCurrentCard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func waitForReveal(index: Int) async {
|
||||||
|
guard settings.showAnimations else { return }
|
||||||
|
|
||||||
|
if settings.revealStyle == .tap || settings.revealStyle == .squeeze {
|
||||||
|
currentRevealIndex = index
|
||||||
|
revealProgress = 0.0
|
||||||
|
isWaitingForReveal = true
|
||||||
|
|
||||||
|
await withCheckedContinuation { continuation in
|
||||||
|
revealContinuation = continuation
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Auto Styles
|
||||||
|
try? await Task.sleep(for: flipDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
// MARK: - Betting Actions
|
// MARK: - Betting Actions
|
||||||
|
|
||||||
/// Places a bet of the specified amount on the given bet type.
|
/// Places a bet of the specified amount on the given bet type.
|
||||||
@ -685,13 +733,17 @@ final class GameState: CasinoGameState {
|
|||||||
|
|
||||||
// Wait for layout animation to complete before dealing cards
|
// Wait for layout animation to complete before dealing cards
|
||||||
if settings.showAnimations {
|
if settings.showAnimations {
|
||||||
try? await Task.sleep(for: .seconds(1))
|
// Increased from 1s to 1.25s for better stability on all devices
|
||||||
|
try? await Task.sleep(for: .milliseconds(1250))
|
||||||
}
|
}
|
||||||
|
|
||||||
let initialCards = engine.dealInitialCards()
|
let initialCards = engine.dealInitialCards()
|
||||||
|
|
||||||
// Check if animations are enabled
|
// Check if animations are enabled
|
||||||
if settings.showAnimations {
|
if settings.showAnimations {
|
||||||
|
// Brief extra delay before the very first card deal starts
|
||||||
|
try? await Task.sleep(for: .milliseconds(250))
|
||||||
|
|
||||||
// Animate dealing: P1, B1, P2, B2
|
// Animate dealing: P1, B1, P2, B2
|
||||||
for (index, card) in initialCards.enumerated() {
|
for (index, card) in initialCards.enumerated() {
|
||||||
try? await Task.sleep(for: dealDelay)
|
try? await Task.sleep(for: dealDelay)
|
||||||
@ -708,21 +760,52 @@ final class GameState: CasinoGameState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flip cards one by one instead of all at once
|
// Reveal according to selected style
|
||||||
|
switch settings.revealStyle {
|
||||||
|
case .auto, .tap, .squeeze:
|
||||||
|
// Sequential reveal: P1, B1, P2, B2
|
||||||
for i in 0..<playerCardsFaceUp.count {
|
for i in 0..<playerCardsFaceUp.count {
|
||||||
// Flip player card
|
// Flip player card
|
||||||
try? await Task.sleep(for: flipDelay)
|
await waitForReveal(index: i * 2)
|
||||||
sound.playCardFlip()
|
sound.playCardFlip()
|
||||||
playerCardsFaceUp[i] = true
|
playerCardsFaceUp[i] = true
|
||||||
|
|
||||||
// Flip banker card
|
// Flip banker card
|
||||||
try? await Task.sleep(for: flipDelay)
|
await waitForReveal(index: i * 2 + 1)
|
||||||
sound.playCardFlip()
|
sound.playCardFlip()
|
||||||
bankerCardsFaceUp[i] = true
|
bankerCardsFaceUp[i] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pause to let user see initial totals
|
case .batch:
|
||||||
|
// Reveal Top (Banker) then Bottom (Player)
|
||||||
|
// We use sequential flips within the batch (1 after the other)
|
||||||
|
try? await Task.sleep(for: flipDelay)
|
||||||
|
|
||||||
|
// Top Batch (Banker)
|
||||||
|
for i in 0..<bankerCardsFaceUp.count {
|
||||||
|
sound.playCardFlip()
|
||||||
|
bankerCardsFaceUp[i] = true
|
||||||
|
if i < bankerCardsFaceUp.count - 1 {
|
||||||
|
try? await Task.sleep(for: shortDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try? await Task.sleep(for: resultDelay)
|
try? await Task.sleep(for: resultDelay)
|
||||||
|
|
||||||
|
// Bottom Batch (Player)
|
||||||
|
for i in 0..<playerCardsFaceUp.count {
|
||||||
|
sound.playCardFlip()
|
||||||
|
playerCardsFaceUp[i] = true
|
||||||
|
if i < playerCardsFaceUp.count - 1 {
|
||||||
|
try? await Task.sleep(for: shortDelay)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause to let user see initial totals
|
||||||
|
if settings.revealStyle == .auto || settings.revealStyle == .batch {
|
||||||
|
try? await Task.sleep(for: resultDelay)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// No animations - show all cards immediately
|
// No animations - show all cards immediately
|
||||||
for (index, card) in initialCards.enumerated() {
|
for (index, card) in initialCards.enumerated() {
|
||||||
@ -755,11 +838,13 @@ final class GameState: CasinoGameState {
|
|||||||
visiblePlayerCards.append(playerThird)
|
visiblePlayerCards.append(playerThird)
|
||||||
playerCardsFaceUp.append(false)
|
playerCardsFaceUp.append(false)
|
||||||
|
|
||||||
try? await Task.sleep(for: flipDelay) // Using flipDelay for third card too
|
await waitForReveal(index: 4) // Player 3rd card
|
||||||
sound.playCardFlip()
|
sound.playCardFlip()
|
||||||
playerCardsFaceUp[2] = true
|
playerCardsFaceUp[2] = true
|
||||||
|
|
||||||
|
if settings.revealStyle == .auto || settings.revealStyle == .batch {
|
||||||
try? await Task.sleep(for: resultDelay)
|
try? await Task.sleep(for: resultDelay)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
visiblePlayerCards.append(playerThird)
|
visiblePlayerCards.append(playerThird)
|
||||||
playerCardsFaceUp.append(true)
|
playerCardsFaceUp.append(true)
|
||||||
@ -775,11 +860,13 @@ final class GameState: CasinoGameState {
|
|||||||
visibleBankerCards.append(bankerThird)
|
visibleBankerCards.append(bankerThird)
|
||||||
bankerCardsFaceUp.append(false)
|
bankerCardsFaceUp.append(false)
|
||||||
|
|
||||||
try? await Task.sleep(for: flipDelay) // Using flipDelay for third card too
|
await waitForReveal(index: 5) // Banker 3rd card
|
||||||
sound.playCardFlip()
|
sound.playCardFlip()
|
||||||
bankerCardsFaceUp[2] = true
|
bankerCardsFaceUp[2] = true
|
||||||
|
|
||||||
|
if settings.revealStyle == .auto || settings.revealStyle == .batch {
|
||||||
try? await Task.sleep(for: resultDelay)
|
try? await Task.sleep(for: resultDelay)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
visibleBankerCards.append(bankerThird)
|
visibleBankerCards.append(bankerThird)
|
||||||
bankerCardsFaceUp.append(true)
|
bankerCardsFaceUp.append(true)
|
||||||
|
|||||||
@ -34,6 +34,43 @@ enum DeckCount: Int, CaseIterable, Identifiable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The style used to reveal cards.
|
||||||
|
enum RevealStyle: String, CaseIterable, Identifiable {
|
||||||
|
case auto // Sequential automatic (current)
|
||||||
|
case batch // Player batch then Banker batch
|
||||||
|
case tap // Sequential tap-to-reveal
|
||||||
|
case squeeze // Gestural squeeze/peel
|
||||||
|
|
||||||
|
var id: String { rawValue }
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .auto: return String(localized: "Auto-Flip")
|
||||||
|
case .batch: return String(localized: "Batch Reveal")
|
||||||
|
case .tap: return String(localized: "Tap-to-Reveal")
|
||||||
|
case .squeeze: return String(localized: "Squeeze/Peel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .auto: return String(localized: "Classic sequential reveal")
|
||||||
|
case .batch: return String(localized: "Top position cards together, then Bottom")
|
||||||
|
case .tap: return String(localized: "Tap each card to reveal it")
|
||||||
|
case .squeeze: return String(localized: "The authentic high-stakes ritual")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var icon: String {
|
||||||
|
switch self {
|
||||||
|
case .auto: return "play.circle"
|
||||||
|
case .batch: return "square.grid.2x2"
|
||||||
|
case .tap: return "hand.tap"
|
||||||
|
case .squeeze: return "hand.draw"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TableLimits is now provided by CasinoKit
|
// TableLimits is now provided by CasinoKit
|
||||||
|
|
||||||
/// Observable settings class for Baccarat configuration.
|
/// Observable settings class for Baccarat configuration.
|
||||||
@ -74,6 +111,9 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
/// Speed of card dealing (uses CasinoDesign.DealingSpeed constants)
|
/// Speed of card dealing (uses CasinoDesign.DealingSpeed constants)
|
||||||
var dealingSpeed: Double = CasinoDesign.DealingSpeed.normal
|
var dealingSpeed: Double = CasinoDesign.DealingSpeed.normal
|
||||||
|
|
||||||
|
/// The style used to reveal cards.
|
||||||
|
var revealStyle: RevealStyle = .auto
|
||||||
|
|
||||||
// MARK: - Display Settings
|
// MARK: - Display Settings
|
||||||
|
|
||||||
/// Whether to show the cards remaining indicator.
|
/// Whether to show the cards remaining indicator.
|
||||||
@ -110,6 +150,7 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
static let soundEnabled = "settings.soundEnabled"
|
static let soundEnabled = "settings.soundEnabled"
|
||||||
static let hapticsEnabled = "settings.hapticsEnabled"
|
static let hapticsEnabled = "settings.hapticsEnabled"
|
||||||
static let soundVolume = "settings.soundVolume"
|
static let soundVolume = "settings.soundVolume"
|
||||||
|
static let revealStyle = "settings.revealStyle"
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - iCloud
|
// MARK: - iCloud
|
||||||
@ -225,6 +266,11 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
if let volume = defaults.object(forKey: Keys.soundVolume) as? Float {
|
if let volume = defaults.object(forKey: Keys.soundVolume) as? Float {
|
||||||
self.soundVolume = volume
|
self.soundVolume = volume
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let rawStyle = defaults.string(forKey: Keys.revealStyle),
|
||||||
|
let style = RevealStyle(rawValue: rawStyle) {
|
||||||
|
self.revealStyle = style
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads settings from iCloud.
|
/// Loads settings from iCloud.
|
||||||
@ -276,6 +322,11 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
if let volume = store.object(forKey: Keys.soundVolume) as? Double {
|
if let volume = store.object(forKey: Keys.soundVolume) as? Double {
|
||||||
self.soundVolume = Float(volume)
|
self.soundVolume = Float(volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let rawStyle = store.string(forKey: Keys.revealStyle),
|
||||||
|
let style = RevealStyle(rawValue: rawStyle) {
|
||||||
|
self.revealStyle = style
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Saves settings to UserDefaults and iCloud.
|
/// Saves settings to UserDefaults and iCloud.
|
||||||
@ -293,6 +344,7 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
defaults.set(soundEnabled, forKey: Keys.soundEnabled)
|
defaults.set(soundEnabled, forKey: Keys.soundEnabled)
|
||||||
defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
defaults.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
||||||
defaults.set(soundVolume, forKey: Keys.soundVolume)
|
defaults.set(soundVolume, forKey: Keys.soundVolume)
|
||||||
|
defaults.set(revealStyle.rawValue, forKey: Keys.revealStyle)
|
||||||
|
|
||||||
// Also save to iCloud
|
// Also save to iCloud
|
||||||
if iCloudAvailable, let store = iCloudStore {
|
if iCloudAvailable, let store = iCloudStore {
|
||||||
@ -307,6 +359,7 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
store.set(soundEnabled, forKey: Keys.soundEnabled)
|
store.set(soundEnabled, forKey: Keys.soundEnabled)
|
||||||
store.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
store.set(hapticsEnabled, forKey: Keys.hapticsEnabled)
|
||||||
store.set(Double(soundVolume), forKey: Keys.soundVolume)
|
store.set(Double(soundVolume), forKey: Keys.soundVolume)
|
||||||
|
store.set(revealStyle.rawValue, forKey: Keys.revealStyle)
|
||||||
store.synchronize()
|
store.synchronize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,6 +395,7 @@ final class GameSettings: GameSettingsProtocol {
|
|||||||
soundEnabled = true
|
soundEnabled = true
|
||||||
hapticsEnabled = true
|
hapticsEnabled = true
|
||||||
soundVolume = 1.0
|
soundVolume = 1.0
|
||||||
|
revealStyle = .auto
|
||||||
save()
|
save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -723,6 +723,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Auto-Flip" : {
|
||||||
|
"comment" : "Name of the card reveal style that reveals cards one by one automatically.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Avoid the Tie bet — 14.4% house edge!" : {
|
"Avoid the Tie bet — 14.4% house edge!" : {
|
||||||
"comment" : "Tip for avoiding the Tie bet in baccarat, highlighting its low house edge.",
|
"comment" : "Tip for avoiding the Tie bet in baccarat, highlighting its low house edge.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -1158,6 +1162,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"Batch Reveal" : {
|
||||||
|
|
||||||
},
|
},
|
||||||
"Bet on Player, Banker, or Tie" : {
|
"Bet on Player, Banker, or Tie" : {
|
||||||
"comment" : "Welcome screen feature title for betting options.",
|
"comment" : "Welcome screen feature title for betting options.",
|
||||||
@ -1528,6 +1535,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Classic sequential reveal" : {
|
||||||
|
"comment" : "Description of the \"Auto-Flip\" reveal method.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Clear" : {
|
"Clear" : {
|
||||||
"comment" : "The label of a button that clears all current bets in the game.",
|
"comment" : "The label of a button that clears all current bets in the game.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -3228,6 +3239,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"PEEL" : {
|
||||||
|
"comment" : "Text shown on the card when it's in the interactive reveal state, instructing them to peel the card.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Player" : {
|
"Player" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
"en" : {
|
"en" : {
|
||||||
@ -3665,6 +3680,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"REVEAL STYLE" : {
|
||||||
|
"comment" : "The title of the section in the settings view related to the reveal style.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Road maps show game history and trends" : {
|
"Road maps show game history and trends" : {
|
||||||
"comment" : "Welcome screen feature description for pattern tracking.",
|
"comment" : "Welcome screen feature description for pattern tracking.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -4218,6 +4237,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Squeeze/Peel" : {
|
||||||
|
"comment" : "Description of the \"Squeeze/Peel\" reveal style.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"STARTING BALANCE" : {
|
"STARTING BALANCE" : {
|
||||||
"comment" : "Section header for starting balance settings.",
|
"comment" : "Section header for starting balance settings.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -4423,6 +4446,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"TAP" : {
|
||||||
|
"comment" : "Text displayed as an overlay on the card during the interactive reveal state, instructing the user to tap.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Tap Deal to start the round" : {
|
"Tap Deal to start the round" : {
|
||||||
"comment" : "Instructional text for new players.",
|
"comment" : "Instructional text for new players.",
|
||||||
"extractionState" : "stale",
|
"extractionState" : "stale",
|
||||||
@ -4447,6 +4474,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Tap each card to reveal it" : {
|
||||||
|
"comment" : "Text describing how to reveal cards in the \"Tap-to-Reveal\" reveal style.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"Tap-to-Reveal" : {
|
||||||
|
"comment" : "Name of the card reveal style where players tap each card to reveal it.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
|
"The authentic high-stakes ritual" : {
|
||||||
|
"comment" : "Description of the \"Squeeze/Peel\" reveal style.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"The hand closest to 9 wins" : {
|
"The hand closest to 9 wins" : {
|
||||||
"comment" : "Welcome screen feature description for betting explanation.",
|
"comment" : "Welcome screen feature description for betting explanation.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
@ -4791,6 +4830,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Top position cards together, then Bottom" : {
|
||||||
|
"comment" : "Description of the \"Batch Reveal\" reveal style.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
|
},
|
||||||
"Total game time" : {
|
"Total game time" : {
|
||||||
"comment" : "Rows in the \"Game stats\" section of the statistics sheet, showing various statistics about a Baccarat session.",
|
"comment" : "Rows in the \"Game stats\" section of the statistics sheet, showing various statistics about a Baccarat session.",
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|||||||
@ -305,12 +305,18 @@ struct GameTableView: View, SherpaDelegate {
|
|||||||
isTie: isTie,
|
isTie: isTie,
|
||||||
showAnimations: settings.showAnimations,
|
showAnimations: settings.showAnimations,
|
||||||
dealingSpeed: settings.dealingSpeed,
|
dealingSpeed: settings.dealingSpeed,
|
||||||
|
revealStyle: settings.revealStyle,
|
||||||
|
isWaitingForReveal: state.isWaitingForReveal,
|
||||||
|
currentRevealIndex: state.currentRevealIndex,
|
||||||
|
revealProgress: state.revealProgress,
|
||||||
bettedOnPlayer: state.bettedOnPlayer,
|
bettedOnPlayer: state.bettedOnPlayer,
|
||||||
isDealing: isDealing,
|
isDealing: isDealing,
|
||||||
screenSize: screenSize
|
screenSize: screenSize,
|
||||||
|
onReveal: { state.revealCurrentCard() },
|
||||||
|
onUpdateProgress: { state.updateRevealProgress($0) }
|
||||||
)
|
)
|
||||||
.frame(maxWidth: maxContentWidth)
|
.frame(maxWidth: maxContentWidth)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, isDealing ? 0 : Design.Spacing.medium)
|
||||||
.debugBorder(showDebugBorders, color: .red, label: "CardsArea")
|
.debugBorder(showDebugBorders, color: .red, label: "CardsArea")
|
||||||
|
|
||||||
Spacer(minLength: 0)
|
Spacer(minLength: 0)
|
||||||
@ -322,6 +328,9 @@ struct GameTableView: View, SherpaDelegate {
|
|||||||
)
|
)
|
||||||
.frame(maxWidth: maxContentWidth)
|
.frame(maxWidth: maxContentWidth)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
|
.opacity(isDealing ? (state.isWaitingForReveal ? 0.1 : 0.3) : 1.0)
|
||||||
|
.blur(radius: isDealing ? (state.isWaitingForReveal ? 3 : 1.5) : 0)
|
||||||
|
.scaleEffect(isDealing && state.isWaitingForReveal ? 0.95 : 1.0)
|
||||||
.debugBorder(showDebugBorders, color: .blue, label: "BetTable")
|
.debugBorder(showDebugBorders, color: .blue, label: "BetTable")
|
||||||
|
|
||||||
// Betting hint (static, below table, above chips)
|
// Betting hint (static, below table, above chips)
|
||||||
@ -408,12 +417,18 @@ struct GameTableView: View, SherpaDelegate {
|
|||||||
isTie: isTie,
|
isTie: isTie,
|
||||||
showAnimations: settings.showAnimations,
|
showAnimations: settings.showAnimations,
|
||||||
dealingSpeed: settings.dealingSpeed,
|
dealingSpeed: settings.dealingSpeed,
|
||||||
|
revealStyle: settings.revealStyle,
|
||||||
|
isWaitingForReveal: state.isWaitingForReveal,
|
||||||
|
currentRevealIndex: state.currentRevealIndex,
|
||||||
|
revealProgress: state.revealProgress,
|
||||||
bettedOnPlayer: state.bettedOnPlayer,
|
bettedOnPlayer: state.bettedOnPlayer,
|
||||||
isDealing: isDealing,
|
isDealing: isDealing,
|
||||||
screenSize: screenSize
|
screenSize: screenSize,
|
||||||
|
onReveal: { state.revealCurrentCard() },
|
||||||
|
onUpdateProgress: { state.updateRevealProgress($0) }
|
||||||
)
|
)
|
||||||
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, isDealing ? 0 : Design.Spacing.medium)
|
||||||
.debugBorder(showDebugBorders, color: .red, label: "CardsArea")
|
.debugBorder(showDebugBorders, color: .red, label: "CardsArea")
|
||||||
|
|
||||||
Spacer(minLength: smallSpacerHeight)
|
Spacer(minLength: smallSpacerHeight)
|
||||||
@ -438,6 +453,9 @@ struct GameTableView: View, SherpaDelegate {
|
|||||||
)
|
)
|
||||||
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
.frame(maxWidth: isLargeScreen ? maxContentWidth : .infinity)
|
||||||
.padding(.horizontal, Design.Spacing.medium)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
|
.opacity(isDealing ? (state.isWaitingForReveal ? 0.1 : 0.3) : 1.0)
|
||||||
|
.blur(radius: isDealing ? (state.isWaitingForReveal ? 3 : 1.5) : 0)
|
||||||
|
.scaleEffect(isDealing && state.isWaitingForReveal ? 0.95 : 1.0)
|
||||||
.debugBorder(showDebugBorders, color: .blue, label: "BetTable")
|
.debugBorder(showDebugBorders, color: .blue, label: "BetTable")
|
||||||
|
|
||||||
// Betting hint (static, below table, above chips)
|
// Betting hint (static, below table, above chips)
|
||||||
|
|||||||
@ -71,6 +71,14 @@ struct SettingsView: View {
|
|||||||
.background(Color.white.opacity(Design.Opacity.subtle))
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
SpeedPicker(speed: $settings.dealingSpeed, accentColor: accent)
|
SpeedPicker(speed: $settings.dealingSpeed, accentColor: accent)
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.background(Color.white.opacity(Design.Opacity.subtle))
|
||||||
|
|
||||||
|
RevealStylePicker(selection: $settings.revealStyle, accentColor: accent)
|
||||||
|
.onChange(of: settings.revealStyle) { _, _ in
|
||||||
|
hasChanges = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
Divider()
|
||||||
@ -439,6 +447,35 @@ struct TableLimitsPicker: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - Reveal Style Picker (Baccarat-specific)
|
||||||
|
|
||||||
|
struct RevealStylePicker: View {
|
||||||
|
@Binding var selection: RevealStyle
|
||||||
|
let accentColor: Color
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading, spacing: Design.Spacing.small) {
|
||||||
|
Text(String(localized: "REVEAL STYLE"))
|
||||||
|
.font(.system(size: Design.BaseFontSize.small, weight: .bold))
|
||||||
|
.foregroundStyle(.white.opacity(Design.Opacity.medium))
|
||||||
|
.padding(.bottom, Design.Spacing.xxSmall)
|
||||||
|
|
||||||
|
VStack(spacing: Design.Spacing.small) {
|
||||||
|
ForEach(RevealStyle.allCases) { style in
|
||||||
|
SelectableRow(
|
||||||
|
title: style.displayName,
|
||||||
|
subtitle: style.description,
|
||||||
|
isSelected: selection == style,
|
||||||
|
accentColor: accentColor,
|
||||||
|
action: { selection = style }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.vertical, Design.Spacing.xSmall)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#Preview {
|
#Preview {
|
||||||
SettingsView(settings: GameSettings(), gameState: GameState()) { }
|
SettingsView(settings: GameSettings(), gameState: GameState()) { }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,12 +24,18 @@ struct CardsDisplayArea: View {
|
|||||||
let isTie: Bool
|
let isTie: Bool
|
||||||
let showAnimations: Bool
|
let showAnimations: Bool
|
||||||
let dealingSpeed: Double
|
let dealingSpeed: Double
|
||||||
|
let revealStyle: RevealStyle
|
||||||
|
let isWaitingForReveal: Bool
|
||||||
|
let currentRevealIndex: Int
|
||||||
|
let revealProgress: Double
|
||||||
/// Which main bet is placed - nil if no main bet, true if Player, false if Banker.
|
/// Which main bet is placed - nil if no main bet, true if Player, false if Banker.
|
||||||
let bettedOnPlayer: Bool?
|
let bettedOnPlayer: Bool?
|
||||||
/// Whether the game is in dealing/result phase (vertical layout) or betting phase (horizontal).
|
/// Whether the game is in dealing/result phase (vertical layout) or betting phase (horizontal).
|
||||||
let isDealing: Bool
|
let isDealing: Bool
|
||||||
/// Full screen size for calculating card width in dealing mode.
|
/// Full screen size for calculating card width in dealing mode.
|
||||||
let screenSize: CGSize
|
let screenSize: CGSize
|
||||||
|
let onReveal: () -> Void
|
||||||
|
let onUpdateProgress: (Double) -> Void
|
||||||
|
|
||||||
// MARK: - State
|
// MARK: - State
|
||||||
|
|
||||||
@ -195,7 +201,7 @@ struct CardsDisplayArea: View {
|
|||||||
)
|
)
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: Design.CornerRadius.xLarge)
|
RoundedRectangle(cornerRadius: Design.CornerRadius.xLarge)
|
||||||
.fill(Color.black.opacity(Design.Opacity.quarter))
|
.fill(Color.black.opacity(isWaitingForReveal ? 0.05 : Design.Opacity.quarter))
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
)
|
)
|
||||||
.debugBorder(showDebugBorders, color: .mint, label: "HandsContainer")
|
.debugBorder(showDebugBorders, color: .mint, label: "HandsContainer")
|
||||||
@ -238,10 +244,17 @@ struct CardsDisplayArea: View {
|
|||||||
isWinner: playerIsWinner,
|
isWinner: playerIsWinner,
|
||||||
containerWidth: width,
|
containerWidth: width,
|
||||||
showAnimations: showAnimations,
|
showAnimations: showAnimations,
|
||||||
dealingSpeed: dealingSpeed
|
dealingSpeed: dealingSpeed,
|
||||||
|
revealStyle: revealStyle,
|
||||||
|
isWaitingForReveal: isWaitingForReveal,
|
||||||
|
currentRevealIndex: currentRevealIndex,
|
||||||
|
revealProgress: revealProgress,
|
||||||
|
isPlayerHand: true,
|
||||||
|
onReveal: onReveal,
|
||||||
|
onUpdateProgress: onUpdateProgress
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.frame(width: width)
|
.frame(maxWidth: isDealing ? .infinity : width)
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(String(localized: "Player hand"))
|
.accessibilityLabel(String(localized: "Player hand"))
|
||||||
.accessibilityValue(playerHandDescription + (playerIsWinner ? ", " + String(localized: "Winner") : ""))
|
.accessibilityValue(playerHandDescription + (playerIsWinner ? ", " + String(localized: "Winner") : ""))
|
||||||
@ -280,10 +293,17 @@ struct CardsDisplayArea: View {
|
|||||||
isWinner: bankerIsWinner,
|
isWinner: bankerIsWinner,
|
||||||
containerWidth: width,
|
containerWidth: width,
|
||||||
showAnimations: showAnimations,
|
showAnimations: showAnimations,
|
||||||
dealingSpeed: dealingSpeed
|
dealingSpeed: dealingSpeed,
|
||||||
|
revealStyle: revealStyle,
|
||||||
|
isWaitingForReveal: isWaitingForReveal,
|
||||||
|
currentRevealIndex: currentRevealIndex,
|
||||||
|
revealProgress: revealProgress,
|
||||||
|
isPlayerHand: false,
|
||||||
|
onReveal: onReveal,
|
||||||
|
onUpdateProgress: onUpdateProgress
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.frame(width: width)
|
.frame(maxWidth: isDealing ? .infinity : width)
|
||||||
.accessibilityElement(children: .ignore)
|
.accessibilityElement(children: .ignore)
|
||||||
.accessibilityLabel(String(localized: "Banker hand"))
|
.accessibilityLabel(String(localized: "Banker hand"))
|
||||||
.accessibilityValue(bankerHandDescription + (bankerIsWinner ? ", " + String(localized: "Winner") : ""))
|
.accessibilityValue(bankerHandDescription + (bankerIsWinner ? ", " + String(localized: "Winner") : ""))
|
||||||
@ -307,9 +327,15 @@ struct CardsDisplayArea: View {
|
|||||||
isTie: false,
|
isTie: false,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0,
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
bettedOnPlayer: nil,
|
bettedOnPlayer: nil,
|
||||||
isDealing: false,
|
isDealing: false,
|
||||||
screenSize: CGSize(width: 400, height: 800)
|
screenSize: CGSize(width: 400, height: 800),
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,9 +361,15 @@ struct CardsDisplayArea: View {
|
|||||||
isTie: false,
|
isTie: false,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0,
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
bettedOnPlayer: true,
|
bettedOnPlayer: true,
|
||||||
isDealing: true,
|
isDealing: true,
|
||||||
screenSize: CGSize(width: 400, height: 800)
|
screenSize: CGSize(width: 400, height: 800),
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,9 +397,15 @@ struct CardsDisplayArea: View {
|
|||||||
isTie: false,
|
isTie: false,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0,
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
bettedOnPlayer: false,
|
bettedOnPlayer: false,
|
||||||
isDealing: true,
|
isDealing: true,
|
||||||
screenSize: CGSize(width: 400, height: 800)
|
screenSize: CGSize(width: 400, height: 800),
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,10 +15,16 @@ struct CompactHandView: View {
|
|||||||
let isWinner: Bool
|
let isWinner: Bool
|
||||||
/// Container width passed from parent for sizing
|
/// Container width passed from parent for sizing
|
||||||
let containerWidth: CGFloat
|
let containerWidth: CGFloat
|
||||||
/// Whether to show dealing animations
|
|
||||||
let showAnimations: Bool
|
let showAnimations: Bool
|
||||||
/// Speed multiplier for dealing animations
|
|
||||||
let dealingSpeed: Double
|
let dealingSpeed: Double
|
||||||
|
let revealStyle: RevealStyle
|
||||||
|
let isWaitingForReveal: Bool
|
||||||
|
let currentRevealIndex: Int
|
||||||
|
let revealProgress: Double
|
||||||
|
/// Whether this is the player hand (used to calculate reveal index)
|
||||||
|
let isPlayerHand: Bool
|
||||||
|
let onReveal: () -> Void
|
||||||
|
let onUpdateProgress: (Double) -> Void
|
||||||
|
|
||||||
// MARK: - Environment
|
// MARK: - Environment
|
||||||
|
|
||||||
@ -35,6 +41,9 @@ struct CompactHandView: View {
|
|||||||
/// Placeholder spacing when no cards
|
/// Placeholder spacing when no cards
|
||||||
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
private let placeholderSpacing: CGFloat = Design.Spacing.small
|
||||||
|
|
||||||
|
/// Spacing for interactive layout
|
||||||
|
private let spacedGap: CGFloat = Design.Spacing.medium
|
||||||
|
|
||||||
// MARK: - Computed Properties
|
// MARK: - Computed Properties
|
||||||
|
|
||||||
/// Whether we're on a large screen (iPad)
|
/// Whether we're on a large screen (iPad)
|
||||||
@ -51,7 +60,9 @@ struct CompactHandView: View {
|
|||||||
/// Formula: containerWidth = cardWidth + (cardWidth + overlap) * 2
|
/// Formula: containerWidth = cardWidth + (cardWidth + overlap) * 2
|
||||||
/// Where overlap = cardWidth * overlapRatio
|
/// Where overlap = cardWidth * overlapRatio
|
||||||
private var cardWidth: CGFloat {
|
private var cardWidth: CGFloat {
|
||||||
let divisor = 1 + CGFloat(maxCards - 1) * (1 + overlapRatio)
|
// Use a fixed overlap ratio for sizing to keep cards large
|
||||||
|
let baseOverlapRatio: CGFloat = -0.45
|
||||||
|
let divisor = 1 + CGFloat(maxCards - 1) * (1 + baseOverlapRatio)
|
||||||
return containerWidth / divisor
|
return containerWidth / divisor
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,11 +76,76 @@ struct CompactHandView: View {
|
|||||||
cardWidth * CasinoDesign.Size.cardAspectRatio
|
cardWidth * CasinoDesign.Size.cardAspectRatio
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether to use a spaced layout for interaction
|
||||||
|
private var useSpacedLayout: Bool {
|
||||||
|
revealStyle == .tap || revealStyle == .squeeze
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The effective spacing for the card stack
|
||||||
|
private var effectiveSpacing: CGFloat {
|
||||||
|
if cards.isEmpty {
|
||||||
|
return placeholderSpacing
|
||||||
|
} else if useSpacedLayout {
|
||||||
|
return spacedGap
|
||||||
|
} else {
|
||||||
|
return cardOverlap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Total width required for the spaced layout
|
||||||
|
private var totalSpacedWidth: CGFloat {
|
||||||
|
let count = CGFloat(max(cards.count, 2))
|
||||||
|
return (count * cardWidth) + ((count - 1) * effectiveSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Body
|
// MARK: - Body
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
GeometryReader { geometry in
|
||||||
|
let availableWidth = geometry.size.width
|
||||||
|
|
||||||
|
Group {
|
||||||
|
if useSpacedLayout {
|
||||||
|
// Always use ScrollView in interactive modes for stable view hierarchy
|
||||||
|
ScrollViewReader { proxy in
|
||||||
|
ScrollView(.horizontal, showsIndicators: false) {
|
||||||
cardsContent
|
cardsContent
|
||||||
.frame(width: containerWidth, height: cardHeight)
|
.padding(.horizontal, Design.Spacing.medium)
|
||||||
|
.frame(minWidth: availableWidth, alignment: .center)
|
||||||
|
.id("cards_container")
|
||||||
|
}
|
||||||
|
.scrollDisabled(cards.count < 3)
|
||||||
|
.scrollClipDisabled(true) // Prevent clipping during deal animations
|
||||||
|
.background(Color.clear)
|
||||||
|
.onChange(of: cards.count) { _, newCount in
|
||||||
|
// When 3rd card is dealt, wait for animation then scroll
|
||||||
|
if newCount == 3 {
|
||||||
|
let lastIndex = isPlayerHand ? 4 : 5
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.8) {
|
||||||
|
withAnimation(.spring(duration: 0.5)) {
|
||||||
|
proxy.scrollTo(lastIndex, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: currentRevealIndex) { _, newIndex in
|
||||||
|
// Scroll to the active card during interaction
|
||||||
|
if newIndex >= 4 {
|
||||||
|
withAnimation(.spring(duration: 0.5)) {
|
||||||
|
proxy.scrollTo(newIndex, anchor: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Regular centered layout for non-interactive modes
|
||||||
|
cardsContent
|
||||||
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(height: cardHeight)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
.background(winnerBorder)
|
.background(winnerBorder)
|
||||||
.overlay(alignment: .bottom) {
|
.overlay(alignment: .bottom) {
|
||||||
winBadge
|
winBadge
|
||||||
@ -79,7 +155,7 @@ struct CompactHandView: View {
|
|||||||
// MARK: - Private Views
|
// MARK: - Private Views
|
||||||
|
|
||||||
private var cardsContent: some View {
|
private var cardsContent: some View {
|
||||||
HStack(spacing: cards.isEmpty ? placeholderSpacing : cardOverlap) {
|
HStack(spacing: effectiveSpacing) {
|
||||||
if cards.isEmpty {
|
if cards.isEmpty {
|
||||||
// Placeholders - no overlap, just side by side
|
// Placeholders - no overlap, just side by side
|
||||||
ForEach(0..<2, id: \.self) { _ in
|
ForEach(0..<2, id: \.self) { _ in
|
||||||
@ -88,11 +164,28 @@ struct CompactHandView: View {
|
|||||||
} else {
|
} else {
|
||||||
ForEach(cards.indices, id: \.self) { index in
|
ForEach(cards.indices, id: \.self) { index in
|
||||||
let isFaceUp = index < cardsFaceUp.count ? cardsFaceUp[index] : false
|
let isFaceUp = index < cardsFaceUp.count ? cardsFaceUp[index] : false
|
||||||
CardView(
|
|
||||||
|
// Reveal Index logic:
|
||||||
|
// P1=0, B1=1, P2=2, B2=3, P3=4, B3=5
|
||||||
|
let revealIndex: Int = {
|
||||||
|
if index < 2 {
|
||||||
|
return isPlayerHand ? index * 2 : index * 2 + 1
|
||||||
|
} else {
|
||||||
|
return isPlayerHand ? 4 : 5
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
InteractiveCardView(
|
||||||
card: cards[index],
|
card: cards[index],
|
||||||
isFaceUp: isFaceUp,
|
isFaceUp: isFaceUp,
|
||||||
cardWidth: cardWidth
|
cardWidth: cardWidth,
|
||||||
|
revealStyle: revealStyle,
|
||||||
|
isWaiting: isWaitingForReveal && currentRevealIndex == revealIndex,
|
||||||
|
progress: currentRevealIndex == revealIndex ? revealProgress : 0.0,
|
||||||
|
onReveal: onReveal,
|
||||||
|
onUpdateProgress: onUpdateProgress
|
||||||
)
|
)
|
||||||
|
.id(revealIndex)
|
||||||
.zIndex(Double(index))
|
.zIndex(Double(index))
|
||||||
.transition(
|
.transition(
|
||||||
showAnimations
|
showAnimations
|
||||||
@ -151,7 +244,14 @@ struct CompactHandView: View {
|
|||||||
isWinner: false,
|
isWinner: false,
|
||||||
containerWidth: 160,
|
containerWidth: 160,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
|
isPlayerHand: true,
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +268,14 @@ struct CompactHandView: View {
|
|||||||
isWinner: false,
|
isWinner: false,
|
||||||
containerWidth: 160,
|
containerWidth: 160,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
|
isPlayerHand: true,
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +293,14 @@ struct CompactHandView: View {
|
|||||||
isWinner: true,
|
isWinner: true,
|
||||||
containerWidth: 160,
|
containerWidth: 160,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
|
isPlayerHand: true,
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,7 +317,14 @@ struct CompactHandView: View {
|
|||||||
isWinner: false,
|
isWinner: false,
|
||||||
containerWidth: 160,
|
containerWidth: 160,
|
||||||
showAnimations: true,
|
showAnimations: true,
|
||||||
dealingSpeed: 1.0
|
dealingSpeed: 1.0,
|
||||||
|
revealStyle: .auto,
|
||||||
|
isWaitingForReveal: false,
|
||||||
|
currentRevealIndex: 0,
|
||||||
|
revealProgress: 0,
|
||||||
|
isPlayerHand: true,
|
||||||
|
onReveal: {},
|
||||||
|
onUpdateProgress: { _ in }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
Baccarat/Baccarat/Views/Table/InteractiveCardView.swift
Normal file
79
Baccarat/Baccarat/Views/Table/InteractiveCardView.swift
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// InteractiveCardView.swift
|
||||||
|
// Baccarat
|
||||||
|
//
|
||||||
|
// A wrapper around card views that adds interaction logic for Tap and Squeeze reveal styles.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import CasinoKit
|
||||||
|
|
||||||
|
struct InteractiveCardView: View {
|
||||||
|
let card: Card
|
||||||
|
let isFaceUp: Bool
|
||||||
|
let cardWidth: CGFloat
|
||||||
|
let revealStyle: RevealStyle
|
||||||
|
let isWaiting: Bool
|
||||||
|
let progress: Double
|
||||||
|
let onReveal: () -> Void
|
||||||
|
let onUpdateProgress: (Double) -> Void
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
if revealStyle == .squeeze && !isFaceUp && isWaiting {
|
||||||
|
// Specialized squeeze view
|
||||||
|
SqueezeCardView(card: card, width: cardWidth, progress: progress)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
.gesture(
|
||||||
|
DragGesture(minimumDistance: 0)
|
||||||
|
.onChanged { value in
|
||||||
|
let dx = abs(value.translation.width)
|
||||||
|
let dy = abs(value.translation.height)
|
||||||
|
let distance = sqrt(dx*dx + dy*dy)
|
||||||
|
let maxDistance = cardWidth * 1.0
|
||||||
|
let newProgress = min(1.0, max(progress, Double(distance / maxDistance)))
|
||||||
|
onUpdateProgress(newProgress)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.overlay {
|
||||||
|
if progress < 0.05 {
|
||||||
|
revealInstructionOverlay(text: String(localized: "PEEL"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Standard CardView (handles its own flip animation)
|
||||||
|
CardView(card: card, isFaceUp: isFaceUp, cardWidth: cardWidth)
|
||||||
|
.overlay {
|
||||||
|
if !isFaceUp && isWaiting && revealStyle == .tap {
|
||||||
|
revealInstructionOverlay(text: String(localized: "TAP"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
if !isFaceUp && isWaiting && revealStyle == .tap {
|
||||||
|
onReveal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func revealInstructionOverlay(text: String) -> some View {
|
||||||
|
Text(text)
|
||||||
|
.font(.system(size: Design.BaseFontSize.small, weight: .black, design: .rounded))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.padding(.horizontal, Design.Spacing.small)
|
||||||
|
.padding(.vertical, Design.Spacing.xxSmall)
|
||||||
|
.background(
|
||||||
|
Capsule()
|
||||||
|
.fill(Color.black.opacity(Design.Opacity.heavy))
|
||||||
|
.overlay(
|
||||||
|
Capsule()
|
||||||
|
.strokeBorder(.white.opacity(Design.Opacity.medium), lineWidth: 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.scaleEffect(isWaiting ? 1.0 : 0.8)
|
||||||
|
.opacity(isWaiting ? 1.0 : 0)
|
||||||
|
.animation(.interactiveSpring().repeatForever(), value: isWaiting)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
76
Baccarat/Baccarat/Views/Table/SqueezeCardView.swift
Normal file
76
Baccarat/Baccarat/Views/Table/SqueezeCardView.swift
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// SqueezeCardView.swift
|
||||||
|
// Baccarat
|
||||||
|
//
|
||||||
|
// A premium card view that allows for a gestural "squeeze" or "peel" reveal.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import CasinoKit
|
||||||
|
|
||||||
|
struct SqueezeCardView: View {
|
||||||
|
let card: Card
|
||||||
|
let width: CGFloat
|
||||||
|
let progress: Double // 0.0 (Back) to 1.0 (Front)
|
||||||
|
|
||||||
|
private var height: CGFloat {
|
||||||
|
width * CasinoDesign.Size.cardAspectRatio
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ZStack {
|
||||||
|
// The front is always underneath
|
||||||
|
CardFrontView(card: card, width: width, height: height)
|
||||||
|
|
||||||
|
// The back is on top and masked away as progress increases
|
||||||
|
CardBackView(width: width, height: height)
|
||||||
|
.mask(
|
||||||
|
SqueezeMask(progress: progress)
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
)
|
||||||
|
.shadow(
|
||||||
|
color: .black.opacity(Design.Opacity.medium * (1.0 - progress)),
|
||||||
|
radius: 2,
|
||||||
|
x: 2,
|
||||||
|
y: 2
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.frame(width: width, height: height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mask that peels away relative to progress.
|
||||||
|
struct SqueezeMask: Shape {
|
||||||
|
var progress: Double
|
||||||
|
|
||||||
|
var animatableData: Double {
|
||||||
|
get { progress }
|
||||||
|
set { progress = newValue }
|
||||||
|
}
|
||||||
|
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
var path = Path()
|
||||||
|
|
||||||
|
// We want the mask (the card back) to shrink towards the top-left
|
||||||
|
// as progress (the reveal) increases.
|
||||||
|
// A simple diagonal peel:
|
||||||
|
let p = CGFloat(1.0 - progress)
|
||||||
|
|
||||||
|
path.move(to: .zero)
|
||||||
|
path.addLine(to: CGPoint(x: rect.width * p * 1.5, y: 0))
|
||||||
|
path.addLine(to: CGPoint(x: 0, y: rect.height * p * 1.5))
|
||||||
|
path.closeSubpath()
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
VStack(spacing: 40) {
|
||||||
|
SqueezeCardView(card: Card(suit: .spades, rank: .ace), width: 150, progress: 0.2)
|
||||||
|
SqueezeCardView(card: Card(suit: .hearts, rank: .king), width: 150, progress: 0.5)
|
||||||
|
SqueezeCardView(card: Card(suit: .diamonds, rank: .seven), width: 150, progress: 0.8)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.background(Color.CasinoTable.backgroundDark)
|
||||||
|
}
|
||||||
583
CardSamples/CardSamples.xcodeproj/project.pbxproj
Normal file
583
CardSamples/CardSamples.xcodeproj/project.pbxproj
Normal file
@ -0,0 +1,583 @@
|
|||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 77;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
EAC0490D2F2173A0007F87EA /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = EAC048F72F21739F007F87EA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = EAC048FE2F21739F007F87EA;
|
||||||
|
remoteInfo = CardSamples;
|
||||||
|
};
|
||||||
|
EAC049172F2173A0007F87EA /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = EAC048F72F21739F007F87EA /* Project object */;
|
||||||
|
proxyType = 1;
|
||||||
|
remoteGlobalIDString = EAC048FE2F21739F007F87EA;
|
||||||
|
remoteInfo = CardSamples;
|
||||||
|
};
|
||||||
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
EAC048FF2F21739F007F87EA /* CardSamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CardSamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
EAC0490C2F2173A0007F87EA /* CardSamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CardSamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
EAC049162F2173A0007F87EA /* CardSamplesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CardSamplesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
EAC049012F21739F007F87EA /* CardSamples */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = CardSamples;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EAC0490F2F2173A0007F87EA /* CardSamplesTests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = CardSamplesTests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EAC049192F2173A0007F87EA /* CardSamplesUITests */ = {
|
||||||
|
isa = PBXFileSystemSynchronizedRootGroup;
|
||||||
|
path = CardSamplesUITests;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
EAC048FC2F21739F007F87EA /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC049092F2173A0007F87EA /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC049132F2173A0007F87EA /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
EAC048F62F21739F007F87EA = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EAC049012F21739F007F87EA /* CardSamples */,
|
||||||
|
EAC0490F2F2173A0007F87EA /* CardSamplesTests */,
|
||||||
|
EAC049192F2173A0007F87EA /* CardSamplesUITests */,
|
||||||
|
EAC049002F21739F007F87EA /* Products */,
|
||||||
|
);
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
EAC049002F21739F007F87EA /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
EAC048FF2F21739F007F87EA /* CardSamples.app */,
|
||||||
|
EAC0490C2F2173A0007F87EA /* CardSamplesTests.xctest */,
|
||||||
|
EAC049162F2173A0007F87EA /* CardSamplesUITests.xctest */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
EAC048FE2F21739F007F87EA /* CardSamples */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = EAC049202F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamples" */;
|
||||||
|
buildPhases = (
|
||||||
|
EAC048FB2F21739F007F87EA /* Sources */,
|
||||||
|
EAC048FC2F21739F007F87EA /* Frameworks */,
|
||||||
|
EAC048FD2F21739F007F87EA /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
EAC049012F21739F007F87EA /* CardSamples */,
|
||||||
|
);
|
||||||
|
name = CardSamples;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = CardSamples;
|
||||||
|
productReference = EAC048FF2F21739F007F87EA /* CardSamples.app */;
|
||||||
|
productType = "com.apple.product-type.application";
|
||||||
|
};
|
||||||
|
EAC0490B2F2173A0007F87EA /* CardSamplesTests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = EAC049232F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamplesTests" */;
|
||||||
|
buildPhases = (
|
||||||
|
EAC049082F2173A0007F87EA /* Sources */,
|
||||||
|
EAC049092F2173A0007F87EA /* Frameworks */,
|
||||||
|
EAC0490A2F2173A0007F87EA /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
EAC0490E2F2173A0007F87EA /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
EAC0490F2F2173A0007F87EA /* CardSamplesTests */,
|
||||||
|
);
|
||||||
|
name = CardSamplesTests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = CardSamplesTests;
|
||||||
|
productReference = EAC0490C2F2173A0007F87EA /* CardSamplesTests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.unit-test";
|
||||||
|
};
|
||||||
|
EAC049152F2173A0007F87EA /* CardSamplesUITests */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = EAC049262F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamplesUITests" */;
|
||||||
|
buildPhases = (
|
||||||
|
EAC049122F2173A0007F87EA /* Sources */,
|
||||||
|
EAC049132F2173A0007F87EA /* Frameworks */,
|
||||||
|
EAC049142F2173A0007F87EA /* Resources */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
EAC049182F2173A0007F87EA /* PBXTargetDependency */,
|
||||||
|
);
|
||||||
|
fileSystemSynchronizedGroups = (
|
||||||
|
EAC049192F2173A0007F87EA /* CardSamplesUITests */,
|
||||||
|
);
|
||||||
|
name = CardSamplesUITests;
|
||||||
|
packageProductDependencies = (
|
||||||
|
);
|
||||||
|
productName = CardSamplesUITests;
|
||||||
|
productReference = EAC049162F2173A0007F87EA /* CardSamplesUITests.xctest */;
|
||||||
|
productType = "com.apple.product-type.bundle.ui-testing";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
EAC048F72F21739F007F87EA /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = 1;
|
||||||
|
LastSwiftUpdateCheck = 2620;
|
||||||
|
LastUpgradeCheck = 2620;
|
||||||
|
TargetAttributes = {
|
||||||
|
EAC048FE2F21739F007F87EA = {
|
||||||
|
CreatedOnToolsVersion = 26.2;
|
||||||
|
};
|
||||||
|
EAC0490B2F2173A0007F87EA = {
|
||||||
|
CreatedOnToolsVersion = 26.2;
|
||||||
|
TestTargetID = EAC048FE2F21739F007F87EA;
|
||||||
|
};
|
||||||
|
EAC049152F2173A0007F87EA = {
|
||||||
|
CreatedOnToolsVersion = 26.2;
|
||||||
|
TestTargetID = EAC048FE2F21739F007F87EA;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = EAC048FA2F21739F007F87EA /* Build configuration list for PBXProject "CardSamples" */;
|
||||||
|
developmentRegion = en;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
Base,
|
||||||
|
);
|
||||||
|
mainGroup = EAC048F62F21739F007F87EA;
|
||||||
|
minimizedProjectReferenceProxies = 1;
|
||||||
|
preferredProjectObjectVersion = 77;
|
||||||
|
productRefGroup = EAC049002F21739F007F87EA /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
EAC048FE2F21739F007F87EA /* CardSamples */,
|
||||||
|
EAC0490B2F2173A0007F87EA /* CardSamplesTests */,
|
||||||
|
EAC049152F2173A0007F87EA /* CardSamplesUITests */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
|
EAC048FD2F21739F007F87EA /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC0490A2F2173A0007F87EA /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC049142F2173A0007F87EA /* Resources */ = {
|
||||||
|
isa = PBXResourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXResourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
EAC048FB2F21739F007F87EA /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC049082F2173A0007F87EA /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
EAC049122F2173A0007F87EA /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXTargetDependency section */
|
||||||
|
EAC0490E2F2173A0007F87EA /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = EAC048FE2F21739F007F87EA /* CardSamples */;
|
||||||
|
targetProxy = EAC0490D2F2173A0007F87EA /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
EAC049182F2173A0007F87EA /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = EAC048FE2F21739F007F87EA /* CardSamples */;
|
||||||
|
targetProxy = EAC049172F2173A0007F87EA /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
EAC0491E2F2173A0007F87EA /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"DEBUG=1",
|
||||||
|
"$(inherited)",
|
||||||
|
);
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
EAC0491F2F2173A0007F87EA /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||||
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
|
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||||
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
|
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||||
|
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||||
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_COMMA = YES;
|
||||||
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
|
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||||
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
|
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||||
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
|
COPY_PHASE_STRIP = NO;
|
||||||
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
EAC049212F2173A0007F87EA /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamples;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
EAC049222F2173A0007F87EA /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
ENABLE_PREVIEWS = YES;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||||
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamples;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
EAC049242F2173A0007F87EA /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamplesTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CardSamples.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CardSamples";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
EAC049252F2173A0007F87EA /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 26.2;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamplesTests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CardSamples.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/CardSamples";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
EAC049272F2173A0007F87EA /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamplesUITests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_TARGET_NAME = CardSamples;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
EAC049282F2173A0007F87EA /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_STYLE = Automatic;
|
||||||
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
|
DEVELOPMENT_TEAM = 6R7KLBPBLZ;
|
||||||
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
MARKETING_VERSION = 1.0;
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.mbrucedogs.CardSamplesUITests;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
|
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||||
|
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||||
|
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||||
|
SWIFT_VERSION = 5.0;
|
||||||
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
TEST_TARGET_NAME = CardSamples;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
EAC048FA2F21739F007F87EA /* Build configuration list for PBXProject "CardSamples" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
EAC0491E2F2173A0007F87EA /* Debug */,
|
||||||
|
EAC0491F2F2173A0007F87EA /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
EAC049202F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamples" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
EAC049212F2173A0007F87EA /* Debug */,
|
||||||
|
EAC049222F2173A0007F87EA /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
EAC049232F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamplesTests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
EAC049242F2173A0007F87EA /* Debug */,
|
||||||
|
EAC049252F2173A0007F87EA /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
EAC049262F2173A0007F87EA /* Build configuration list for PBXNativeTarget "CardSamplesUITests" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
EAC049272F2173A0007F87EA /* Debug */,
|
||||||
|
EAC049282F2173A0007F87EA /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = EAC048F72F21739F007F87EA /* Project object */;
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "dark"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"appearances" : [
|
||||||
|
{
|
||||||
|
"appearance" : "luminosity",
|
||||||
|
"value" : "tinted"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"idiom" : "universal",
|
||||||
|
"platform" : "ios",
|
||||||
|
"size" : "1024x1024"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
6
CardSamples/CardSamples/Assets.xcassets/Contents.json
Normal file
6
CardSamples/CardSamples/Assets.xcassets/Contents.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
17
CardSamples/CardSamples/CardSamplesApp.swift
Normal file
17
CardSamples/CardSamples/CardSamplesApp.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// CardSamplesApp.swift
|
||||||
|
// CardSamples
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/21/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct CardSamplesApp: App {
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
CardSamples/CardSamples/ContentView.swift
Normal file
24
CardSamples/CardSamples/ContentView.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// CardSamples
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/21/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Image(systemName: "globe")
|
||||||
|
.imageScale(.large)
|
||||||
|
.foregroundStyle(.tint)
|
||||||
|
Text("Hello, world!")
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
ContentView()
|
||||||
|
}
|
||||||
17
CardSamples/CardSamplesTests/CardSamplesTests.swift
Normal file
17
CardSamples/CardSamplesTests/CardSamplesTests.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// CardSamplesTests.swift
|
||||||
|
// CardSamplesTests
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/21/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Testing
|
||||||
|
@testable import CardSamples
|
||||||
|
|
||||||
|
struct CardSamplesTests {
|
||||||
|
|
||||||
|
@Test func example() async throws {
|
||||||
|
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
CardSamples/CardSamplesUITests/CardSamplesUITests.swift
Normal file
41
CardSamples/CardSamplesUITests/CardSamplesUITests.swift
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// CardSamplesUITests.swift
|
||||||
|
// CardSamplesUITests
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/21/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class CardSamplesUITests: XCTestCase {
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||||
|
continueAfterFailure = false
|
||||||
|
|
||||||
|
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testExample() throws {
|
||||||
|
// UI tests must launch the application that they test.
|
||||||
|
let app = XCUIApplication()
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testLaunchPerformance() throws {
|
||||||
|
// This measures how long it takes to launch your application.
|
||||||
|
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||||
|
XCUIApplication().launch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// CardSamplesUITestsLaunchTests.swift
|
||||||
|
// CardSamplesUITests
|
||||||
|
//
|
||||||
|
// Created by Matt Bruce on 1/21/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
final class CardSamplesUITestsLaunchTests: XCTestCase {
|
||||||
|
|
||||||
|
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
func testLaunch() throws {
|
||||||
|
let app = XCUIApplication()
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||||
|
// such as logging into a test account or navigating somewhere in the app
|
||||||
|
|
||||||
|
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||||
|
attachment.name = "Launch Screen"
|
||||||
|
attachment.lifetime = .keepAlways
|
||||||
|
add(attachment)
|
||||||
|
}
|
||||||
|
}
|
||||||
3
CasinoGames.xcworkspace/contents.xcworkspacedata
generated
3
CasinoGames.xcworkspace/contents.xcworkspacedata
generated
@ -1,6 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:CardSamples/CardSamples.xcodeproj">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "group:Baccarat/Baccarat.xcodeproj">
|
location = "group:Baccarat/Baccarat.xcodeproj">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user