How to display Game Center leaderboard with SwiftUI
我创建了一个测试器应用,以测试将GameCenter排行榜添加到我正在创建的简单SwiftUI游戏中。我一直无法弄清楚如何显示所有分数的Game Center排行榜。
我创建了一个类,其中包含所有Game Center功能(身份验证和向排行榜添加得分。这是从主ContentView视图调用的。我不知道如何使它显示排行榜(甚至是游戏中心)登录屏幕(如果播放器尚未登录。)
这是我的GameCenterManager类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | class GameCenterManager { var gcEnabled = Bool() // Check if the user has Game Center enabled var gcDefaultLeaderBoard = String() // Check the default leaderboardID var score = 0 let LEADERBOARD_ID ="grp.colorMatcherLeaderBoard_1" //Leaderboard ID from Itunes Connect // MARK: - AUTHENTICATE LOCAL PLAYER func authenticateLocalPlayer() { let localPlayer: GKLocalPlayer = GKLocalPlayer.local localPlayer.authenticateHandler = {(ViewController, error) -> Void in if((ViewController) != nil) { print("User is not logged into game center") } else if (localPlayer.isAuthenticated) { // 2. Player is already authenticated & logged in, load game center self.gcEnabled = true // Get the default leaderboard ID localPlayer.loadDefaultLeaderboardIdentifier(completionHandler: { (leaderboardIdentifer, error) in if error != nil { print(error ??"error1") } else { self.gcDefaultLeaderBoard = leaderboardIdentifer! } }) print("Adding GameCenter user was a success") } else { // 3. Game center is not enabled on the users device self.gcEnabled = false print("Local player could not be authenticated!") print(error ??"error2") } } } //authenticateLocalPlayer() func submitScoreToGC(_ score: Int){ let bestScoreInt = GKScore(leaderboardIdentifier: LEADERBOARD_ID) bestScoreInt.value = Int64(score) GKScore.report([bestScoreInt]) { (error) in if error != nil { print(error!.localizedDescription) } else { print("Best Score submitted to your Leaderboard!") } } }//submitScoreToGc() } |
这是ContentView结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | struct ContentView: View { //GameCenter init() { self.gameCenter = GameCenterManager() self.gameCenter.authenticateLocalPlayer() } @State var score = 0 var gcEnabled = Bool() //Checks if the user had enabled GameCenter var gcDefaultLeaderboard = String() //Checks the default leaderboard ID let gameCenter: GameCenterManager /*End GameCenter Variables */ var body: some View { HStack { Text("Hello, world!") Button(action: { self.score += 1 print("Score increased by 10. It is now \\(self.score)") self.gameCenter.submitScoreToGC(self.score) }) { Text("Increase Score") } } } } |
将非常感谢您为解决此问题提供的任何帮助。
我有个解决方法。
我在我的SwiftUI App声音匹配器中成功使用了Game Center。要遵循的代码段。
该代码不完全遵循SwiftUI声明性原理,但是可以完美地工作。我将片段添加到SceneDelegate和ContentView中,并使用GameKitHelper类,类似于Thomas为他的测试应用程序创建的类。我的版本基于在raywenderlich.com上找到的代码。
我实际上是按照与bg2b相同的思路尝试使用符合UIViewControllerRepresentable的结构作为我的第一次尝试,但是它一直抱怨游戏中心视图控制器需要以模态显示。最终,我放弃了尝试,尝试了我目前更成功的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { let contentView = ContentView() .environmentObject(GameKitHelper.sharedInstance) // publish enabled state func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene { let window = UIWindow(windowScene: windowScene) window.rootViewController = UIHostingController(rootView: contentView) // new code to create listeners for the messages // you will be sending later PopupControllerMessage.PresentAuthentication .addHandlerForNotification( self, handler: #selector(SceneDelegate .showAuthenticationViewController)) PopupControllerMessage.GameCenter .addHandlerForNotification( self, handler: #selector(SceneDelegate .showGameCenterViewController)) // now we are back to the standard template // generated when your project was created self.window = window window.makeKeyAndVisible() } } // pop's up the leaderboard and achievement screen @objc func showGameCenterViewController() { if let gameCenterViewController = GameKitHelper.sharedInstance.gameCenterViewController { self.window?.rootViewController?.present( gameCenterViewController, animated: true, completion: nil) } } // pop's up the authentication screen @objc func showAuthenticationViewController() { if let authenticationViewController = GameKitHelper.sharedInstance.authenticationViewController { self.window?.rootViewController?.present( authenticationViewController, animated: true) { GameKitHelper.sharedInstance.enabled = GameKitHelper.sharedInstance.gameCenterEnabled } } } } // content you want your app to display goes here struct ContentView: View { @EnvironmentObject var gameCenter : GameKitHelper @State private var isShowingGameCenter = false { didSet { PopupControllerMessage .GameCenter .postNotification() }} var body: some View { VStack { if self.gameCenter.enabled { Button(action:{ self.isShowingGameCenter.toggle()}) { Text( "Press to show leaderboards and achievements")} } // The authentication popup will appear when you first enter // the view }.onAppear() {GameKitHelper.sharedInstance .authenticateLocalPlayer()} } } import GameKit import UIKit // Messages sent using the Notification Center to trigger // Game Center's Popup screen public enum PopupControllerMessage : String { case PresentAuthentication ="PresentAuthenticationViewController" case GameCenter ="GameCenterViewController" } extension PopupControllerMessage { public func postNotification() { NotificationCenter.default.post( name: Notification.Name(rawValue: self.rawValue), object: self) } public func addHandlerForNotification(_ observer: Any, handler: Selector) { NotificationCenter.default . addObserver(observer, selector: handler, name: NSNotification.Name(rawValue: self.rawValue), object: nil) } } // based on code from raywenderlich.com // helper class to make interacting with the Game Center easier open class GameKitHelper: NSObject, ObservableObject, GKGameCenterControllerDelegate { public var authenticationViewController: UIViewController? public var lastError: Error? private static let _singleton = GameKitHelper() public class var sharedInstance: GameKitHelper { return GameKitHelper._singleton } private override init() { super.init() } @Published public var enabled :Bool = false public var gameCenterEnabled : Bool { return GKLocalPlayer.local.isAuthenticated } public func authenticateLocalPlayer () { let localPlayer = GKLocalPlayer.local localPlayer.authenticateHandler = {(viewController, error) in self.lastError = error as NSError? self.enabled = GKLocalPlayer.local.isAuthenticated if viewController != nil { self.authenticationViewController = viewController PopupControllerMessage .PresentAuthentication .postNotification() } } } public var gameCenterViewController : GKGameCenterViewController? { get { guard gameCenterEnabled else { print("Local player is not authenticated") return nil } let gameCenterViewController = GKGameCenterViewController() gameCenterViewController.gameCenterDelegate = self gameCenterViewController.viewState = .achievements return gameCenterViewController }} open func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) { gameCenterViewController.dismiss( animated: true, completion: nil) } } |
在这里为您提供部分答案。我可以下载排行榜得分并将其显示在SwiftUI列表中,前提是设备(或模拟器)已登录到iCloud,并且已经在设置中启用了GameCenter。如果不是这种情况,我没有尝试显示gameCenter身份验证视图控制器。
感谢您提出问题的代码。我使用了GameCenterManager(),但将其放入了我的AppDelegate:
1 | let gameCenter = GameCenterManager() |
下面是我的ShowRankings.swift SwiftUI视图。我能够成功进行身份验证并获得分数。但是我仍然有"异常"。第一次运行此程序(在模拟器中)时,出现预期的"用户未登录Game Center"错误,指示GameCenterManager中的ViewController不为零(我什至从未尝试显示它)。但是之后我就可以成功获得分数并将它们显示在列表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | import SwiftUI import GameKit struct ShowRankings: View { let appDelegate = UIApplication.shared.delegate as! AppDelegate let leaderBoard = GKLeaderboard() @State var scores: [GKScore] = [] var body: some View { VStack { Button(action: { self.updateLeader() }) { Text("Refresh leaderboard") } List(scores, id: \\.self) { score in Text("\\(score.player.alias) \\(score.value)") } }.onAppear() { self.appDelegate.gameCenter.authenticateLocalPlayer() self.updateLeader() } } func updateLeader() { let leaderBoard: GKLeaderboard = GKLeaderboard() leaderBoard.identifier ="YOUR_LEADERBOARD_ID_HERE" leaderBoard.timeScope = .allTime leaderBoard.loadScores { (scores, error) in if let error = error { debugPrint("leaderboard loadScores error \\(error)") } else { guard let scores = scores else { return } self.scores = scores } } } } |