kirillzyusko / react-native-keyboard-controller

Keyboard manager which works in identical way on both iOS and Android
https://kirillzyusko.github.io/react-native-keyboard-controller/
MIT License
1.62k stars 67 forks source link

feat: eliminate one frame delay in `onMove` handler on iOS #412

Closed kirillzyusko closed 5 months ago

kirillzyusko commented 5 months ago

πŸ“œ Description

Improved precision of onMove handler to drive animation frame-in-frame using this handler.

πŸ’‘ Motivation and Context

This problem was introduced since https://github.com/kirillzyusko/react-native-keyboard-controller/pull/87 was merged.

The thing is that we are reading layer properties using CADisplayLink. The problem is that in this case we read always out-of-date values, because CADisplayLink fires a callback before the next frame - in this case we'll always read a value for a previous frame. Thus we have always one frame delay.

The idea of this PR is to have an ability to read future values and use them. For that I had to replicate how SpringAnimation calculates its values (i. e. the math behind it, because default CASpringAnimation class doesn't give us such ability).

Now we know all variables - current timestamp, beginning of the animation, next scheduled timestamp etc. so we can calculate the next frame.

However it's also tricky, because in my observation:

So the formula like targetTimestamp - beginTime + duration may work good if XCode debugger is not attached, but will produce junky animation when debugger is attached.

I struggled a lot and found that CADisplayLink can fire its updates with monotonic intervals (i. e. 16ms) but the keyboard position can be at this point of time in a different values (it can be 12ms or 20ms or any other value comparing to the previous frame) - see the image below as an example of values captured on simulator:

image

So in the end I decided to stick to slightly different approach - when CADisplayLink callback is fired, then we read prev frame keyboard position, and instead of calculation duration value based on absolute time intervals we are doing reversing of current keyboard frame, i. e. for a give keyboard position value we are calculating the duration (of animation), and only then calculate next frame as durationFromKeyboardPosition + frameDuration. Such approach gives a decent result on a real device (debug/release, debugger attached/detached etc.).

Last but not least - this PR improves precision for plain keyboard show/hide movements. However on iOS in certain cases animation can be not only CASpringAniamtion - right now I handle it in a PR in a fallback way, i. e. if it's not a spring animation we fallback to the mechanism introduced in https://github.com/kirillzyusko/react-native-keyboard-controller/pull/87 and simply read layer properties.

Later on I'll cover this case as well and will synchronize animations for all types of animations.

[!WARNING] I can not test this PR on ProMotion device because I don't have it at the moment. However I'll get it back after 10.05.24, so I'll test it there and will see if any code adjustments are needed. Attached video proof, that on iPhone 14 Pro animations are running frame in frame.

πŸ“’ Changelog

Docs

E2E

JS

iOS

πŸ€” How Has This Been Tested?

Tested manually on:

πŸ“Έ Screenshots (if appropriate):

iPhone 11, iPhone 14, iPhone 6s

KeyboardAnimation

When Simulator iPhone 11 iPhone 6s iPhone 14 Pro
Before N/A
After

KeyboardAvoidingView

When Simulator iPhone 11 iPhone 6s iPhone 14 Pro
Before N/A
After

KeyboardAwareScrollView

When Simulator iPhone 11 iPhone 6s iPhone 14 Pro
Before N/A
After

πŸ“ Checklist

github-actions[bot] commented 5 months ago

πŸ“Š Package size report

Current size Target Size Difference
131402 bytes 129703 bytes 1699 bytes πŸ“ˆ
github-actions[bot] commented 5 months ago

PR Preview Action v1.4.7 :---: Preview removed because the pull request was closed. 2024-05-02 08:37 UTC

argos-ci[bot] commented 5 months ago

The latest updates on your projects. Learn more about Argos notifications β†—οΈŽ

Build Status Details Updated (UTC)
default (Inspect) βœ… No change detected - May 2, 2024, 8:14 AM