This week, I have met a problem when I am developing a hybrid app with Cordova (Phonegap). The Cordova app is basing on UIWebView. So the whole app is build by HTML5, Javascript and CSS. Usually, UIWebView will scroll up when keyboard is shown if we click an input field or textfield focus. Therefore, all content in the UIWebView is pushing up. In most of the case, this behavior is correct. However, in my case, it becomes to be a problem. And I have to prevent UIWebView to scroll when keyboard appears.
Let me describe my problem first. I am using Cordova (Phonegap) to build an iOS app. The whole app is basing on UIWebView. In my app, I have a fixed div header at UIWebView top and a fixed div footer at UIWebView bottom. In the middle of the content, there is an input text field. When I click on the input text field and get the input text field focused, all content in UIWebView will scroll up, including the fixed header and fixed footer, which I really don’t want. I have to find a way to set UIWebView content not move when soft keyboard is shown in iOS app.
Javascript Solution
At the beginning, I search on Google and find lots of solutions. One solution is using Javascript to scroll the html content down when the soft keyboard popups. Here is the basic idea:
$('input').on('focus', function(e) { e.preventDefault(); e.stopPropagation(); window.scrollTo(0,0); });
However, it doesn’t work on new iOS version. Therefore, there is an improved Javascript solution:
$('input').on('focus', function(e) { e.preventDefault(); e.stopPropagation(); setTimeout( function(){ windows.scrollTo(0, 0); }, 100); });
But the improved Javascript is not ideal, sometimes you can still see a little jitter animation. To solve the problem perfectly, I have to go deep and try to solve the problem with native objective-c code.
Native Solution
When we click the input field in UIWebView, the soft keyboard will appear and push all content up. It is a built-in feature in iOS. The UIWebView has a UIScrollView inside, which will move the content up and down. Now the solution is disabling the UIScrollView scrolling content up in the UIWebView.
Some people says we can using following code to disable scroll view scrolling:
myWebView.scrollView.scrollEnabled = NO;
Unfortunately, the above code will not prevent keyboard pushing up webview at iOS using PhoneGap (Cordova). It only disable user interaction. User will not be able to scroll the webview any more. It is definitely not what we want.
The Working Solution in Native Code
Another way to stop scroll view scrolling is seting the UIScrollViewDelegate of UIScrollView, then over write the scrollViewDidScroll delegate function. Now the problem is how we can set the delegate and it will not affect the original UIWebView function.
Actually, we can observer the notification when the soft keyboard gets ready to appear and after appearing. In iOS, there will be two local notifications sent out when the soft keyboard is showing up.
- UIKeyboardWillShowNotification: this notification will be sent out when the soft keyboard is going to show up.
- UIKeyboardDidShowNotification: this notification will be sent out after the soft keyboard appears.
So we can first set UIScrollView delegate to our customized UIScrollViewDelegate before keyboard appears and set UIScrollView delegate back once keyboard appears. Now, let’s see how I implement this solution in Objective-C. Please check these example source code.
First, set the observer of above local notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWasShown:) name:UIKeyboardDidShowNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillShown:) name:UIKeyboardWillShowNotification object:nil];
Second, implement the observer functions:
- (void)keyboardWillShown:(NSNotification*)aNotification { oldScrollDelegate = viewController.webView.scrollView.delegate; oldOffset = viewController.webView.scrollView.contentOffset; viewController.webView.scrollView.delegate = self; } - (void)keyboardWasShown:(NSNotification*)aNotification { viewController.webView.scrollView.delegate = oldScrollDelegate; }
Third, implement the UIScrollViewDelegate function to disable the scrolling feature of the UIScrollView.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView { scrollView.contentOffset = oldOffset; }
In this way, when the keyboard is going to show up, I set the UIScrollView’s delegate to self, and UIScrollView will always scroll to point (0, 0). After the keyboard appears, I set the UIScrollView’s delegate back to its old delegate, which will make UIScrollView working as what it should be.
Fixed Header Push Up When Scrolling
This is another problem when I use fixed div in iOS UIWebView. When I scroll my page in UIWebView, the fixed div is pushed up, just like a normal div. Actually, it could be solved by adding following meta data in html header:
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
Hello!
I’d like to use your native solution, however I don’t know where to put this code. I don’t know Objective-C at all so could you give some piece of advice? I’m using latest PhoneGap version.
If you don’t know Objective-C at all, and you want to fix the problem in native way, how can I give advice? Maybe start to learn native programming. In my example, I put those code in AppDelegate.m file.
I tried to paste this code there, but Xcode started to complain after the second piece of code. I found however a PhoneGap plugin “ionic-plugin-keyboard” – it does what I need and gives ability to turn on/off this behaviour with javascript
Anyway – thanks for your answer.
That’s a good news. I suggest you to learn native a little bit if you have time. As phonegap supports you to create your own plugin which may enhance your app more powerful.
You have a typo in your improved JS example. windows ? window
Sorry?
Can you explain how I should initialize “oldOffset” and “oldScrollDelegate” please? I tried with “CGPoint oldOffset = nil;” and “UIScrollViewDelegate* oldScrollDelegate = nil;” but it didn’t work. I’ll appreciate your answer. Thx
What do you mean “it didn’t work”? What error do you get?
I’m getting the following errors:
Classes/AppDelegate.m:33:9: error:
initializing ‘CGPoint’ (aka ‘struct CGPoint’) with an expression of
incompatible type ‘void *’
CGPoint oldOffset = nil;
Classes/AppDelegate.m:34:1: error:
unknown type name ‘UIScrollViewDelegate’
UIScrollViewDelegate* oldScrollDelegate;
Classes/AppDelegate.m:55:53: warning:
assigning to ‘id _Nullable’ from incompatible type
‘AppDelegate *const __strong’
self.viewController.webView.scrollView.delegate = self;
I solved the compilation problem with:
CGPoint oldOffset = { 0.0, 0.0 };
UIScrollView *oldScrollDelegate = nil;
but the solution didn’t work in the emulator. I haven’t tested yet in a real device. Is there a way to observe the focus action to stop scrolling to the input? The emulator haven’t got keyboard and the problem persist. Thx for your time
I also used your solution last time, but I got some side effect after that.
Hi James,
I tried the solution and it solve a part of the problem. The fixed-top menu is displayed correctly at the right place. But when I begin to scroll during edit mode the fixed-top menu scroll with the page and don’t stay at the top. Do you have an idea how to solve that ?
I used this page from bootstrap and add a long paragraph with a text field at the end. https://getbootstrap.com/examples/navbar-fixed-top/
Thanks in advance.
You can use the native solution I mentioned in this post. I hope it still works in latest iOS.
The solution I implanted is the native.
iOS version 10.3 with UIWebView.
Thanks again.
When you start to edit and the fixed-top menu scroll with page, it is because the UIWebView is pushed up by soft-keyboard. So you have to listen some event like “UIKeyboardWillShowNotification” or “UIKeyboardDidShowNotification” to reset the position of your UIWebView.
Good Luck
you really help me a lot , thanks a lot