Add macOS Catalyst support for RollkofferSimulator
- Enable Mac Catalyst in Xcode project (SUPPORTS_MACCATALYST=YES) - Set macOS deployment target to 13.0 (Ventura+) - Add keyboard support for all scenes (Escape, Space, Enter) - Add macOS menu bar with game commands (Cmd+P pause, Cmd+R restart) - Configure window size restrictions for macOS - Update Info.plist with macOS minimum version
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -50,5 +50,9 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>13.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright 2024 Ingo K. All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -223,6 +223,27 @@ class GameOverScene: SKScene {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Keyboard Handling (macOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
override var canBecomeFirstResponder: Bool { true }
|
||||
|
||||
override func pressesBegan(_ presses: Set<UIPress>, 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)
|
||||
|
||||
@@ -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<UIPress>, 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 {
|
||||
|
||||
@@ -245,6 +245,25 @@ class MenuScene: SKScene {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Keyboard Handling (macOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
override var canBecomeFirstResponder: Bool { true }
|
||||
|
||||
override func pressesBegan(_ presses: Set<UIPress>, 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)
|
||||
|
||||
@@ -280,6 +280,27 @@ class VictoryScene: SKScene {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Keyboard Handling (macOS)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
override var canBecomeFirstResponder: Bool { true }
|
||||
|
||||
override func pressesBegan(_ presses: Set<UIPress>, 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)
|
||||
|
||||
Reference in New Issue
Block a user