Reputation: 21730
I have a SwiftUI app that shows a GKGameCenterViewController
. I am hitting a problem which is specific to iPads running iPadOS 16.1 and above in light mode only. Under these circumstances, the child views of GKGameCenterViewController
appear as black text on a dark background, which is very difficult to read - see screenshot for the leaderboard "Test" below.
The app runs with versions of iOS/iPadOS 14.0 and above. The problem is very specific to iPads running iPadOS 16.1 and above in light mode. The top-level parent view of GKGameCenterViewController
(showing the title "Game Center" and a navigation menu) works fine, only the child views are affected. The problem is not seen on iPhones, nor on iPads running versions of iPadOS 14 or 15. The problem is also not seen on iPads running iPadOS 16.1 and above if dark mode is in operation when GKGameCenterViewController
is first shown.
The GKGameCenterViewController
is being presented using a UIViewControllerRepresentable
as a layer in a ZStack
. From the examples I could find, using UIViewControllerRepresentable
seems to be the standard way to do it, as also described in How to display Game Center leaderboard with SwiftUI.
It is interesting to note that the problem is not seen when the Game Center dashboard is shown by tapping the Game Center access point. I assume that the access point shows the GKGameCenterViewController
in a different way.
The following stripped-down code illustrates how the GKGameCenterViewController
is being shown. This is a standalone app that can be used to reproduce the problem. However, for Game Center authentication to work and for the overview of leaderboards to show, the following needs to be done too:
import SwiftUI
import GameKit
@main
struct GameCenterBlackoutApp: App {
var body: some Scene {
WindowGroup {
MyContentView()
}
}
}
struct MyContentView: View {
@State private var showingGameCenter = false
var body: some View {
ZStack {
Button("Show Game Center leaderboards") {
showingGameCenter = true
}
if showingGameCenter {
MyGameCenterView(showingGameCenter: $showingGameCenter)
.ignoresSafeArea()
}
}
.onAppear {
// Authenticate the local player
GKLocalPlayer.local.authenticateHandler = handleAuthenticationOutcome
}
}
private func handleAuthenticationOutcome(vc: UIViewController?, error: Error?) {
if let error {
#if DEBUG
print("Failed to authenticate player: \(error)")
#endif
}
// Prepare and show the GameCenter access point.
// If authentication failed then the access point takes
// the form of a button to sign in
GKAccessPoint.shared.location = .topTrailing
GKAccessPoint.shared.showHighlights = false
GKAccessPoint.shared.isActive = true
}
}
/// A Bridge between the Game Center view controller and its wrapper
final class MyCoordinator : NSObject, GKGameCenterControllerDelegate {
@Binding private var showingGameCenter: Bool
init(showingGameCenter: Binding<Bool>) {
self._showingGameCenter = showingGameCenter
}
func gameCenterViewControllerDidFinish(
_ gameCenterViewController: GKGameCenterViewController
) {
gameCenterViewController.dismiss(animated:false)
showingGameCenter = false
}
}
/// A wrapper for GKGameCenterViewController
struct MyGameCenterView: UIViewControllerRepresentable {
typealias Coordinator = MyCoordinator
/// Binding to the state variable that controls the visibility of the Game Center layer
@Binding private var showingGameCenter: Bool
init(showingGameCenter: Binding<Bool>) {
self._showingGameCenter = showingGameCenter
}
/// Factory function for the Bridge between the GKGameCenterViewController and this wrapper view
func makeCoordinator() -> Coordinator {
MyCoordinator(showingGameCenter: $showingGameCenter)
}
/// Creates the GKGameCenterViewController
func makeUIViewController(
context: UIViewControllerRepresentableContext<MyGameCenterView>
) -> GKGameCenterViewController {
let result = GKGameCenterViewController(state: .leaderboards)
result.gameCenterDelegate = context.coordinator
return result
}
/// Stub implementation of protocol method
func updateUIViewController(
_ gameCenterViewController: GKGameCenterViewController,
context: UIViewControllerRepresentableContext<MyGameCenterView>
) {
// NOP
}
}
I would be really grateful for any workaround that will resolve the black text issue as described and allow the child views of GKGameCenterViewController
to be shown in a normal, readable foreground color. I have tried all of the following, none of which made any difference:
.dark
or .light
as .overrideUserInterfaceStyle
on the view controller.environment(\.colorScheme, .dark)
to the UIViewControllerRepresentable
or to a higher-level parent.dark
or .white
as .preferredColorScheme
on the UIViewControllerRepresentable
.toolbarColorScheme
(all combinations of parameters) on the UIViewControllerRepresentable
Color.white
as .foregroundColor
on the UIViewControllerRepresentable
UIViewControllerRepresentable
UIViewControllerRepresentable
as an overlay instead of as a ZStack
layerGKGameCenterViewControllerState
values in the init callinit
functions for GKGameCenterViewController
init
functions and setters on GKGameCenterViewController
.BTW, I also reported the issue to Apple, but they have not been able to help.
Upvotes: 0
Views: 171