Closed kirillzyusko closed 5 months ago
Current size | Target Size | Difference |
---|---|---|
131402 bytes | 129703 bytes | 1699 bytes π |
PR Preview Action v1.4.7 :---: Preview removed because the pull request was closed. 2024-05-02 08:37 UTC
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 |
π 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 usingCADisplayLink
. The problem is that in this case we read always out-of-date values, becauseCADisplayLink
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 defaultCASpringAnimation
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: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 calculationduration
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 asdurationFromKeyboardPosition + 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 readlayer
properties.Later on I'll cover this case as well and will synchronize animations for all types of animations.
π’ Changelog
Docs
onMove
handler;E2E
KeyboardAnimation
component;JS
gray
circle inKeyboardAnimation
component which is driven byonMove
handler;FlatList
example -> useonMove
handler to handle a case when layout animation is not schedule (when keyboard closed viakeyboardShouldPersistTaps
)iOS
core
folder;SpringAnimation
class;CASpringAnimation
;SpringAnimation
class is available, then calculate keyboard position using this class;onMove
will not be perfectly synchronized in any way (layout animation will override our calculations);π€ How Has This Been Tested?
Tested manually on:
<- I tested it during development, however I made some changes in final code and can not test it again because this device is not with me at the moment - I'll test later when device comes back to meπΈ Screenshots (if appropriate):
iPhone 11, iPhone 14, iPhone 6s
KeyboardAnimation
KeyboardAvoidingView
KeyboardAwareScrollView
π Checklist