|
23 | 23 | static NSURLCredential* clientAuthenticationCredential;
|
24 | 24 | static NSDictionary* customCertificatesForHost;
|
25 | 25 |
|
| 26 | +NSString *const CUSTOM_SELECTOR = @"_CUSTOM_SELECTOR_"; |
| 27 | + |
26 | 28 | #if !TARGET_OS_OSX
|
27 | 29 | // runtime trick to remove WKWebView keyboard default toolbar
|
28 | 30 | // see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
|
@@ -188,11 +190,114 @@ - (instancetype)initWithFrame:(CGRect)frame
|
188 | 190 | return self;
|
189 | 191 | }
|
190 | 192 |
|
| 193 | +- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { |
| 194 | + // Only allow long press gesture |
| 195 | + if ([otherGestureRecognizer isKindOfClass:[UILongPressGestureRecognizer class]]) { |
| 196 | + return YES; |
| 197 | + }else{ |
| 198 | + return NO; |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +// Listener for long presses |
| 203 | +- (void)startLongPress:(UILongPressGestureRecognizer *)pressSender |
| 204 | +{ |
| 205 | + // When a long press ends, bring up our custom UIMenu |
| 206 | + if(pressSender.state == UIGestureRecognizerStateEnded) { |
| 207 | + if (!self.menuItems || self.menuItems.count == 0) { |
| 208 | + return; |
| 209 | + } |
| 210 | + UIMenuController *menuController = [UIMenuController sharedMenuController]; |
| 211 | + NSMutableArray *menuControllerItems = [NSMutableArray arrayWithCapacity:self.menuItems.count]; |
| 212 | + |
| 213 | + for(NSString *menuItemName in self.menuItems) { |
| 214 | + NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]]; |
| 215 | + NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]]; |
| 216 | + NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey]; |
| 217 | + UIMenuItem *item = [[UIMenuItem alloc] initWithTitle: menuItemLabel |
| 218 | + action: NSSelectorFromString(sel)]; |
| 219 | + |
| 220 | + [menuControllerItems addObject: item]; |
| 221 | + } |
| 222 | + |
| 223 | + menuController.menuItems = menuControllerItems; |
| 224 | + [menuController setMenuVisible:YES animated:YES]; |
| 225 | + } |
| 226 | +} |
| 227 | + |
191 | 228 | - (void)dealloc
|
192 | 229 | {
|
193 | 230 | [[NSNotificationCenter defaultCenter] removeObserver:self];
|
194 | 231 | }
|
195 | 232 |
|
| 233 | +- (void)tappedMenuItem:(NSString *)eventType |
| 234 | +{ |
| 235 | + // Get the selected text |
| 236 | + // NOTE: selecting text in an iframe or shadow DOM will not work |
| 237 | + [self.webView evaluateJavaScript: @"window.getSelection().toString()" completionHandler: ^(id result, NSError *error) { |
| 238 | + if (error != nil) { |
| 239 | + RCTLogWarn(@"%@", [NSString stringWithFormat:@"Error evaluating injectedJavaScript: This is possibly due to an unsupported return type. Try adding true to the end of your injectedJavaScript string. %@", error]); |
| 240 | + } else { |
| 241 | + if (self.onCustomMenuSelection) { |
| 242 | + NSPredicate *filter = [NSPredicate predicateWithFormat:@"key contains[c] %@ ",eventType]; |
| 243 | + NSArray *filteredMenuItems = [self.menuItems filteredArrayUsingPredicate:filter]; |
| 244 | + NSDictionary *selectedMenuItem = filteredMenuItems[0]; |
| 245 | + NSString *label = [RCTConvert NSString:selectedMenuItem[@"label"]]; |
| 246 | + self.onCustomMenuSelection(@{ |
| 247 | + @"key": eventType, |
| 248 | + @"label": label, |
| 249 | + @"selectedText": result |
| 250 | + }); |
| 251 | + } else { |
| 252 | + RCTLogWarn(@"Error evaluating onCustomMenuSelection: You must implement an `onCustomMenuSelection` callback when using custom menu items"); |
| 253 | + } |
| 254 | + } |
| 255 | + }]; |
| 256 | +} |
| 257 | + |
| 258 | +// Overwrite method that interprets which action to call upon UIMenu Selection |
| 259 | +// https://developer.apple.com/documentation/objectivec/nsobject/1571960-methodsignatureforselector |
| 260 | +- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel |
| 261 | +{ |
| 262 | + NSMethodSignature *existingSelector = [super methodSignatureForSelector:sel]; |
| 263 | + if (existingSelector) { |
| 264 | + return existingSelector; |
| 265 | + } |
| 266 | + return [super methodSignatureForSelector:@selector(tappedMenuItem:)]; |
| 267 | +} |
| 268 | + |
| 269 | +// Needed to forward messages to other objects |
| 270 | +// https://developer.apple.com/documentation/objectivec/nsobject/1571955-forwardinvocation |
| 271 | +- (void)forwardInvocation:(NSInvocation *)invocation |
| 272 | +{ |
| 273 | + NSString *sel = NSStringFromSelector([invocation selector]); |
| 274 | + NSRange match = [sel rangeOfString:CUSTOM_SELECTOR]; |
| 275 | + if (match.location == 0) { |
| 276 | + [self tappedMenuItem:[sel substringFromIndex:17]]; |
| 277 | + } else { |
| 278 | + [super forwardInvocation:invocation]; |
| 279 | + } |
| 280 | +} |
| 281 | + |
| 282 | +// Allows the instance to respond to UIMenuController Actions |
| 283 | +- (BOOL)canBecomeFirstResponder |
| 284 | +{ |
| 285 | + return YES; |
| 286 | +} |
| 287 | + |
| 288 | +// Control which items show up on the UIMenuController |
| 289 | +- (BOOL)canPerformAction:(SEL)action withSender:(id)sender |
| 290 | +{ |
| 291 | + NSString *sel = NSStringFromSelector(action); |
| 292 | + // Do any of them have our custom keys? |
| 293 | + NSRange match = [sel rangeOfString:CUSTOM_SELECTOR]; |
| 294 | + |
| 295 | + if (match.location == 0) { |
| 296 | + return YES; |
| 297 | + } |
| 298 | + return NO; |
| 299 | +} |
| 300 | + |
196 | 301 | /**
|
197 | 302 | * See https://stackoverflow.com/questions/25713069/why-is-wkwebview-not-opening-links-with-target-blank/25853806#25853806 for details.
|
198 | 303 | */
|
@@ -328,6 +433,17 @@ - (void)didMoveToWindow
|
328 | 433 | [self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction];
|
329 | 434 | [self visitSource];
|
330 | 435 | }
|
| 436 | + |
| 437 | + // Allow this object to recognize gestures |
| 438 | + if (self.menuItems != nil) { |
| 439 | + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(startLongPress:)]; |
| 440 | + longPress.delegate = self; |
| 441 | + |
| 442 | + longPress.minimumPressDuration = 0.4f; |
| 443 | + longPress.numberOfTouchesRequired = 1; |
| 444 | + longPress.cancelsTouchesInView = YES; |
| 445 | + [self addGestureRecognizer:longPress]; |
| 446 | + } |
331 | 447 | }
|
332 | 448 |
|
333 | 449 | // Update webview property when the component prop changes.
|
|
0 commit comments