Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: Tab bar is unhidden too quickly after keyboard dismiss #25990

Closed
4 of 7 tasks
milanharia opened this issue Sep 22, 2022 · 28 comments · Fixed by #27417
Closed
4 of 7 tasks

bug: Tab bar is unhidden too quickly after keyboard dismiss #25990

milanharia opened this issue Sep 22, 2022 · 28 comments · Fixed by #27417
Labels
package: core @ionic/core package type: bug a confirmed bug report

Comments

@milanharia
Copy link

Prerequisites

Ionic Framework Version

  • v4.x
  • v5.x
  • v6.x
  • Nightly

Current Behavior

When the keyboard is dismissed the tab bar temporarily flashes on the screen as it is unhidden before the webview has expanded back to the full size of the viewport.

Simulator.Screen.Recording.-.iPhone.14.-.2022-09-22.at.13.55.17.mp4

Expected Behavior

The tab bar should not be unhidden until the web view has expanded back to its full size so that the tab bar does not flash in the center of the screen.

Steps to Reproduce

Use the default capacitor keyboard config:

// capacitor-config.ts
Keyboard: {
    resize: KeyboardResize.Native,
    resizeOnFullScreen: true,
},

This happens on any page with an IonTabBar.

Code Reproduction URL

No response

Ionic Info

Ionic:

Ionic CLI : 6.20.1 (/usr/local/lib/node_modules/@ionic/cli)
Ionic Framework : @ionic/react 6.2.8

Capacitor:

Capacitor CLI : 4.3.0
@capacitor/android : not installed
@capacitor/core : 4.3.0
@capacitor/ios : 4.3.0

Utility:

cordova-res : not installed globally
native-run : 1.7.0

System:

NodeJS : v16.14.2 (/usr/local/bin/node)
npm : 8.7.0
OS : macOS Monterey

Additional Information

No response

@maccman
Copy link

maccman commented Oct 10, 2022

We just ran into this too (cc @mlynch )

@vojto
Copy link

vojto commented Oct 10, 2022

Looks like this started in 6.2.3. Works well in 6.2.2. Could be related to #25746

@BinDohry
Copy link

BinDohry commented Nov 7, 2022

@liamdebeasi Any info on when this fix will be merged into production? Thanks!

@vojto
Copy link

vojto commented Nov 25, 2022

Still breaking in 6.3.8.

@aginvlad
Copy link

aginvlad commented Dec 3, 2022

Same for me in 6.3.9
I temporarily fixed it with Capacitor Keyboard

useEffect(() => {
  Keyboard.addListener('keyboardWillShow', () => setKeyboardShown(true));
  Keyboard.addListener('keyboardWillHide', () =>
    setTimeout(() => {
      setKeyboardShown(false);
    }, 10)
  );

  return () => {
    Keyboard.removeAllListeners();
  };
}, []);

And then provided keyboardShown variable to IonTabBar hidden prop

@milanharia
Copy link
Author

@aginvlad thanks for sharing. I seem to see the tab bar flash still because the tab bar is being unhidden a little too soon so I have changed the timeout delay to 100ms and seems to work consistently. The full code for anybody interested:

const App: React.FC = () => {
  const [keyboardIsOpen, setKeyboardIsOpen] = useState(false);

  useEffect(() => {
    Keyboard.addListener("keyboardWillShow", () => {
      setKeyboardIsOpen(true);
    });
    Keyboard.addListener("keyboardWillHide", () => {
      setTimeout(() => setKeyboardIsOpen(false), 100);
    });

    return () => {
      Keyboard.removeAllListeners();
    };
  }, []);

  return (
    <IonApp>
      <IonReactRouter>
        <IonTabs>
          <IonRouterOutlet>
            <Route exact path="/tab1">
              <Tab1 />
            </Route>
            <Route exact path="/tab2">
              <Tab2 />
            </Route>
            <Route path="/tab3">
              <Tab3 />
            </Route>
            <Route exact path="/">
              <Redirect to="/tab1" />
            </Route>
          </IonRouterOutlet>
          <IonTabBar hidden={keyboardIsOpen} slot="bottom">
            <IonTabButton tab="tab1" href="/tab1">
              <IonIcon icon={triangle} />
              <IonLabel>Tab 1</IonLabel>
            </IonTabButton>
            <IonTabButton tab="tab2" href="/tab2">
              <IonIcon icon={ellipse} />
              <IonLabel>Tab 2</IonLabel>
            </IonTabButton>
            <IonTabButton tab="tab3" href="/tab3">
              <IonIcon icon={square} />
              <IonLabel>Tab 3</IonLabel>
            </IonTabButton>
          </IonTabBar>
        </IonTabs>
      </IonReactRouter>
    </IonApp>
  );
};

export default App;

@liamdebeasi can you give any insight into when this issue will be resolved in production?

@vojto
Copy link

vojto commented Jan 18, 2023

Still breaking in 6.3.8.

No improvement in 6.4.2.

@vojto
Copy link

vojto commented Mar 30, 2023

Just upgraded to Ionic 7.0.0, and not seeing any improvement.

Can we get some feedback on this please?

I guess we'll have to use some hack, we're currently stuck on Ionic 6.2.2.

@maccman
Copy link

maccman commented Apr 6, 2023

@mlynch this one seems worth looking at :)

@mlynch
Copy link
Contributor

mlynch commented Apr 12, 2023

@maccman talked to the team and it's currently being looked at. Hope to have an update soon

@informatikTirol
Copy link

I have the same issue with Ionic 7, see #27386

@liamdebeasi
Copy link
Member

liamdebeasi commented May 8, 2023

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

7.0.6-dev.11683905366.13943af0

Install Example:

npm install @ionic/angular@7.0.6-dev.11683905366.13943af0

@informatikTirol
Copy link

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

7.0.6-dev.11683572927.1bfe3920

Install Example:

npm install @ionic/angular@7.0.6-dev.11683572927.1bfe3920

HI @liamdebeasi, the problem still exists for me after installing "7.0.6-dev.11683572927.1bfe3920".

@liamdebeasi
Copy link
Member

Can you run npm ls @ionic/core and provide the result? Also, do you have a screen recording of the issue with the dev build installed?

@informatikTirol
Copy link

Here you go

npm ls @ionic/core

└─┬ @ionic/angular@7.0.6-dev.11683572927.1bfe3920 └── @ionic/core@7.0.6-dev.11683572927.1bfe3920

RPReplay_Final1683637381.MP4

@liamdebeasi
Copy link
Member

Thanks! Can you send over a sample app that reproduces the issue? The sample app I have no longer reproduces the issue with the dev build installed.

@informatikTirol
Copy link

Here the adapted code (login removed, at start directly the test page)

https://www.dropbox.com/s/uaguy8k1feylaag/APP_RL_GITHUB_ISSUE_25990.zip?dl=0

Please a short response, then I will delete the File, thanks!

@liamdebeasi
Copy link
Member

I'm not able to install the dependencies in your project:

liamdebeasi@MacBook-Pro APP_RL_GITHUB_ISSUE_25990 % npm i
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: @angular/service-worker@15.2.2
npm ERR! Found: @angular/common@15.2.1
npm ERR! node_modules/@angular/common
npm ERR!   @angular/common@"^15.0.0" from the root project
npm ERR!   peer @angular/common@"15.2.1" from @angular/forms@15.2.1
npm ERR!   node_modules/@angular/forms
npm ERR!     @angular/forms@"^15.0.0" from the root project
npm ERR!     peer @angular/forms@">=14.0.0" from @ionic/angular@7.0.6-dev.11683572927.1bfe3920
npm ERR!     node_modules/@ionic/angular
npm ERR!       @ionic/angular@"^7.0.6-dev.11683572927.1bfe3920" from the root project
npm ERR!     1 more (ngx-chips)
npm ERR!   6 more (@angular/platform-browser, ...)
npm ERR! 
npm ERR! Could not resolve dependency:
npm ERR! peer @angular/common@"15.2.2" from @angular/service-worker@15.2.2
npm ERR! node_modules/@angular/service-worker
npm ERR!   @angular/service-worker@"^15.2.2" from the root project
npm ERR!   peerOptional @angular/service-worker@"^15.0.0" from @angular-devkit/build-angular@15.2.1
npm ERR!   node_modules/@angular-devkit/build-angular
npm ERR!     dev @angular-devkit/build-angular@"^15.0.0" from the root project
npm ERR! 
npm ERR! Conflicting peer dependency: @angular/common@15.2.2
npm ERR! node_modules/@angular/common
npm ERR!   peer @angular/common@"15.2.2" from @angular/service-worker@15.2.2
npm ERR!   node_modules/@angular/service-worker
npm ERR!     @angular/service-worker@"^15.2.2" from the root project
npm ERR!     peerOptional @angular/service-worker@"^15.0.0" from @angular-devkit/build-angular@15.2.1
npm ERR!     node_modules/@angular-devkit/build-angular
npm ERR!       dev @angular-devkit/build-angular@"^15.0.0" from the root project
npm ERR! 
npm ERR! Fix the upstream dependency conflict, or retry
npm ERR! this command with --force, or --legacy-peer-deps
npm ERR! to accept an incorrect (and potentially broken) dependency resolution.
npm ERR! 

Do you have a minimal reproduction of the issue?

@informatikTirol
Copy link

@liamdebeasi
Copy link
Member

The issue appears resolved when using the dev build and your sample reproduction:

RPReplay_Final1683640311.MP4

Can you make sure you do a clean build/sync when testing in your application?

npm run build && npx cap copy ios

@informatikTirol
Copy link

informatikTirol commented May 9, 2023

I did a clean build / sync, the bug is still there..

I have to toggle more often to reproduce the error, see video..

RPReplay_Final1683640684.MP4

The first time it happens on second 7

DeiZ

I use an Iphone 14 Pro

@liamdebeasi
Copy link
Member

Can you give this new dev build a try?

7.0.6-dev.11683643659.1ad8e6be

@informatikTirol
Copy link

It is better, but not completely gone.
It seems to me that it happens even more often when using "ion-split-pane" with "tabs", at least the APP "#25990 (comment)" flickers more...

@liamdebeasi
Copy link
Member

Do you have an example of this issue happening when using split pane?

@informatikTirol
Copy link

Here is the App with the split pane. I removed a few packages, so
npm install
should work on your machine.

https://www.dropbox.com/s/qbr278uqe8x9p5r/APP_RL_GITHUB_ISSUE_25990_2.zip?dl=0

Please a short response, then I will delete the File, thanks!

Here you see the bug:
zglx

Most likely the error occurs when going from tab 1 to tab 2, then directly activating the input.

@liamdebeasi
Copy link
Member

Thanks for testing. I think I have resolved the lingering issues if you are interested in giving this another test:

npm install @ionic/angular@7.0.6-dev.11683905366.13943af0

@informatikTirol
Copy link

Works, thank you!

liamdebeasi added a commit that referenced this issue May 16, 2023
Issue number: resolves #25990

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The tab bar and footer are being shown too soon after the keyboard
begins to hide. This is happening because the webview resizes _after_
the keyboard begins to dismiss. As a result, it is possible for the tab
bar and footer to briefly appear on the top of the keyboard in
environments where the webview resizes.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- The tab bar and footer wait until after the webview has resized before
showing again

| before | after |
| - | - |
| <video
src="https://user-images.githubusercontent.com/2721089/236905066-42ac17a5-a5bf-458b-9c62-005fcce05e20.MP4"></video>
| <video
src="https://user-images.githubusercontent.com/2721089/236905185-d2f539d1-6d93-4385-b1cb-24dd7aa06393.MP4"></video>
|

This code works by adding an optional parameter to the keyboard
controller callback called `waitForResize`. When defined, code within
Ionic can wait for the webview to resize as a result of the keyboard
opening or closing. Tab bar and footer wait for this `waitForResize`
promise to resolve before re-showing the relevant elements.

This `waitForResize` parameter is only only defined when all of the
following are two:

**1. The webview resize mode is known and is _not_ "None".**

If the webview resize mode is unknown then either the Keyboard plugin is
not installed (in which case the tab bar/footer are never hidden in the
first place) or the app is being deployed in a browser/PWA environment
(in which case the web content typically does not resize). If the
webview resize mode is "None" then that means the keyboard plugin is
installed, but the webview is configured to never resize when the
keyboard opens/closes. As a result, there is no need to wait for the
webview to resize.

**2. The webview has previously resized.**

If the keyboard is closed _before_ the opening keyboard animation
completes then it is possible for the webview to never resize. In this
case, the webview is at full height and the tab bar/footer can
immediately be re-shown.


------

Under the hood, we use a
[ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
to listen for when the web content resizes. Which element we listen on
depends on the resize mode set in the developer's Capacitor app. We
determine this in the `getResizeContainer` function.

From there, we wait for the ResizeObserver callback, then wait 1 more
frame so the promise resolves _after_ the resize has finished.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.0.6-dev.11683905366.13943af0`
brandyscarney pushed a commit that referenced this issue May 22, 2023
Issue number: resolves #25990

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

<!-- Please describe the current behavior that you are modifying. -->

The tab bar and footer are being shown too soon after the keyboard
begins to hide. This is happening because the webview resizes _after_
the keyboard begins to dismiss. As a result, it is possible for the tab
bar and footer to briefly appear on the top of the keyboard in
environments where the webview resizes.

<!-- Please describe the behavior or changes that are being added by
this PR. -->

- The tab bar and footer wait until after the webview has resized before
showing again

| before | after |
| - | - |
| <video
src="https://user-images.githubusercontent.com/2721089/236905066-42ac17a5-a5bf-458b-9c62-005fcce05e20.MP4"></video>
| <video
src="https://user-images.githubusercontent.com/2721089/236905185-d2f539d1-6d93-4385-b1cb-24dd7aa06393.MP4"></video>
|

This code works by adding an optional parameter to the keyboard
controller callback called `waitForResize`. When defined, code within
Ionic can wait for the webview to resize as a result of the keyboard
opening or closing. Tab bar and footer wait for this `waitForResize`
promise to resolve before re-showing the relevant elements.

This `waitForResize` parameter is only only defined when all of the
following are two:

**1. The webview resize mode is known and is _not_ "None".**

If the webview resize mode is unknown then either the Keyboard plugin is
not installed (in which case the tab bar/footer are never hidden in the
first place) or the app is being deployed in a browser/PWA environment
(in which case the web content typically does not resize). If the
webview resize mode is "None" then that means the keyboard plugin is
installed, but the webview is configured to never resize when the
keyboard opens/closes. As a result, there is no need to wait for the
webview to resize.

**2. The webview has previously resized.**

If the keyboard is closed _before_ the opening keyboard animation
completes then it is possible for the webview to never resize. In this
case, the webview is at full height and the tab bar/footer can
immediately be re-shown.

------

Under the hood, we use a
[ResizeObserver](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
to listen for when the web content resizes. Which element we listen on
depends on the resize mode set in the developer's Capacitor app. We
determine this in the `getResizeContainer` function.

From there, we wait for the ResizeObserver callback, then wait 1 more
frame so the promise resolves _after_ the resize has finished.

- [ ] Yes
- [x] No

<!-- If this introduces a breaking change, please describe the impact
and migration path for existing applications below. -->

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `7.0.6-dev.11683905366.13943af0`
@ionitron-bot
Copy link

ionitron-bot bot commented Jun 15, 2023

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Jun 15, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: core @ionic/core package type: bug a confirmed bug report
Projects
None yet
8 participants