Open ideopunk opened 2 years ago
I am also having this problem, will this be addressed soon?
Also having this problem. I tried typecasting the the rootViewController like this:
UIViewController *rootViewController = (HomeIndicatorViewController *) [self.reactDelegate createRootViewController];
and this:
UIViewController *rootViewController = [HomeIndicatorViewController new];
rootViewController = (HomeIndicatorViewController *) [self.reactDelegate createRootViewController];
but they both got the same error. Bear in mind, I don't know the first thing about Objective-C, or Swift, or managing a bare react-native workflow, so I don't know if I typecasted correctly, or if it even makes sense to typecast in this context. I'm using an expo managed project that has been ejected with expokit.
Well the package is working as intended now, and the reason is that I replaced
UIViewController *rootViewController = [self.reactDelegate createRootViewController];
with
UIViewController *rootViewController = [HomeIndicatorViewController new];
and it worked. I didn't try it before since @ideopunk said they did the same and ran into a problem. I checked the issues before ever trying it myself and just assumed I would run into the same problem. That said, I'm not sure why it worked for me and not them. Still, I think the installation guide should address this case for people using expo projects.
Casting will not work because we need to create the rootView from the HomeIndicatorViewController class. This is mandatory for the functionality of the library. Thats also why the error explicitly gets thrown when the rootViewController
of the app does not have the correct type.
I'm unsure if replacing the init of the *rootViewController
will reliably work for expo projects. I can see here that it is possible that the rootViewController is supposed to come from self.handlers
which probably has a reason. When we replace the controllers in AppDelegate we ignore controllers which may have been initialised and stored in self.handlers
before.
I don't have any experience with expo so I don't have a feeling for the downsides of this approach. Does it still work for you @jakeorbtl ?
@ideopunk Did you have any luck ever getting this to work?
Also wondering :-)
Here's a working config plugin for anyone who needs it.
Note: This simply turns it off entirely. It does not provide the option to set it manually.
const { withAppDelegate } = require('@expo/config-plugins')
function withHiddenHomeIndicator(expoConfig) {
return withAppDelegate(expoConfig, (config) => {
const { modResults } = config
const { contents } = modResults
const lines = contents.split('\n')
const importIndex = lines.findIndex((line) => /^#import "AppDelegate.h"/.test(line))
modResults.contents = [
'#import <objc/runtime.h>',
...lines.slice(0, importIndex + 1),
...lines.slice(importIndex + 1),
'@implementation UIViewController (Swizzling)',
'+ (void)load',
'{',
' static dispatch_once_t onceToken;',
' dispatch_once(&onceToken, ^{',
' Class classVC = [self class];',
' SEL originalSelector = @selector(prefersHomeIndicatorAutoHidden);',
' SEL swizzledSelector = @selector(swizzledPrefersHomeIndicatorAutoHidden);',
' Method originalMethod = class_getInstanceMethod(classVC, originalSelector);',
' Method swizzledMethod = class_getInstanceMethod(classVC, swizzledSelector);',
' const BOOL didAdd = class_addMethod(classVC, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));',
' if (didAdd)',
' class_replaceMethod(classVC, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));',
' else',
' method_exchangeImplementations(originalMethod, swizzledMethod);',
' });',
'}',
'- (BOOL)prefersHomeIndicatorAutoHidden {',
' return YES;',
'}',
'- (BOOL)swizzledPrefersHomeIndicatorAutoHidden {',
' return YES;',
'}',
'@end',
].join('\n')
return config
})
}
module.exports = withHiddenHomeIndicator
Wow, I'm impressed and terrified that it's possible to alter AppDelegate.h so drastically without even (re)building a native app.
I knew objective c was quite dynamic but I didn't realise it was that dynamic.
Thanks for your contribution!
Here's a working config plugin for anyone who needs it.
Note: This simply turns it off entirely. It does not provide the option to set it manually.
const { withAppDelegate } = require('@expo/config-plugins') function withHiddenHomeIndicator(expoConfig) { return withAppDelegate(expoConfig, (config) => { const { modResults } = config const { contents } = modResults const lines = contents.split('\n') const importIndex = lines.findIndex((line) => /^#import "AppDelegate.h"/.test(line)) modResults.contents = [ '#import <objc/runtime.h>', ...lines.slice(0, importIndex + 1), ...lines.slice(importIndex + 1), '@implementation UIViewController (Swizzling)', '+ (void)load', '{', ' static dispatch_once_t onceToken;', ' dispatch_once(&onceToken, ^{', ' Class classVC = [self class];', ' SEL originalSelector = @selector(prefersHomeIndicatorAutoHidden);', ' SEL swizzledSelector = @selector(swizzledPrefersHomeIndicatorAutoHidden);', ' Method originalMethod = class_getInstanceMethod(classVC, originalSelector);', ' Method swizzledMethod = class_getInstanceMethod(classVC, swizzledSelector);', ' const BOOL didAdd = class_addMethod(classVC, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));', ' if (didAdd)', ' class_replaceMethod(classVC, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));', ' else', ' method_exchangeImplementations(originalMethod, swizzledMethod);', ' });', '}', '- (BOOL)prefersHomeIndicatorAutoHidden {', ' return YES;', '}', '- (BOOL)swizzledPrefersHomeIndicatorAutoHidden {', ' return YES;', '}', '@end', ].join('\n') return config }) } module.exports = withHiddenHomeIndicator
I want to ask if it is possible to do with this preferredScreenEdgesDeferringSystemGestures method what i tried -
const { withAppDelegate } = require("@expo/config-plugins");
function withDeferredSystemGestures(expoConfig) {
return withAppDelegate(expoConfig, (config) => {
const { modResults } = config;
const { contents } = modResults;
const lines = contents.split("\n");
const importIndex = lines.findIndex((line) =>
/^#import "AppDelegate.h"/.test(line)
);
// Inject Objective-C code to override preferredScreenEdgesDeferringSystemGestures
modResults.contents = [
...lines.slice(0, importIndex + 1),
"#import <objc/runtime.h>",
...lines.slice(importIndex + 1),
"@implementation UIViewController (DeferredSystemGestures)",
"+ (void)load",
"{",
" static dispatch_once_t onceToken;",
" dispatch_once(&onceToken, ^{",
" Class class = [self class];",
" SEL originalSelector = @selector(preferredScreenEdgesDeferringSystemGestures);",
" SEL swizzledSelector = @selector(swizzled_preferredScreenEdgesDeferringSystemGestures);",
" Method originalMethod = class_getInstanceMethod(class, originalSelector);",
" Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);",
" BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));",
" if (didAddMethod) {",
" class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));",
" } else {",
" method_exchangeImplementations(originalMethod, swizzledMethod);",
" }",
" });",
"}",
"",
// This method will return the edges you want to defer system gestures for.
// Here, we're deferring the bottom edge as an example.
// You can change UIRectEdgeBottom to UIRectEdgeLeft, UIRectEdgeRight, or UIRectEdgeTop as needed.
"- (UIRectEdge)swizzled_preferredScreenEdgesDeferringSystemGestures {",
" return UIRectEdgeBottom;",
"}",
"@end",
].join("\n");
return config;
});
}
module.exports = withDeferredSystemGestures;
but i got error... I am trying to modify the iOS behavior where the home indicator dims, then requires two swipes to activate: the first swipe to brighten the indicator and the second swipe to invoke the home action. This is to mimic the behavior of preferredScreenEdgesDeferringSystemGestures in a native iOS app
@syropian Are you using this in any of your repos?
How does one instantiate your plugin? I've tried importing it and adding a <withHiddenHomeIndicator />
tag to my Expo app, but no dice: "Unable to resolve module assert from ...@expo/config-plugins/build/android/Manifest.js: assert could not be found"
@Ever-It-Lazy It's a pretty quick-n-dirty solution so keep in mind it will apply globally to your app. All you need to do is add the code I wrote to a file called something like withHiddenHomeIndicator.js
in a plugins
directory at the root, and then reference it in your app.json
plugins section
// ...
"plugins": [
"./plugins/withHiddenHomeIndicator.js",
],
//...
@syropian Global is fine. An ever-present Home Indicator kind of ruins the app I'm trying to build.
I've attempted what you said in a bare bones Expo app. It no longer errs, but the Home Indicator is just as visible as ever, at the screen's bottom.
I guess I'd also be cool with the workaround devised by @jakeorbtl...if I could understand how to apply and deploy it?
It seems like the file being changed is node modules > react-native > Libraries > AppDelegate > RCTAppDelegate.mm
?
node modules
directory handled by a package manager like npm or yarn? This wouldn't be part of the project that deploys. How would I then reapply this change across different deployments? (Hence, @syropian's contribution seems like a more portable, sound execution.)It no longer errs, but the Home Indicator is just as visible as ever
@Ever-It-Lazy FYI this will only work in the context of a development build using expo-dev-client
or a production build. It will not work in Expo Go. That may be why you're still seeing it?
@syropian It took me a while to learn expo-dev-client. My discoveries:
react-native-home-indicator
package.package.json
sometime soon.)I had to eventually learn deployment, anyway, so I welcome the experiment. But I did include a repo above, to make this trial-and-error a bit more tangible. So please, do let me know if you ever post a simple, functional example of your plugin (or get my example working).
*correction: your plugin has roughly the same effect as the autoHideHomeIndicator
screenOption of the Stack.Navigator tag. (My repo makes no use of the Navigator API.) This is more than no effect. But the Home Indicator is still only optionally hidden, returning whenever a finger gestures across the screen.
I'm attempting to create a config plugin so that this library will work with Expo. However, on app launch I get this error:
DevLauncher tries to handle uncaught exception: rootViewController is not of type HomeIndicatorViewController as expected.
My
AppDelegate.m
didn't haveUIViewController *rootViewController = [UIViewController new];
as a line to replace, it hadUIViewController *rootViewController = [self.reactDelegate createRootViewController];
instead, so I replaced it. I suspect this has something to do with the problem.