react-native-ios-context-menu icon indicating copy to clipboard operation
react-native-ios-context-menu copied to clipboard

Set specific color scheme

Open axeldelafosse opened this issue 3 years ago • 4 comments

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.

axeldelafosse avatar Jan 21 '22 15:01 axeldelafosse

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

dominicstop avatar Feb 01 '22 23:02 dominicstop

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 avatar Feb 02 '22 14:02 axeldelafosse

@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

dominicstop avatar Feb 02 '22 22:02 dominicstop

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. 🤔

andreialecu avatar Jun 11 '22 13:06 andreialecu

You can now do this with Appearance.setColorScheme from React Native.

nandorojo avatar Oct 16 '23 16:10 nandorojo