react-native-ios-context-menu
react-native-ios-context-menu copied to clipboard
Set specific color scheme
Hey Dominic,
Thank you very much for your hard work!
It's not really an issue but more a question or feature request: can we set a specific color scheme for the menu?
We let our user choose between light and dark mode but unfortunately the native menu is using the device color scheme.
hello ✨
this is possible via the UIAppearance API — but I'm going to do it in a separate library called react-native-ios-utilites since this isn't specific to UIMenu.
this involves overriding the root view controller's overrideUserInterfaceStyle, and manually specifying whether to use the dark or light appearance — the problem is that this approach won't work if you're using a navigation library (since they're probably going to replace the root view controller with their own implementation)
the correct approach is to override the root view controller's overrideUserInterfaceStyle via replacing it's implementation via swizzling/reflection
the example below is from my other library that changes the root view controller's childForStatusBarStyle method so you can set the status bar style:
internal func swizzleRootViewController(for window: UIWindow){
let baseClass : AnyClass = UIViewController.self;
let baseSelector: Selector = #selector(getter: UIViewController.childForStatusBarStyle);
let baseMethod : Method? = class_getInstanceMethod(baseClass, baseSelector);
let replacementClass : AnyClass = RNIRootViewController.self;
let replacementSelector: Selector = #selector(getter: RNIRootViewController.childForStatusBarStyle);
let replacementMethod : Method? = class_getInstanceMethod(replacementClass, replacementSelector);
// replace the root view controller's default `childForStatusBarStyle` impl.
if let originalMethod = baseMethod,
let swizzledMethod = replacementMethod {
method_exchangeImplementations(originalMethod, swizzledMethod);
};
};
I'll close this issue once I get around publishing the library haha
Thank you for the detailed answer! This is very informative. I'm new to this concept of swizzling so I might be missing something but I'm wondering if an approach similar to expo-system-ui could work: https://github.com/expo/expo/blob/sdk-44/packages/expo-system-ui/ios/ExpoSystemUI/ExpoSystemUIModule.swift. Any idea?
@axeldelafosse hey i checked the examples you mentioned, and the one from expo just sets the background color property of the root view controller's view (e.g. rootVC.view.backgroundColor)
with the approach i mentioned earlier, the appearance can only be changed via subclassing UIViewController, overriding the default implementation of overrideUserInterfaceStyle, and replacing the current window's root view controller with your own implementation.
unfortunately, in the case of view controllers, overrideUserInterfaceStyle is a computed property (i.e. a getter method), so the only way to change what it's returning is to override the method
fortunately, we can also set overrideUserInterfaceStyle on the main window itself (much more simpler/easier since it's like setting a global variable haha)
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
window?.overrideUserInterfaceStyle = .dark
It seems this also came up as a requirement in our project.
I’ve been reading the documentation of UIAppearance, and it appears that there’s a mention of:
iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back.
Based on that notice, my understanding is that it may not be exactly straight-forward to change this at run-time. 🤔
You can now do this with Appearance.setColorScheme from React Native.