Ionic-framework: bug: input focus scroll does not work when viewport resizing is disabled

Created on 13 Jun 2019  ·  28Comments  ·  Source: ionic-team/ionic-framework

Bug Report

If KeyboardResize is set to false the keyboard overlaps the webview and does not resize it. The Content scrolling on input focus does not work in this case.
As it is at the moment, I prefer the keyboard overlapping the webview and not resizing it because resizing feels clunky with jumped animations, tab-bar hiding bugs, etc.

Ionic version:


[x] 4.5.0

Current behavior:
Content scrolling not available with KeyboardResize=false because webview is full-size

Expected behavior:
If KeyboardResize is set to false, ion-content adds a keyboard-offset as padding-bottom so the content is bigger and content scrolling works.

Steps to reproduce:
Add <preference name="KeyboardResize" value="false" /> to config.xml, create a page with a long form that uses more than half of the screen

Related code:
Config.xml

<preference name="KeyboardResize" value="false" />

Page.html

<ion-content>
  <ion-list>
    <ion-item>
      <ion-label position="stacked">
        <h3>Strasse, Nr.</h3>
      </ion-label>
      <ion-input type="text" name="street" inputmode="text" autocomplete="street-address" [(ngModel)]="address.street"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label position="stacked">
        <h3>PLZ</h3>
      </ion-label>
      <ion-input type="number" name="postalCode" pattern="[0-9]*" autocomplete="postal-code"
                 [(ngModel)]="address.postalCode"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label position="stacked">
        <h3>Ort</h3>
      </ion-label>
      <ion-input type="text" name="location" inputmode="text" autocomplete="locality" [(ngModel)]="address.location"></ion-input>
    </ion-item>

    <ion-item>
      <ion-label position="stacked">
        <h3>Land</h3>
      </ion-label>
      <ion-input type="text" name="country" inputmode="text" autocomplete="country" [(ngModel)]="address.country"></ion-input>
    </ion-item>
  </ion-list>
</ion-content>

Other information:

Image from iOS

Ionic info:

Ionic:

   ionic (Ionic CLI)             : 4.12.0 (/usr/local/lib/node_modules/ionic)
   Ionic Framework               : @ionic/angular 4.5.0
   @angular-devkit/build-angular : 0.12.4
   @angular-devkit/schematics    : 7.2.4
   @angular/cli                  : 7.2.4
   @ionic/angular-toolkit        : 1.4.1

Cordova:

   cordova (Cordova CLI) : 9.0.0 ([email protected])
   Cordova Platforms     : android 8.0.0, ios 5.0.1
   Cordova Plugins       : cordova-plugin-ionic-keyboard 2.1.3, cordova-plugin-ionic-webview 2.4.1, (and 14 other plugins)

System:

   Android SDK Tools : 26.1.1 (/Users/pascalgraf/Library/Android/sdk)
   ios-deploy        : 1.9.4
   ios-sim           : 8.0.1
   NodeJS            : v10.14.1 (/usr/local/bin/node)
   npm               : 6.9.0
   OS                : macOS Mojave
   Xcode             : Xcode 10.2.1 Build version 10E1001

core bug

Most helpful comment

I spent some time reworking Scroll Assist, and I fixed a few things that should significantly improve the user experience:

  1. We now listen on either the input or its parent ion-item (if one exists) to initiate scroll assist. This resolved an issue where scroll assist would sometimes not work because users tapped ion-item. ion-item delegates focus to components like ion-input, so the keyboard would have still opened.

  2. Updated scroll padding so that it works with ion-input. This is the fix that I mentioned in https://github.com/ionic-team/ionic-framework/issues/18532#issuecomment-729223021. This should resolve the thread's issue where scroll assist was not working when viewport resizing was disabled.

  3. We no longer wait for the keyboard to finish opening or for the webview to resize. We can do this because by the time we need to scroll the input into view, the scroll padding utility has already added the scroll height we need. This should improve the performance of scroll assist and make it seem more responsive.

  4. We have improved our methods for determining how much we need to scroll the input by. If you are in Capacitor, we use the precise keyboard height given in the keyboardWillOpen event. If you are in a browser/PWA we use the approximate keyboard height given in the keyboardHeight config option. This should improve the reliability of the scrolling and it should resolve the issue where tapping inputs would always cause the content to scroll upwards.

Due to the significance of scroll assist, I have decided that these changes warrant more testing than usual. As a result, I am going to remove this issue from its existing milestone. My plan is to introduce this change as an experimental, opt-in feature in an upcoming feature release of Ionic Framework with the stable version shipping in Ionic Framework v6.

I am doing some additional testing, but I will provide a dev build here soon.

All 28 comments

Did you find any solution?, I am having same issue. Seems like it's not adding --keyboard-offset.

As I see it, --keyboard-offset does not even exist if KeyboardResize is set to false, as the Keyboard overlays the App.

Same problem,any thing new for fix it ?

I am having a similar issue, if I use a native input on my page, the viewport does not adjust to have the input on screen. However, if I use IonInput it does animate the viewport to show the input instead of it being hidden behind the keyboard. I tried digging in to the component source code to see what could be fixing that, but no luck yet.

Change @ionic/angular to this version work for me
"@ionic/angular": "5.2.2",

Can everyone try the following dev build and let me know if it resolves the issue?

Angular

npm install @ionic/[email protected]

React

npm install @ionic/[email protected] @ionic/[email protected]

Vue

npm install @ionic/[email protected] @ionic/[email protected]

Stencil/Vanilla JavaScript

npm install @ionic/[email protected]

I installed the dev build for an Ionic Angular project (https://github.com/Spinnenzunge/ionic-framework-issue-18532) and it seems to work with disabled viewport resizing with this build. Can't say anything for vanilla, react and vue. Having an active cursor and scrolling the webview still feels a little bit janky but I guess that does not have anything to do with Ionic.

Edit: I only built and testet it on iOS, I have a new Mac and had not time to setup Android Studio atm. Maybe someone else can test it for Android, otherwise I will try to do it in a few days.

Thanks for testing! The input focus scroll should not impact Android since I think it always resizes the webview (I don't think there's a way to turn that off easily besides android:windowSoftInputMode="adjustResize"?)

Exactly, you can't do it with the cordova keyboard plugin preference but you can add it directly to the AndroidManifest via the edit-config tag in config.xml and set the windowSoftInputMode like that. We used to have that in our App, so I feel like it is worth checking the Input Focus Scrolling as well on Android with the keyboard resizing disabled.

I will add the property to the Repository https://github.com/Spinnenzunge/ionic-framework-issue-18532 so maybe if someone has time right now he or she can test it.

Makes sense. I tested your repo, and it seems to work fine on Android. Would love to have other people test too!

For anyone else testing: The scroll assist is only enabled by default on iOS, so if you want to use it on Android you will need to add inputShims: true to your config:

IonicModule.forRoot({ inputShims: true })

@liamdebeasi While the dev version does with the delay I noticed, in some ways it's worse. Try it with https://github.com/aparajita/ionic-input-problem.

  • Inputs 1-5 are always scrolling the input to the top. Is that what you meant to do?
  • On the main page, input 6 scrolls down to be just above the keyboard, where it should be. But it's rather disconcerting for the user to jump from the top in the other inputs to the bottom.
  • In the modal, input 6 is cut off by the accessory bar.

The whole UX is rather jumpy now. I'm sorry to say I wouldn't want to present this to my users. I have a solution using non-scrollable ion-content and tracking focus + keyboard show that is working quite well, I'll have to stick with that for now. I'll pass it along when I have it fully tested, it may work well with scrollable ion-content as well.

Can you provide some steps to reproduce? I am not seeing that on my end (maybe I need to test with an iPhone 7 here too?)

@liamdebeasi Try it in an iPhone 8 simulator, I get the same result there as on my iPhone 7.

@liamdebeasi To reproduce it, run my demo app, and tap sequentially in the inputs from top to bottom.

Hey @liamdebeasi, my solution to the forcing inputs into view is here: https://github.com/aparajita/ws-input-visibility

Unfortunately you won't be able to see how it acts unless you disable what you're doing. A video of how it looks in action using ionic/[email protected] is here.

To use it, do this in App.vue:

import { ensureVisibility } from 'ws-input-visibility';
...
setup() {
   onBeforeMount(() => {
      ensureVisibility();
  }
}

That's all that's necessary. Here's why I prefer this solution:

  • It works with both scrollable and non-scrollable ion-content.
  • It moves the input the minimum distance possible to place it in view.
  • If an input is already completely in view, it is not moved.
  • Priority is given first to placing the input completely above the keyboard. If it already is above the keyboard, or moving it would cause the top of the input to be out of view, the top is moved into view.
  • It works identically with clicking and tab motion.
  • The transitions can be configured individually for show/hide and ios/android, or removed entirely.
  • The margin between the keyboard and the input is configurable.

I hope you will consider using this code. From a UX perspective it is a more predictable experience. 🙏

Thanks, I appreciate you digging in and helping out with fixing this issue. I agree that your solution leads to a smoother transition, but what concerns me with this approach is that it is using transform which is going to physically move ion-content offscreen. What this means is that if someone has the keyboard open in a form, they will be unable to scroll to the top-most input since the content was partially transformed offscreen.

Scroll Assist was written several years ago (around the Ionic 2/3 days 🙂 ) so it's probably time for a revamp in how we handle it. I will do some research as to how we can better implement Scroll Assist in Ionic Framework so it's less janky.

Looks like Apple has a guide on this for apps built with Swift/Objective-C, so I think this is where I will start my research: https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html

Thanks for taking a look @liamdebeasi, I really want to get this solved.

I only used transform as a temporary hack because I couldn't get scrolling to work — I tried calling ionContent.scrollByPoint() without success. The exact same code will work with a scroll by replacing the lines in moveElementIntoView() that set the transform and transition. Everything else can remain the same, it's the calculations that matter.

I just read the Apple docs you referenced. They say it's up to us to scroll the input into view, there is no real guidance other than that.

If you tell me where the Scroll Assist code is I'll look into making a PR that integrates my code.

I spent some time reworking Scroll Assist, and I fixed a few things that should significantly improve the user experience:

  1. We now listen on either the input or its parent ion-item (if one exists) to initiate scroll assist. This resolved an issue where scroll assist would sometimes not work because users tapped ion-item. ion-item delegates focus to components like ion-input, so the keyboard would have still opened.

  2. Updated scroll padding so that it works with ion-input. This is the fix that I mentioned in https://github.com/ionic-team/ionic-framework/issues/18532#issuecomment-729223021. This should resolve the thread's issue where scroll assist was not working when viewport resizing was disabled.

  3. We no longer wait for the keyboard to finish opening or for the webview to resize. We can do this because by the time we need to scroll the input into view, the scroll padding utility has already added the scroll height we need. This should improve the performance of scroll assist and make it seem more responsive.

  4. We have improved our methods for determining how much we need to scroll the input by. If you are in Capacitor, we use the precise keyboard height given in the keyboardWillOpen event. If you are in a browser/PWA we use the approximate keyboard height given in the keyboardHeight config option. This should improve the reliability of the scrolling and it should resolve the issue where tapping inputs would always cause the content to scroll upwards.

Due to the significance of scroll assist, I have decided that these changes warrant more testing than usual. As a result, I am going to remove this issue from its existing milestone. My plan is to introduce this change as an experimental, opt-in feature in an upcoming feature release of Ionic Framework with the stable version shipping in Ionic Framework v6.

I am doing some additional testing, but I will provide a dev build here soon.

Thanks @liamdebeasi for taking up the challenge. Looking forward to testing it out.

Ok here is my first draft of the revised scroll assist. A few things to note about this dev build:

  1. I am currently focusing on getting this working with webview resizing disabled. As a result, scroll assist will not work if webview resizing is enabled currently. I have plans to address this later. Here is how to disable webview resizing:

Capacitor
capacitor.config.json

{
  "plugins": {
    "Keyboard": {
      "resize": "none"
    }
  }
}

Cordova
config.xml
```xml

````

  1. When running this in a web browser or as a PWA, we do not know the exact height of the keyboard and need to guess. As a result, the amount we scroll by will be slightly more than needed. At the moment I do not have plans to address this since there is no fast way to get the exact keyboard height. I can use Visual Viewport, but that would mean not being able to scroll until the keyboard is fully open causing scroll assist to only start after a ~400ms delay.

  2. When running the demo that @aparajita provided in a web browser, focusing input 6 then input 5 causes Safari to shift the entire view. I am still investigating why this is happening. This does not impact Capacitor/Cordova. Additionally, a similar behavior appears when using the older scroll assist.

  3. I have not addressed the following issue from https://github.com/ionic-team/ionic-framework/issues/22513 yet:

On both Chrome and iOS, the content of an ion-textarea with left padding shifts left and then right when focused.

Here is the dev build for those who would like to test: 5.6.0-dev.202012021910.3a763f4

Thanks @liamdebeasi, I'll give it a shot and let you know what I find.

I am currently focusing on getting this working with webview resizing disabled.

Honestly, I don't know why anyone would want the webview to resize when the keyboard appears. It wreaks havoc on the layout.

Unfortunately, @liamdebeasi, for me that dev build is not working on iOS. On the other hand, I discovered that if I use the current release build and set the keyboard resize mode to "body", the input fields are being scrolled into view perfectly.

Unfortunately, @liamdebeasi, for me that dev build is not working on iOS.

Can you be more specific as to what is not working?

Inputs are not scrolling into view. They move one pixel up I believe. This is with a different app than the input torture test app. I'll be pushing it to github today and you can try yourself.

Did you disable webview resizing on that app? I noticed in your other example your config does not set the resize mode properly: https://github.com/aparajita/ionic-input-problem/blob/main/capacitor.config.json#L12 (Should be "resize": "none" not "resizeMode": "none")

Yes, it was "resize": "none".

Was this page helpful?
0 / 5 - 0 ratings

Related issues

MrBokeh picture MrBokeh  ·  3Comments

alan-agius4 picture alan-agius4  ·  3Comments

fdnhkj picture fdnhkj  ·  3Comments

daveshirman picture daveshirman  ·  3Comments

danbucholtz picture danbucholtz  ·  3Comments