You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 15:31:58