From d60800f9c13b4855b4bf1d1d6c2ef436760e2903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20M=C4=99drek?= Date: Sat, 27 Sep 2025 17:22:43 +0200 Subject: [PATCH] fix(#241): password fields on iOS having doubled offset applied --- .../ios/AvoidSoftInputManager.mm | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/packages/react-native-avoid-softinput/ios/AvoidSoftInputManager.mm b/packages/react-native-avoid-softinput/ios/AvoidSoftInputManager.mm index bc33f2d..4063ccb 100644 --- a/packages/react-native-avoid-softinput/ios/AvoidSoftInputManager.mm +++ b/packages/react-native-avoid-softinput/ios/AvoidSoftInputManager.mm @@ -6,6 +6,13 @@ @implementation AvoidSoftInputManager { AvoidSoftInputAnimationHandler *animationHandler; AvoidSoftInputListener *listener; + CGFloat currentNewSoftInputHeight; + BOOL isDebounceActive; +} + ++ (int64_t)animationDebounceTimeout +{ + return (int64_t)(0.048 * NSEC_PER_SEC); } - (instancetype)init @@ -16,6 +23,8 @@ - (instancetype)init animationHandler.delegate = self; listener = [AvoidSoftInputListener new]; listener.delegate = self; + currentNewSoftInputHeight = 0; + isDebounceActive = NO; } return self; } @@ -119,9 +128,40 @@ - (void)onHeightChangedWithOldSoftInputHeight:(CGFloat)oldSoftInputHeight isOrientationChange:(BOOL)isOrientationChange { [self.delegate onHeightChanged:newSoftInputHeight]; - [animationHandler startAnimationFrom:oldSoftInputHeight - to:newSoftInputHeight - withOrientationChange:isOrientationChange]; + [self debounceAnimation:oldSoftInputHeight + newSoftInputHeight:newSoftInputHeight + isOrientationChange:isOrientationChange]; +} + +- (void)debounceAnimation:(CGFloat)oldSoftInputHeight + newSoftInputHeight:(CGFloat)newSoftInputHeight + isOrientationChange:(BOOL)isOrientationChange +{ + // Save latest target value + currentNewSoftInputHeight = newSoftInputHeight; + // If animation call was already scheduled, let's return early + if (isDebounceActive) { + return; + } + // Activated debounce flag + isDebounceActive = YES; + __weak AvoidSoftInputManager *weakSelf = self; + // Debounce animation call for 48ms and invoke with the latest target value + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, [AvoidSoftInputManager animationDebounceTimeout]), + dispatch_get_main_queue(), + ^{ + AvoidSoftInputManager *strongSelf = weakSelf; + if (!strongSelf) { + return; + } + CGFloat targetHeight = strongSelf->currentNewSoftInputHeight; + [strongSelf->animationHandler startAnimationFrom:oldSoftInputHeight + to:targetHeight + withOrientationChange:isOrientationChange]; + strongSelf->isDebounceActive = NO; + strongSelf->currentNewSoftInputHeight = 0; + }); } @end