关于xcode:如何使用SwiftUI显示Game Center排行榜

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
            }
        }
    }
}