diff --git a/RollkofferSimulator/AppDelegate.swift b/RollkofferSimulator/AppDelegate.swift
index 5a9cbe4..85a1aea 100644
--- a/RollkofferSimulator/AppDelegate.swift
+++ b/RollkofferSimulator/AppDelegate.swift
@@ -14,6 +14,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ #if targetEnvironment(macCatalyst)
+ // Configure for macOS
+ configureMacOS()
+ #endif
return true
}
@@ -33,10 +37,60 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
func applicationDidBecomeActive(_ application: UIApplication) {
// Resume game if needed
}
+
+ #if targetEnvironment(macCatalyst)
+ // MARK: - macOS Configuration
+ private func configureMacOS() {
+ // Set minimum window size for macOS
+ UIApplication.shared.connectedScenes.compactMap { $0 as? UIWindowScene }.forEach { windowScene in
+ windowScene.sizeRestrictions?.minimumSize = CGSize(width: 400, height: 600)
+ windowScene.sizeRestrictions?.maximumSize = CGSize(width: 600, height: 900)
+ }
+ }
+
+ override func buildMenu(with builder: UIMenuBuilder) {
+ super.buildMenu(with: builder)
+
+ // Remove unnecessary menus for a game
+ builder.remove(menu: .format)
+ builder.remove(menu: .edit)
+
+ // Add Game menu
+ let pauseCommand = UIKeyCommand(
+ title: "Pause",
+ action: #selector(handlePauseCommand),
+ input: "p",
+ modifierFlags: .command
+ )
+
+ let restartCommand = UIKeyCommand(
+ title: "Neustart",
+ action: #selector(handleRestartCommand),
+ input: "r",
+ modifierFlags: .command
+ )
+
+ let gameMenu = UIMenu(
+ title: "Spiel",
+ children: [pauseCommand, restartCommand]
+ )
+
+ builder.insertSibling(gameMenu, afterMenu: .file)
+ }
+
+ @objc private func handlePauseCommand() {
+ NotificationCenter.default.post(name: .pauseGame, object: nil)
+ }
+
+ @objc private func handleRestartCommand() {
+ NotificationCenter.default.post(name: .restartGame, object: nil)
+ }
+ #endif
}
// MARK: - Notification Names
extension Notification.Name {
static let pauseGame = Notification.Name("pauseGame")
static let resumeGame = Notification.Name("resumeGame")
+ static let restartGame = Notification.Name("restartGame")
}
diff --git a/RollkofferSimulator/GameViewController.swift b/RollkofferSimulator/GameViewController.swift
index 9d0e5a3..f755f34 100644
--- a/RollkofferSimulator/GameViewController.swift
+++ b/RollkofferSimulator/GameViewController.swift
@@ -36,6 +36,10 @@ class GameViewController: UIViewController {
// Setup notification observers
setupNotificationObservers()
+
+ #if targetEnvironment(macCatalyst)
+ setupMacCatalyst()
+ #endif
}
private func setupNotificationObservers() {
@@ -45,6 +49,13 @@ class GameViewController: UIViewController {
name: .pauseGame,
object: nil
)
+
+ NotificationCenter.default.addObserver(
+ self,
+ selector: #selector(handleRestartNotification),
+ name: .restartGame,
+ object: nil
+ )
}
@objc private func handlePauseNotification() {
@@ -57,12 +68,46 @@ class GameViewController: UIViewController {
// This is just a notification that the app is going to background
}
+ @objc private func handleRestartNotification() {
+ guard let skView = self.view as? SKView else { return }
+
+ let menuScene = MenuScene(size: skView.bounds.size)
+ menuScene.scaleMode = .aspectFill
+
+ let transition = SKTransition.fade(withDuration: 0.5)
+ skView.presentScene(menuScene, transition: transition)
+ }
+
+ #if targetEnvironment(macCatalyst)
+ private func setupMacCatalyst() {
+ // Configure window appearance for macOS
+ if let windowScene = view.window?.windowScene {
+ windowScene.title = "Rollkoffer Simulator"
+
+ // Set window style
+ if let titlebar = windowScene.titlebar {
+ titlebar.titleVisibility = .visible
+ titlebar.toolbarStyle = .unified
+ }
+ }
+ }
+
+ // Enable keyboard input
+ override var canBecomeFirstResponder: Bool {
+ return true
+ }
+ #endif
+
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ #if targetEnvironment(macCatalyst)
+ return .all
+ #else
if UIDevice.current.userInterfaceIdiom == .phone {
return .portrait
} else {
return .all
}
+ #endif
}
override var prefersStatusBarHidden: Bool {
diff --git a/RollkofferSimulator/Info.plist b/RollkofferSimulator/Info.plist
index c05f1e7..8f5d56d 100644
--- a/RollkofferSimulator/Info.plist
+++ b/RollkofferSimulator/Info.plist
@@ -50,5 +50,9 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ LSMinimumSystemVersion
+ 13.0
+ NSHumanReadableCopyright
+ Copyright 2024 Ingo K. All rights reserved.
diff --git a/RollkofferSimulator/RollkofferSimulator.xcodeproj/project.pbxproj b/RollkofferSimulator/RollkofferSimulator.xcodeproj/project.pbxproj
index 0531fdd..2069378 100644
--- a/RollkofferSimulator/RollkofferSimulator.xcodeproj/project.pbxproj
+++ b/RollkofferSimulator/RollkofferSimulator.xcodeproj/project.pbxproj
@@ -381,6 +381,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
+ DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -393,9 +394,12 @@
"$(inherited)",
"@executable_path/Frameworks",
);
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ingok.RollkofferSimulator;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTS_MACCATALYST = YES;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -409,6 +413,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
+ DERIVE_MACCATALYST_PRODUCT_BUNDLE_IDENTIFIER = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
@@ -421,9 +426,12 @@
"$(inherited)",
"@executable_path/Frameworks",
);
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.ingok.RollkofferSimulator;
PRODUCT_NAME = "$(TARGET_NAME)";
+ SUPPORTS_MACCATALYST = YES;
+ SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
diff --git a/RollkofferSimulator/Scenes/GameOverScene.swift b/RollkofferSimulator/Scenes/GameOverScene.swift
index 6d66b8f..4d8b0c6 100644
--- a/RollkofferSimulator/Scenes/GameOverScene.swift
+++ b/RollkofferSimulator/Scenes/GameOverScene.swift
@@ -223,6 +223,27 @@ class GameOverScene: SKScene {
}
}
+ // MARK: - Keyboard Handling (macOS)
+ #if targetEnvironment(macCatalyst)
+ override var canBecomeFirstResponder: Bool { true }
+
+ override func pressesBegan(_ presses: Set, with event: UIPressesEvent?) {
+ guard let key = presses.first?.key else {
+ super.pressesBegan(presses, with: event)
+ return
+ }
+
+ switch key.keyCode {
+ case .keyboardSpacebar, .keyboardReturnOrEnter:
+ retryGame()
+ case .keyboardEscape:
+ returnToMenu()
+ default:
+ super.pressesBegan(presses, with: event)
+ }
+ }
+ #endif
+
private func retryGame() {
let pressDown = SKAction.scale(to: 0.9, duration: 0.1)
let pressUp = SKAction.scale(to: 1.0, duration: 0.1)
diff --git a/RollkofferSimulator/Scenes/GameScene.swift b/RollkofferSimulator/Scenes/GameScene.swift
index 1df64d7..7db94cf 100644
--- a/RollkofferSimulator/Scenes/GameScene.swift
+++ b/RollkofferSimulator/Scenes/GameScene.swift
@@ -318,6 +318,29 @@ class GameScene: SKScene {
isDragging = false
}
+ // MARK: - Keyboard Handling (macOS)
+ #if targetEnvironment(macCatalyst)
+ override var canBecomeFirstResponder: Bool { true }
+
+ override func pressesBegan(_ presses: Set, with event: UIPressesEvent?) {
+ guard let key = presses.first?.key else {
+ super.pressesBegan(presses, with: event)
+ return
+ }
+
+ switch key.keyCode {
+ case .keyboardEscape:
+ togglePause()
+ case .keyboardSpacebar:
+ if gameState.currentState == .paused {
+ resumeGame()
+ }
+ default:
+ super.pressesBegan(presses, with: event)
+ }
+ }
+ #endif
+
// MARK: - Pause Handling
private func togglePause() {
if gameState.currentState == .playing {
diff --git a/RollkofferSimulator/Scenes/MenuScene.swift b/RollkofferSimulator/Scenes/MenuScene.swift
index 7a650ce..074984e 100644
--- a/RollkofferSimulator/Scenes/MenuScene.swift
+++ b/RollkofferSimulator/Scenes/MenuScene.swift
@@ -245,6 +245,25 @@ class MenuScene: SKScene {
}
}
+ // MARK: - Keyboard Handling (macOS)
+ #if targetEnvironment(macCatalyst)
+ override var canBecomeFirstResponder: Bool { true }
+
+ override func pressesBegan(_ presses: Set, with event: UIPressesEvent?) {
+ guard let key = presses.first?.key else {
+ super.pressesBegan(presses, with: event)
+ return
+ }
+
+ switch key.keyCode {
+ case .keyboardSpacebar, .keyboardReturnOrEnter:
+ startGame()
+ default:
+ super.pressesBegan(presses, with: event)
+ }
+ }
+ #endif
+
private func startGame() {
// Button press effect
let pressDown = SKAction.scale(to: 0.9, duration: 0.1)
diff --git a/RollkofferSimulator/Scenes/VictoryScene.swift b/RollkofferSimulator/Scenes/VictoryScene.swift
index c6f4fa4..82910d9 100644
--- a/RollkofferSimulator/Scenes/VictoryScene.swift
+++ b/RollkofferSimulator/Scenes/VictoryScene.swift
@@ -280,6 +280,27 @@ class VictoryScene: SKScene {
}
}
+ // MARK: - Keyboard Handling (macOS)
+ #if targetEnvironment(macCatalyst)
+ override var canBecomeFirstResponder: Bool { true }
+
+ override func pressesBegan(_ presses: Set, with event: UIPressesEvent?) {
+ guard let key = presses.first?.key else {
+ super.pressesBegan(presses, with: event)
+ return
+ }
+
+ switch key.keyCode {
+ case .keyboardSpacebar, .keyboardReturnOrEnter:
+ playAgain()
+ case .keyboardEscape:
+ returnToMenu()
+ default:
+ super.pressesBegan(presses, with: event)
+ }
+ }
+ #endif
+
private func playAgain() {
let pressDown = SKAction.scale(to: 0.9, duration: 0.1)
let pressUp = SKAction.scale(to: 1.0, duration: 0.1)