https://github.com/dmojdehi/SwiftGlobe/blob/master/src/SwiftGlobe.swift
檔案:Syncnext tvOS/App/PlayerKSView/Player/registerForGameControllerNotifications.swift
import GameController
class GameControllerObserver {
init() {
// 連線通知
NotificationCenter.default.addObserver(
self,
selector: #selector(handleControllerDidConnectNotification(notification:)),
name: NSNotification.Name.GCControllerDidConnect,
object: nil
)
// 斷線通知
NotificationCenter.default.addObserver(
self,
selector: #selector(handleControllerDidDisconnectNotification(notification:)),
name: NSNotification.Name.GCControllerDidDisconnect,
object: nil
)
}
@objc private func handleControllerDidConnectNotification(notification: NSNotification) {
// 取得已連線的 GCController
GameControllerStatus.gameController = notification.object as? GCController
// 依照產品類別決定型別
if let controller = GameControllerStatus.gameController {
switch controller.productCategory {
case "Siri Remote (1st Generation)":
GameControllerStatus.gameControllerType = .Remote1
// 需要取得 microGamepad 才能讀取 D‑Pad
controller.microGamepad?.reportsAbsoluteDpadValues = true
case "Siri Remote (2nd Generation)":
GameControllerStatus.gameControllerType = .Remote2
default:
// 其他類型(例如 MFi 控制器)可自行處理
GameControllerStatus.gameControllerType = .MFi
}
}
}
@objc private func handleControllerDidDisconnectNotification(notification: NSNotification) {
// 釋放資源或重置狀態
GameControllerStatus.gameController = nil
GameControllerStatus.gameControllerType = .Remote1 // 依需求調整
}
}
檔案:Syncnext tvOS/Status/Normal/GameControllerStatus.swift
import GameController
/// 用來全域保存目前連線中控制器的資訊
class GameControllerStatus {
/// 控制器的型別
enum GameControllerType { case MFi, Remote1, Remote2 }
/// 目前已連線的 GCController(可為 nil)
static var gameController: GCController?
/// 目前偵測到的型別,預設為 .Remote1(可自行調整預設)
static var gameControllerType: GameControllerType = .Remote1
}
檔案:Syncnext tvOS/App/PlayerKSView/Player/ExtensionRemoteController.swift
import UIKit
import GameController
class ExtensionRemoteController: UIViewController {
// … 其他程式碼 …
override func viewDidLoad() {
super.viewDidLoad()
// 依型別決定不同的按鍵處理方式
if GameControllerStatus.gameControllerType == .Remote1 {
// ==================== 傳統遙控器 ====================
// 需要在 pressesBegan / pressesEnded 裡自行解析按鍵
// 這裡只示範取得 D‑Pad 的值(見第 4 點)
} else {
// ==================== 新遙控器(Play/Pause) ====================
// 使用 UITapGestureRecognizer 只要支援 .playPause
let pressRecognizer = UITapGestureRecognizer()
pressRecognizer.allowedPressTypes = [NSNumber(value: UIPress.PressType.playPause.rawValue)]
view.addGestureRecognizer(pressRecognizer)
}
}
// MARK: - 例:處理 GCController 的按鍵
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let press = presses.first else { return }
switch press.type {
case .menu: // 例:菜單鍵
// 你的處理...
break
case .playPause: // 例:播放/暫停鍵
// 你的處理...
break
default:
break
}
}
}
檔案:同上(ExtensionRemoteController.swift)
// 只在是 Remote1 時才需要判斷 D‑Pad
if GameControllerStatus.gameControllerType == .Remote1,
let microGamepad = GameControllerStatus.gameController?.microGamepad,
let xAxis = microGamepad.dpad.xAxis.value {
// xAxis 介於 -1 ~ 1,超過阈值視為「按下」
if xAxis > 0.55 {
touchPressType = .touchRight // 向右按下
} else if xAxis < -0.55 {
touchPressType = .touchLeft // 向左按下
}
}
檔案:Syncnext tvOS/Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"<http://www.apple.com/DTDs/PropertyList-1.0.dtd>">
<plist version="1.0">
<dict>
<!-- 其他設定 ... -->
<!-- 支援的遊戲控制器 Profile -->
<key>GCSupportedGameControllers</key>
<array>
<!-- 1st‑Gen Siri Remote -->
<dict>
<key>ProfileName</key>
<string>MicroGamepad</string>
</dict>
<!-- 2nd‑Gen Siri Remote -->
<dict>
<key>ProfileName</key>
<string>ExtendedGamepad</string>
</dict>
<!-- 方向式遙控器(D‑Pad) -->
<dict>
<key>ProfileName</key>
<string>DirectionalGamepad</string>
</dict>
</array>
<!-- 其他設定 ... -->
</dict>
</plist>