React Native中Swift调用Apple Game Kit排行榜弹窗失败问题
解决React Native调用iOS GameKit排行榜时的视图层级错误
错误原因
你遇到的Attempt to present ... whose view is not in the window hierarchy错误,核心问题是:
- 你创建的
RNGameKit实例只是内存中的对象,它的视图从未被添加到当前App的窗口层级中 - 同时你在
showLeaderboards里冗余调用了show、pushViewController、present三种视图跳转方法,逻辑混乱
解决方案
1. 添加获取顶层ViewController的工具方法
首先需要获取当前App的活跃顶层ViewController,用它来展示GameCenter弹窗,而不是用未挂载的RNGameKit实例。在RNGameKit.swift中添加工具方法:
import Foundation import UIKit import GameKit import React @objc(RNGameKit) class RNGameKit : NSObject, GKGameCenterControllerDelegate { // 获取当前App的顶层ViewController private func getTopViewController() -> UIViewController? { guard let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .compactMap({$0 as? UIWindowScene}) .first?.windows .filter({$0.isKeyWindow}).first else { return nil } var topVC = keyWindow.rootViewController while let presentedVC = topVC?.presentedViewController { topVC = presentedVC } return topVC } func showLeaderboards() { guard let topVC = getTopViewController() else { print("无法获取可用于弹窗的顶层ViewController") return } let gcViewController = GKGameCenterViewController() gcViewController.gameCenterDelegate = self gcViewController.viewState = .leaderboards gcViewController.leaderboardIdentifier = "pointsLeaderboard" // 仅用顶层VC执行present操作 topVC.present(gcViewController, animated: true, completion: nil) } // 必须实现的GameCenter代理方法,用于关闭弹窗 func gameCenterViewControllerDidFinish(_ gameCenterViewController: GKGameCenterViewController) { gameCenterViewController.dismiss(animated: true, completion: nil) } }
2. 修正ViewManager的调用逻辑
不要在view()方法中创建未挂载的RNGameKit实例,直接在导出的方法中调用正确的逻辑:
import Foundation import React import GameKit @objc(CounterViewManager) class CounterViewManager: RCTViewManager { override static func requiresMainQueueSetup() -> Bool { return true } override func view() -> UIView! { let label = UILabel() label.text = "Swift Text" label.textAlignment = .center return label } @objc func authenticateUser() { // 保留你的认证逻辑 } @objc func showLeaderboards() { let gameKit = RNGameKit() gameKit.showLeaderboards() } }
3. 关键注意事项
- 不要用未添加到窗口层级的ViewController执行
present/push操作,必须确保用于弹窗的VC是当前活跃的顶层VC - 必须实现
GKGameCenterControllerDelegate的gameCenterViewControllerDidFinish方法,否则弹窗无法正常关闭 - 移除冗余的视图跳转代码,只保留一种符合需求的跳转方式(此处用
present即可)
内容的提问来源于stack exchange,提问作者asdkajd
相关产品推荐
相关产品推荐

