-
Notifications
You must be signed in to change notification settings - Fork 21
fix(ios): prevent black QuickType bar when using Magic Keyboard on iPad + Fix Keyboard on iOS 26 #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fix(ios): prevent black QuickType bar when using Magic Keyboard on iPad + Fix Keyboard on iOS 26 #52
Changes from all commits
68188c1
67309d2
e9bad39
4a1b4a9
0ebaaab
8f718b7
08e5f52
6dd9266
f06eac6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ Licensed to the Apache Software Foundation (ASF) under one | |
| #import "Keyboard.h" | ||
| #import <Foundation/Foundation.h> | ||
| #import <objc/runtime.h> | ||
| #import <UIKit/UIKit.h> | ||
| #import <Capacitor/Capacitor.h> | ||
| #import <Capacitor/Capacitor-Swift.h> | ||
| #import <Capacitor/CAPBridgedPlugin.h> | ||
|
|
@@ -54,6 +55,99 @@ @implementation KeyboardPlugin | |
| NSString* UITraitsClassString; | ||
| double stageManagerOffset; | ||
|
|
||
| #pragma mark - Helpers | ||
|
|
||
| - (UIWindow *)currentKeyWindow { | ||
| UIWindow *window = nil; | ||
| if ([[[UIApplication sharedApplication] delegate] respondsToSelector:@selector(window)]) { | ||
| window = [[[UIApplication sharedApplication] delegate] window]; | ||
| } | ||
| if (!window && @available(iOS 13.0, *)) { | ||
| NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", UIWindowScene.class]; | ||
| UIScene *scene = [UIApplication.sharedApplication.connectedScenes.allObjects filteredArrayUsingPredicate:predicate].firstObject; | ||
| window = [[(UIWindowScene*)scene windows] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isKeyWindow == YES"]].firstObject; | ||
| } | ||
| return window; | ||
| } | ||
|
|
||
| - (void)forceBackdropColor:(UIColor *)color { | ||
| UIWindow *w = [self currentKeyWindow]; | ||
| if (w) { | ||
| dispatch_async(dispatch_get_main_queue(), ^{ | ||
| w.backgroundColor = color; | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| - (BOOL)isIPad { | ||
| return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad); | ||
| } | ||
|
|
||
| - (BOOL)shouldIgnoreResizeForHeight:(double)height { | ||
| if (![self isIPad]) return NO; | ||
| if (height <= 0.0) return NO; | ||
| CGFloat screenHeight = UIScreen.mainScreen.bounds.size.height; | ||
| return (height / screenHeight) < 0.20; | ||
| } | ||
|
theproducer marked this conversation as resolved.
|
||
|
|
||
| #pragma mark - Lifecycle | ||
|
|
||
| - (UIColor *)colorFromCssColorString:(NSString *)cssColor { | ||
| NSString *trimmed = [cssColor stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; | ||
|
|
||
| if ([trimmed isEqualToString:@"transparent"]) { | ||
| return [UIColor clearColor]; | ||
| } | ||
|
|
||
| if ([trimmed hasPrefix:@"rgb"]) { | ||
| NSRange parenRange = [trimmed rangeOfString:@"("]; | ||
| NSString *clean = parenRange.location != NSNotFound ? [trimmed substringFromIndex:parenRange.location + 1] : trimmed; | ||
| clean = [clean stringByReplacingOccurrencesOfString:@")" withString:@""]; | ||
| NSArray *parts = [clean componentsSeparatedByString:@","]; | ||
| if (parts.count >= 3) { | ||
| CGFloat r = [parts[0] floatValue] / 255.0; | ||
| CGFloat g = [parts[1] floatValue] / 255.0; | ||
| CGFloat b = [parts[2] floatValue] / 255.0; | ||
| CGFloat a = parts.count >= 4 ? [parts[3] floatValue] : 1.0; | ||
| return [UIColor colorWithRed:r green:g blue:b alpha:a]; | ||
| } | ||
| } | ||
|
|
||
| if ([trimmed hasPrefix:@"#"]) { | ||
| unsigned rgbValue = 0; | ||
| NSScanner *scanner = [NSScanner scannerWithString:trimmed]; | ||
| [scanner setScanLocation:1]; | ||
| if ([scanner scanHexInt:&rgbValue]) { | ||
| CGFloat r = ((rgbValue & 0xFF0000) >> 16) / 255.0; | ||
| CGFloat g = ((rgbValue & 0x00FF00) >> 8) / 255.0; | ||
| CGFloat b = (rgbValue & 0x0000FF) / 255.0; | ||
| return [UIColor colorWithRed:r green:g blue:b alpha:1.0]; | ||
| } | ||
| } | ||
|
|
||
| return [UIColor whiteColor]; // fallback | ||
| } | ||
|
|
||
| - (void)updateBackdropColor { | ||
| if (self.bridge.config.backgroundColor) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Capacitor config has Source: https://github.com/ionic-team/capacitor/blob/main/cli/src/declarations.ts |
||
| [self forceBackdropColor:self.bridge.config.backgroundColor]; | ||
| } else { | ||
| [self updateBackdropColorFromDOM]; | ||
| } | ||
| } | ||
|
|
||
| - (void)updateBackdropColorFromDOM { | ||
| if (!self.webView) return; | ||
| [self.webView evaluateJavaScript:@"window.getComputedStyle(document.body).backgroundColor" completionHandler:^(id result, NSError *error) { | ||
| if (result && [result isKindOfClass:[NSString class]]) { | ||
| UIColor *color = [self colorFromCssColorString:(NSString *)result]; | ||
| if (color) { | ||
| [self forceBackdropColor:color]; | ||
| } | ||
| } | ||
| }]; | ||
| } | ||
|
|
||
| - (void)load | ||
| { | ||
| self.disableScroll = !self.bridge.config.scrollingEnabled; | ||
|
|
@@ -97,8 +191,16 @@ - (void)load | |
| [nc removeObserver:self.webView name:UIKeyboardWillShowNotification object:nil]; | ||
| [nc removeObserver:self.webView name:UIKeyboardWillChangeFrameNotification object:nil]; | ||
| [nc removeObserver:self.webView name:UIKeyboardDidChangeFrameNotification object:nil]; | ||
| } | ||
|
|
||
| // Make WKWebView transparent | ||
| if (self.webView) { | ||
| self.webView.opaque = NO; | ||
| self.webView.backgroundColor = UIColor.clearColor; | ||
| self.webView.scrollView.backgroundColor = UIColor.clearColor; | ||
| } | ||
|
Comment on lines
+195
to
+200
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Due to how vastly different UIs of Web Apps / Capacitor Apps can be, I'm wondering if such a change could have unforeseen consequences in app's UI. Is this how it is on Android? |
||
|
|
||
| [self updateBackdropColor]; | ||
| } | ||
|
|
||
| #pragma mark Keyboard events | ||
|
|
||
|
|
@@ -124,11 +226,15 @@ - (void)onKeyboardWillShow:(NSNotification *)notification | |
| if (hideTimer != nil) { | ||
| [hideTimer invalidate]; | ||
| } | ||
|
|
||
| // Force DOM color whenever keyboard shows | ||
| [self updateBackdropColor]; | ||
|
|
||
| CGRect rect = [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; | ||
|
|
||
| double height = rect.size.height; | ||
|
|
||
| if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { | ||
| if ([self isIPad]) { | ||
| if (stageManagerOffset > 0) { | ||
| height = stageManagerOffset; | ||
| } else { | ||
|
|
@@ -142,8 +248,14 @@ - (void)onKeyboardWillShow:(NSNotification *)notification | |
| } | ||
| } | ||
|
|
||
| BOOL ignored = [self shouldIgnoreResizeForHeight:height]; | ||
| if (ignored) { | ||
| NSLog(@"KeyboardPlugin: Ignoring QuickType Bar (%.1f) -> treat as 0.", height); | ||
| height = 0.0; | ||
| } | ||
|
|
||
| double duration = [[notification.userInfo valueForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue]+0.2; | ||
| [self setKeyboardHeight:height delay:duration]; | ||
| [self setKeyboardHeight:(int)height delay:duration]; | ||
| [self resetScrollView]; | ||
|
|
||
| NSString * data = [NSString stringWithFormat:@"{ 'keyboardHeight': %d }", (int)height]; | ||
|
|
@@ -157,6 +269,24 @@ - (void)onKeyboardDidShow:(NSNotification *)notification | |
| CGRect rect = [[notification.userInfo valueForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue]; | ||
| double height = rect.size.height; | ||
|
|
||
| if ([self isIPad]) { | ||
| if (stageManagerOffset > 0) { | ||
| height = stageManagerOffset; | ||
| } else { | ||
| CGRect webViewAbsolute = [self.webView convertRect:self.webView.frame toCoordinateSpace:self.webView.window.screen.coordinateSpace]; | ||
| height = (webViewAbsolute.size.height + webViewAbsolute.origin.y) - (UIScreen.mainScreen.bounds.size.height - rect.size.height); | ||
| if (height < 0) { | ||
| height = 0; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| BOOL ignored = [self shouldIgnoreResizeForHeight:height]; | ||
| if (ignored) { | ||
| NSLog(@"KeyboardPlugin: (didShow) Ignoring QuickType Bar (%.1f) -> report 0.", height); | ||
| height = 0.0; | ||
| } | ||
|
|
||
| [self resetScrollView]; | ||
|
|
||
| NSString * data = [NSString stringWithFormat:@"{ 'keyboardHeight': %d }", (int)height]; | ||
|
|
@@ -205,19 +335,8 @@ - (void)resizeElement:(NSString *)element withPaddingBottom:(int)paddingBottom w | |
| - (void)_updateFrame | ||
| { | ||
| CGRect f, wf = CGRectZero; | ||
| UIWindow * window = nil; | ||
|
|
||
| if ([[[UIApplication sharedApplication] delegate] respondsToSelector:@selector(window)]) { | ||
| window = [[[UIApplication sharedApplication] delegate] window]; | ||
| } | ||
|
|
||
| if (!window) { | ||
| if (@available(iOS 13.0, *)) { | ||
| NSPredicate *predicate = [NSPredicate predicateWithFormat:@"self isKindOfClass: %@", UIWindowScene.class]; | ||
| UIScene *scene = [UIApplication.sharedApplication.connectedScenes.allObjects filteredArrayUsingPredicate:predicate].firstObject; | ||
| window = [[(UIWindowScene*)scene windows] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isKeyWindow == YES"]].firstObject; | ||
| } | ||
| } | ||
| UIWindow *window = [self currentKeyWindow]; | ||
|
|
||
| if (window) { | ||
| f = [window bounds]; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@available(iOS 13.0, *)should always be true for Capacitor 8+, so it's not needed right?