Programmatically show soft keyboard on iPhone in a PhoneGap application?
Asked Answered
S

7

27

I've been searching far and long, and to this moment, I did not come across a working solution for PhoneGap / Cordova applications that would show soft keyboard programmatically.

Scenario:

We have a PhoneGap application - a website created in jQuery Mobile - that at one point shows a dialog to the user. This dialog is also a web page and has one single INPUT text box where user should enter a code.

Problem:

When the code dialog is shown, the input box is focused using JavaScript. However, due to restrictions placed on iPhone's internal browser, the soft keyboard does not come up until the user actually really clicks inside the input text box.

What we tried:

  • creating a hidden text box and making it first responder
  • making the actual webview a first responder once the input receives focus via JavaScript
  • using sendActionsForControlEvents to try and delive Touch events to the webview (although if anyone has a working code for a PhoneGap application, I would appreciate if they could share it, since I'm no professional in iOS coding)

Any ideas?


EDIT: The restriction mentioned in this question is for built-in browsers only... if you're aiming Opera, you will be successful by using the following code:

var e = jQuery.Event("keydown", { keyCode: 37 });
$('#element').focus().trigger(e);

EDIT2: This is a final working PhoneGap code that can be used in a plugin:

keyboardhelper.h

//
//  keyboardHelper.h
//  soft keyboard displaying plugin for PhoneGap
//
//  Copyright 2012 Martin Ambrus.
//

#import <Foundation/Foundation.h>
#ifdef CORDOVA_FRAMEWORK
#import <Cordova/CDVPlugin.h>
#else
#import "CDVPlugin.h"
#endif

@interface keyboardHelper : CDVPlugin {
    NSString *callbackID;
}

@property (nonatomic, copy) NSString *callbackID;

- (void)showKeyboard:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

@end

keyboardhelper.m

//
//  keyboardHelper.m
//  soft keyboard displaying plugin for PhoneGap
//
//  Copyright 2012 Martin Ambrus.
//

#import "keyboardHelper.h"
#import "AppDelegate.h"

@implementation keyboardHelper
@synthesize callbackID;

-(void)showKeyboard:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {
    self.callbackID = [arguments pop];

    //Get text field coordinate from webview. - You should do this after the webview gets loaded
    //myCustomDiv is a div in the html that contains the textField.
    int textFieldContainerHeightOutput = [[((AppDelegate *)[[UIApplication sharedApplication] delegate]).viewController.webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetHeight;"] intValue];

    int textFieldContainerWidthOutput = [[((AppDelegate *)[[UIApplication sharedApplication] delegate]).viewController.webView  stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetWidth;"] intValue];

    int textFieldContainerYOffset = [[((AppDelegate *)[[UIApplication sharedApplication] delegate]).viewController.webView  stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetTop;"] intValue];

    int textFieldContainerXOffset = [[((AppDelegate *)[[UIApplication sharedApplication] delegate]).viewController.webView  stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetLeft;"] intValue];

    UITextField *myTextField = [[UITextField alloc] initWithFrame: CGRectMake(textFieldContainerXOffset, textFieldContainerYOffset, textFieldContainerWidthOutput, textFieldContainerHeightOutput)];

    [((AppDelegate *)[[UIApplication sharedApplication] delegate]).viewController.webView addSubview:myTextField];
    myTextField.delegate = self;

    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: @"ok"];

    [self writeJavascript:[pluginResult toSuccessCallbackString:self.callbackID]];
}

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//here you create your request to the server
return NO;
}

-(BOOL)textFieldDidEndEditing:(UITextField *)textField
{
//here you create your request to the server
return NO;
}

@end

javascript

var keyboardHelper = {
    showKeyboard: function(types, success, fail) {
        return Cordova.exec(success, fail, "keyboardHelper", "showKeyboard", types);
    }
};
Simper answered 27/8, 2012 at 10:44 Comment(4)
Have you tried $("#input_id").trigger("click")? I did not yet as I am at work but that is one option more you could try.Cristincristina
yes, we tried that... this is not a about JavaScript here, since programmatic events never trigger soft keyboard on mobile devices due to usability and performance issues... I'm looking for native iPhone code that can help out hereSimper
I've updated the question to include a solution for Opera Mobile browser which doesn't seem to be affected by they keyboard restrictionSimper
downvoter - care for an explanation?Simper
O
7

You can get the coordinates for the input field using javascript on the webView. Then, place your own textField right on top of it and in it's delegate method textFieldShouldReturn send a request to the server with the code the user typed in.

    //Get text field coordinate from webview. - You should do this after the webview gets loaded
    //myCustomDiv is a div in the html that contains the textField.
int textFieldContainerHeightOutput = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetHeight;"] intValue];

int textFieldContainerWidthOutput = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetWidth;"] intValue];

int textFieldContainerYOffset = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetTop;"] intValue];

int textFieldContainerXOffset = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetLeft;"] intValue];

myTextField.frame = CGRectMake(textFieldContainerXOffset, textFieldContainerYOffset, textFieldContainerWidthOutput, textFieldContainerHeightOutput);
[webView addSubview:myTextField];
myTextField.delegate = self;

Then you implement textFieldShouldReturn and create your request to the server there.

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//here you create your request to the server
return NO;
}

This is done in existing project, however without using PhoneGap. I hope you can adapt it to suit your needs.

To remove the text field, you can hide it

myTextField.hidden = YES;

or

myTextField = nil;

or

[myTextField removeFromSuperView];
Opener answered 5/9, 2012 at 10:12 Comment(11)
thanks... would you be able to provide an example code? I wouldn't know how to accomplish it for PhoneGap and it would benefit others as well... if your example works, I'll fetch you 200 reputation should this one expireSimper
I have edited my question to include the full code based on your example - it does not work, but could you spot anything out of the ordinary that would make it not work?Simper
The code I have shown you is just an example. You have to adapt it to your own project, not simply copy/paste. For example you have to enter your own id for the javascript to work.Opener
lol, I can't believe I forgot to change element IDs... sorry, that was really stupid... testing again :)))Simper
it seems to work, just one more question - how do I remove that text field from screen once editing has finished?Simper
just hide it: myTextField.hidden = YES;Opener
A perfect solution, I believe you've became many iPhone programmers' hero now! Thank you very much!Simper
@AndreiFilip I have to show keyboard on input box focus into .html page.So can you tell me on that page how i have to apply this code, because currently i am doing inputtextid.focus() for showing keyboard but in this case just for 1 sec focus comes but then it hide.So can you please tell me your code will help me out.Tartarous
@SaurabhAndroid What I explained in my answer is basically a way to add a textview on top of the input box in html. you should be able to use it in a similar way to what i suggested. however, this is no longer needed if your target ios version is iOS6 or higher. in this case, take a look at the Horak's answerOpener
@AndreiFilip this solution is for IOS but what i have to apply same for android.Tartarous
@SaurabhAndroid I'm not familiar with Android. However, as far as I know, this problem is not happening on Android as well. You can always post a new question (tagged with Android) if you have trouble with this.Opener
V
12

You can solve the issue with a config.xml entry these days, add:

<preference name="keyboardDisplayRequiresUserAction" value="false" />
Vevine answered 26/3, 2014 at 20:10 Comment(1)
unfortunately, your example affect on whole appEndeavor
O
7

You can get the coordinates for the input field using javascript on the webView. Then, place your own textField right on top of it and in it's delegate method textFieldShouldReturn send a request to the server with the code the user typed in.

    //Get text field coordinate from webview. - You should do this after the webview gets loaded
    //myCustomDiv is a div in the html that contains the textField.
int textFieldContainerHeightOutput = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetHeight;"] intValue];

int textFieldContainerWidthOutput = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetWidth;"] intValue];

int textFieldContainerYOffset = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetTop;"] intValue];

int textFieldContainerXOffset = [[webView stringByEvaluatingJavaScriptFromString:@"document.getElementById(\"myCustomDiv\").offsetLeft;"] intValue];

myTextField.frame = CGRectMake(textFieldContainerXOffset, textFieldContainerYOffset, textFieldContainerWidthOutput, textFieldContainerHeightOutput);
[webView addSubview:myTextField];
myTextField.delegate = self;

Then you implement textFieldShouldReturn and create your request to the server there.

-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
//here you create your request to the server
return NO;
}

This is done in existing project, however without using PhoneGap. I hope you can adapt it to suit your needs.

To remove the text field, you can hide it

myTextField.hidden = YES;

or

myTextField = nil;

or

[myTextField removeFromSuperView];
Opener answered 5/9, 2012 at 10:12 Comment(11)
thanks... would you be able to provide an example code? I wouldn't know how to accomplish it for PhoneGap and it would benefit others as well... if your example works, I'll fetch you 200 reputation should this one expireSimper
I have edited my question to include the full code based on your example - it does not work, but could you spot anything out of the ordinary that would make it not work?Simper
The code I have shown you is just an example. You have to adapt it to your own project, not simply copy/paste. For example you have to enter your own id for the javascript to work.Opener
lol, I can't believe I forgot to change element IDs... sorry, that was really stupid... testing again :)))Simper
it seems to work, just one more question - how do I remove that text field from screen once editing has finished?Simper
just hide it: myTextField.hidden = YES;Opener
A perfect solution, I believe you've became many iPhone programmers' hero now! Thank you very much!Simper
@AndreiFilip I have to show keyboard on input box focus into .html page.So can you tell me on that page how i have to apply this code, because currently i am doing inputtextid.focus() for showing keyboard but in this case just for 1 sec focus comes but then it hide.So can you please tell me your code will help me out.Tartarous
@SaurabhAndroid What I explained in my answer is basically a way to add a textview on top of the input box in html. you should be able to use it in a similar way to what i suggested. however, this is no longer needed if your target ios version is iOS6 or higher. in this case, take a look at the Horak's answerOpener
@AndreiFilip this solution is for IOS but what i have to apply same for android.Tartarous
@SaurabhAndroid I'm not familiar with Android. However, as far as I know, this problem is not happening on Android as well. You can always post a new question (tagged with Android) if you have trouble with this.Opener
C
4

Prior to iOS 6, Apple only allowed the keyboard to be brought up following a user interaction. In iOS 6 they've introduced the following property for UIWebView which you merely need to set to NO.

    "yourWebView".keyboardDisplayRequiresUserAction = NO;

Apple sets this by default to "Yes". Now you can call focus() in your JS and the keyboard will appear.

Creolacreole answered 22/10, 2012 at 16:44 Comment(4)
in what file do change this? I found a reference to keyboardDisplayRequiresUserAction in UIWebView.h but this file can't be edited.Redistrict
You can call this whenever you've created an instance of UIWebView upon which to call it. I know that up until recently Trigger.IO did not allow the creation of a UIWebView instance so this wasn't possible with this method. I'm not sure if phoneGap allows you to do so but considering it has a much greater following and more developed product I'd imagine it would be possible.Creolacreole
@Horak this solution is for IOS but what i have to apply same for android.Tartarous
@SaurabhAndroid, I don't have experience on Android unfortunately, sorry.Creolacreole
M
3

Have you tried using Native Controls and calling them from Javascript?

Here you have some code that shows the usage of Native Controls on a Phonegap Cordova application (Phonegap 1.5)

https://gist.github.com/1384250

Hope it helps to solve the problem :)

Makowski answered 29/8, 2012 at 12:31 Comment(3)
+1 for the plugin, I was unfamiliar with it... however, I don't see how would I use this to show a keyboard on screen, while preserving the focus on my web page input element?Simper
You said that the dialog which pops up is another web page, the it should have a onLoad funcition you can make the keyboard appear when the dialog gets load. What do you think?Makowski
sure, that would not be a problem - but how to show a keyboard using NativeControls? I only see tab controls in this plugin, not a keyboard showing controlSimper
Z
3

I admit this is private, but it might help you out:

@class UIKeyboard;

void showKeyboard()
{
    static UIKeyboard *kb = nil;
    if ([[UIKeyboard class] respondsToSelector:@selector(automaticKeyboard)])
        kb = [UIKeyboard automaticKeyboard];
    else
        kb = [UIKeyboard activeKeyboard];

    if (kb == nil) {
        kb = [[[UIKeyboard alloc] initWithDefaultSize] autorelease];
        [[[UIApplication sharedApplication] keyWindow] addSubview:kb];
    }

    if ([kb respondsToSelector:@selector(orderInWithAnimation:)]) {
        [kb orderInWithAnimation:YES];
    } else {
        [kb activate];
        [kb minimize];
        [kb maximize];
    }
}

And call it like this:

showKeyboard();
Zonnya answered 29/8, 2012 at 12:38 Comment(7)
I'll just say now that any app destined for the App Store can't do any of this. :)Occipital
I would agree with @JonathanGrynspan, however if you could point me out how or where would I put this? I've never done any class code writing in XCode, so I'm in a bit of a loss as to how to use this snippet :PSimper
@Zathrus - wherever you want. Just make sure you call this function when you want to hide/show the keyboard.Zonnya
well, that's the problem - wherever I put this, I get an error message :DSimper
@H2CO3 would you be able to provide a sample? I don't have a clue about PhoneGap / X-Code coding itself, so it's an impossible task for me to do at the momentSimper
@ZathrusWriter here you are, it should now compile.Zonnya
thanks! unfortunately, this does not seem to work from within a PhoneGap plugin... will try to play around stillSimper
R
3

I actually just found a solution to this. Like horak says and as described in this article, with or without soft keyboard, it's now possible to achieve this with iOS 6.0 by using: KeyboardDisplayRequiresUserAction = NO;

As of Cordova 2.2, the iOS property mentioned above can simply be added to the Cordova.plist file:

KeyboardDisplayRequiresUserAction, boolean, NO

This solves it all for everyone using Cordova 2.2. Now just call input.focus() as previously discussed, and the keyboard will automatically appear. For those of us who haven't yet updated our Cordova to the current latest version (2.2), it's fortunately still possible.

Programmatically show keyboard on iPhone using cordova v. < 2.2

Step 1: Adding property to Cordova.plist

Go to your project > Resources > Cordova.plist. Add: KeyboardDisplayRequiresUserAction, boolean, NO

Step 2: Adding below code snippet to CDVViewController.m

Search for "@interface CDVViewController" (or similar to locate above file). For Cordova 2.1, go to line 240 (everyone else go to a line after a "IsAtLeastiOSVersion" if statement, sorry can't be more precise than that.) Add this code snippet to your CDVViewController.m on the above mentioned line:

if (IsAtLeastiOSVersion(@"6.0")) {
    BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES
    if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] != nil) {
        if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"]) {
            keyboardDisplayRequiresUserAction = [(NSNumber*)[self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] boolValue]; //JB-VAL121212
        }
    }

    // property check for compiling under iOS < 6
    if ([self.webView respondsToSelector:@selector(setKeyboardDisplayRequiresUserAction:)]) {
        [self.webView setValue:[NSNumber numberWithBool:keyboardDisplayRequiresUserAction] forKey:@"keyboardDisplayRequiresUserAction"];
    }

}

Disclaimer: This has been tested on Cordova 2.1 only, it is, however, very likely that it still works with 2.0 or any other earlier version that comes with the CDVViewController.m file)

Redistrict answered 10/12, 2012 at 21:26 Comment(0)
B
0

You can use the FocusOut event on the input field and this will be fired when the Done key is pressed. I could use this on IOS 6 and above. I believe it will also work on previous versions.

Briard answered 16/2, 2013 at 2:33 Comment(1)
as far as I know, from iOS 6 you can tell the system if you want to show the keyboard 'onFocus()' or not natively - so I guess there's no need for a FocusOut there?Simper

© 2022 - 2024 — McMap. All rights reserved.