Capacitor: iOS 12 App Touch-Areas are offset after keyboard is hidden

Created on 21 Sep 2018  Â·  41Comments  Â·  Source: ionic-team/capacitor

Steps to reproduce:

  • Type into an input, which will scroll the Application's view
  • Press a button, while the input is focued

What happens:
-> The UI shifts down, but touchareas of UI-Elements are still offset. Causing always touching the wrong button

Tested with iPhone X and iPhone 8 Simulator on iOS 11 and iOS 12.
Only happens on iOS 12 (both Simulators)

When you take a look at the View Hierarchy it seems, some Subviews of the WebView are still offset.
Screenshot of Hierarchy attached.

bildschirmfoto 2018-09-21 um 18 29 11

ios

Most helpful comment

I adapted the solution from https://github.com/ionic-team/capacitor/issues/814#issuecomment-432724789 to Swift and used it in the ios/App/App/AppDelegate.swift.

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

    return true
  }

    @objc func keyboardWillHide(notification: NSNotification) {
        if #available(iOS 12.0, *) {
            let vc = self.window?.rootViewController as! CAPBridgeViewController
            for v in vc.bridgedWebView!.subviews {
                if v is UIScrollView {
                    let scrollView = v as! UIScrollView
                    scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
                }
            }
        }
    }

This fixes my issues.

All 41 comments

Are you using Xcode 10, right?
I think this is a bug in the SDK 12 included in Xcode 10.

Can you try with Xcode 9 and see if you can reproduce there?

But testing directly from Xcode 9 in iOS 12 devices is not possible as it fails to install, you'll have to export the ipa and install it manually

You are right, does not happen with Xcode 9 / iOS 11.
But when you open an URL and load a website in a WKWebView everything works fine with iOS 12.
So it seems there is an issue in the Capacitor-Layer.

I meant, can you test this in Xcode 9 but on iOS 12 device?

Just tried it, built with Xcode 9 and run on iOS 12 device works fine.

I am also seeing this issue when using keyboard which causes input to scroll into view (may be through using keyboard arrows) then closing the keyboard, the buttons in my toolbar cannot be clicked. When you press them nothing happens. This issue is consistently reproducible and makes the app unusable.

Inline with this issue, it does not seem to affect my app when running on iOS 11.

Not super sure the cause of this issue, many issues have been raised across other projects, but it is also effecting capacitor apps.
ionic-team/cordova-plugin-ionic-webview#176
apache/cordova-ios#417
http://www.openradar.me/44655885

Potential test project https://github.com/dpogue/WKScrollTest

We just filed the same bug against ionic 4, but was told it was probably a capacitor issue as far as ionic is concerned, so rather than open another issue, I'll paste it here:

Ionic Info
Run ionic info from a terminal/cmd prompt and paste the output below.

ionic (Ionic CLI)          : 4.1.0 (/usr/local/lib/node_modules/ionic)
   Ionic Framework            : @ionic/angular 4.0.0-beta.7
   @angular-devkit/core       : 0.7.5
   @angular-devkit/schematics : 0.7.5
   @angular/cli               : 6.1.5
   @ionic/ng-toolkit          : 1.0.8
   @ionic/schematics-angular  : 1.0.6

System:

   NodeJS : v8.11.4 (/usr/local/bin/node)
   npm    : 5.6.0
   OS     : macOS High Sierra

Describe the Bug
When the on screen keyboard is displayed and hidden (from a footer input), it causes the UI to be scrolled up, the user interface is 'shifted' up by the width of the keyboard. When the keyboard then disappears, all the UI input is still shifted up by the width of the keyboard. This means all clicks in the UI are too high (ie inputs, etc). Also, when an input is in the content area, the keyboard will hide the input instead of scrolling it as it should.

Steps to Reproduce
Steps to reproduce the behavior:

  1. Clone: https://github.com/grabitlogistics/ios12kbd
  2. run npm i && ng build && npx cap add ios && npx cap copy ios && npx cap open ios
  3. build and run the app on an ios 12 device
  4. Click in an upper input to display the keyboard, then click outside to hide the keyboard and lose focus.
  5. Click on an input in the footer, making the keyboard appear and shift the inputs up. Click outside the input to hide the keyboard.
  6. All touch events have now been shifted up by the height of the keyboard, so to click in the input in the content OR the footer, you must click a few inches above the respective input (about the height of the keyboard)

Related Code
sample test case: https://github.com/grabitlogistics/ios12kbd

Additional Context
This ONLY happens on iOS 12, on a hardware device OR emulator. To test in an emulator, you must use the emulated keyboard in order to cause the UI to shift up.

I did some testing on multiple ios/emulators
XCode 10.0

iPhone 8 Plus iOS 11 -> no view shits everything is ok
iPhone 8 Plus iOS 12 -> no view shits everything is ok
IPhone X iOS 11.3 -> view shits but clicks are in place ()
IPhone X ans XS iOS 12 -> view shits and clicks are not accurate (the click action triggers lower the the touch point - same amount of view shit)

it seems related seems related to the extra track pad.
image

Anyone has the fix of this problem?

Can confirm that this issue exists in Cordova on iOS 12 as well https://github.com/apache/cordova-ios/issues/417

Hi all, i am facing that issue run on IOS 12 that was built with Xcode 10 (if you build with XCode 9, the issue don't appear). The root cause of issue is WKScrollview in WKWebview will be changed automatically content offset fit with height of keyboard in case keyboard is showed, but it didn't reset in case keyboard is hidden. That is my solution to fixed it, hope it help for you.

/**
 * observer notification when keyboard will hide
 */
[[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(keyboardWillHide)
                                                     name:UIKeyboardWillHideNotification
                                                   object:nil];

/////////////--------------------------//////////////
/*
 *Description: this method was trigger by selector keyboarwillhide from notification
 */
-(void)keyboardWillHide
{
    if (@available(iOS 12.0, *)) {
        WKWebView *webview = (WKWebView*)self.webView;
         for(UIView* v in webview.subviews){
            if([v isKindOfClass:NSClassFromString(@"WKScrollView")]){
                UIScrollView *scrollView = (UIScrollView*)v;
                [scrollView setContentOffset:CGPointMake(0, 0)];
             }
          }
     }
}

@jcesarmobile Regarding this comment: https://github.com/ionic-team/capacitor/issues/814#issuecomment-423609753

Is it not possible to set the SDK that XCode uses to build instead of having to install another version of XCode?

Here it says you can but it present a few problems
https://stackoverflow.com/questions/44985307/xcode-9-how-to-install-ios-10-sdk

OK, thanks for the feedback! Looks like it would be more of a headache to do this than installing an additional version of XCode.

any solution?

This was merged in corodva ionic webview
https://github.com/ionic-team/cordova-plugin-ionic-webview/commit/a67056816ee7fd2b8c74eef1854f2effbd145aea

Anyone knows how to integrate it with capacitor (I am not using cordova plugin)?

this is exactly the problem I am having - any use of the keyboard completely messes up the webview container and shifts it all up

this happens on the IOS device itself and has completely halted any chances of pushing out to customers

not sure if this is related however, my app will also always load first time with a slight gap under the app displaying the WKScrollView:

screenshot 2018-11-21 at 18 20 01
screenshot 2018-11-21 at 18 34 38

If I rerun the app it will sometime disappear, other times it wont - very sporadic

Hello, we are experiencing the same bug on iOS 12. When the keyboard closes, the webview keeps a "padding" from the bottom like if the keyboard was still visible.

Same here. But only after I upgraded to beta.11.

On every view I use the keyboard the whole app is shifts up and stays there until I rotate my phone to landscape and back.

Tabbed email input field with keyboard displayed.
img_3354
Keyboard loses focus and disappears but view stays shifted up.
img_3355

This is a blocker for all apps using the keyboard.

I experience the problem with XCode 10 and XCode 10.1 deployment target was 11.0.

I adapted the solution from https://github.com/ionic-team/capacitor/issues/814#issuecomment-432724789 to Swift and used it in the ios/App/App/AppDelegate.swift.

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide(notification:)), name: NSNotification.Name.UIKeyboardWillHide, object: nil)

    return true
  }

    @objc func keyboardWillHide(notification: NSNotification) {
        if #available(iOS 12.0, *) {
            let vc = self.window?.rootViewController as! CAPBridgeViewController
            for v in vc.bridgedWebView!.subviews {
                if v is UIScrollView {
                    let scrollView = v as! UIScrollView
                    scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false)
                }
            }
        }
    }

This fixes my issues.

That last solution works fine !
But I got a problem when I released. The AppDelegate.swift file is not taking into consideration, because of an optimization level during the release build.

That problem can be fixed by disabling the release optimizations :
In Xcode and "Build Settings", seek "optimization level" and select "None [-O0]".

As well as the keyboard input issues, I appear to be having essentially the same problem with the UI that pops up to handle selection of choices in a form select element.

I had the same issue. My fix was to add the following code to the index.html:

    window.addEventListener('keyboardWillHide', () => {
        window.scrollTo(0,0);
    document.body.scrollTop = 0;
    });

I had the same issue. My fix was to add the following code to the index.html:

    window.addEventListener('keyboardWillHide', () => {
        window.scrollTo(0,0);
  document.body.scrollTop = 0;
    });

Can confirm this works as a workaround, even if you update xcode to 10.1. Thank you @Nudelsieb !!

 if #available(iOS 11.0, *) {
             self.webView.scrollView.contentInsetAdjustmentBehavior = .never
 } else {
            self.automaticallyAdjustsScrollViewInsets = false
 }

And UIScrollViewDelegate

 func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) {
       Logger.print("scrollViewDidChangeAdjustedContentInset")
         if #available(iOS 12.0, *) {
           for view in webView.subviews {
                if let scrollView = view as? UIScrollView {
                   scrollView.setContentOffset(.zero, animated: true)
                 }
             }
        }
}

Worked

FWIW, I implemented @Nudelsieb's solution above, with a couple caveats.

  1. It requires cordova-plugin-keyboard for the keyboardWillHide event to trigger.

  2. When immediately transitioning from one input to another, the keyboardWillHide event will trigger, even though the keyboard does not actually have time to hide. That seemed to cause undesired scrolling. I added a setTimeout() to prevent it from executing immediately, which helped.

const scrollToTopFn = () => {
  if (window.Keyboard && !window.Keyboard.isVisible) {
    window.scrollTo(0,0);
    window.document.body.scrollTop = 0;
  }
};

window.addEventListener('keyboardDidHide', () => {
  window.setTimeout(scrollToTopFn, 100);
});

Finally got it to work.. I had to piece together this file here: fix keyboard displacement bug in iOS 12 WKWebView from @hghammoud

A straight copy didn't work, but taking each part and adding it to my CDVWKWebViewEngine.m file worked for me. I'll post my file if anyone needs it.

Here's my setup:
Ionic:
ionic (Ionic CLI) : 4.6.0
Ionic Framework : ionic-angular 3.9.2
@ionic/app-scripts : 3.1.10

Cordova:
cordova (Cordova CLI) : 8.1.2 ([email protected])
Cordova Platforms : android 7.1.4, ios 4.5.5
Cordova Plugins : cordova-plugin-ionic-keyboard 2.1.3, cordova-plugin-ionic-webview 1.1.1, (and 18 other plugins)

System:
ios-deploy : 1.9.2
NodeJS : v6.15.1 (/usr/local/Cellar/node/11.5.0/bin/node)
npm : 3.10.10
OS : macOS Mojave
Xcode : Xcode 10.1 Build version 10B61

  1. template
    <input  (blur)="scrollToTop()">
  1. page
  scrollToTop()
  {
    window.scrollTo(0,0);
  }
  1. down

期待有人能真正解决

You are right, does not happen with Xcode 9 / iOS 11.
But when you open an URL and load a website in a WKWebView everything works fine with iOS 12.
So it seems there is an issue in the Capacitor-Layer.

I can still reproduce this with iOS 11.4.1 and xcode 9.4.1

I think this should be fixed in next release, I made a few keyboard changes that combined seem to fix it.

@daniellizik what you comment should be a different issue as this only affects iOS 12 when using Xcode 10, not iOS 11 despite the Xcode version, and doesn't affect any iOS version when using Xcode 9.

@jcesarmobile Confirming that even with the latest release (4.0.2) the issue is not fixed.

Not sure what 4.0.2 you mean, but I’m talking about next capacitor release (1.0.0 beta 18, still not released)

@jcesarmobile Is the fix that you've made is in a branch that we can test?

Beta 18 is out, can you test with it?

@jcesarmobile
Part of this bug is fixed but there is still strange behaviour. Check my video:

https://drive.google.com/file/d/11Msg2lSiW1UoN-si6nL1QxLS93shBu9h/view

None of these occur on my Android-devices.

@jcesarmobile it is working great on my iPhone 7 test device running iOS 12! It seems to have completely fixed the bug introduced by Apple's webview in iOS 12. Thanks for this update.

when the given page loads, if it is ios etc etc, I attach a listener
document.addEventListener('focusout', resetScroll);

reset scroll is running on a short timeout and checks if the innerheight and height is the same, if se, it resets the scroll. This results in the scroll only being reset once the keyboard is removed from screen, rather than everytime the focus is changed.

 setTimeout(()=>{
        if (window.innerHeight == screen.height) {
            window.scrollTo(0, 0)    
        }
    },timeout);

I can't reproduce since beta 18 and calvinckho also confirmed that it's working fine for him, so I'm going to close.
For other strange behaviours report new issues.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

natevw picture natevw  Â·  3Comments

MatanYadaev picture MatanYadaev  Â·  3Comments

ebk46 picture ebk46  Â·  3Comments

stripathix picture stripathix  Â·  3Comments

nicobytes picture nicobytes  Â·  3Comments