Set UIWebView Content not to Scroll When Keyboard is Shown

On February 18, 2016, in iPhone iPad Object-C, by James Liu

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">
Tagged with:  

10 Responses to “Set UIWebView Content not to Scroll When Keyboard is Shown”

  1. Olek says:

    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.

    • James Liu says:

      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.

  2. Olek says:

    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.

    • James Liu says:

      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.

  3. John says:

    You have a typo in your improved JS example. windows ? window

  4. Jorge says:

    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

    • James Liu says:

      What do you mean “it didn’t work”? What error do you get?

      • Jorge says:

        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;

        • Jorge says:

          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

Leave a Reply

Free WordPress Theme

Weboy