Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### 🐛 Bug fixes

- **iOS**: `onPositionChange` no longer emits a slightly fractional `index` during the present transition — the detent offset is now learned before positions are emitted, so the index lands exactly on the target detent instead of snapping after `didPresent`. ([#731](https://github.com/lodev09/react-native-true-sheet/pull/731) by [@lodev09](https://github.com/lodev09))

## 3.11.5

### 🎉 New features
Expand Down
4 changes: 2 additions & 2 deletions example/bare/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2049,7 +2049,7 @@ PODS:
- ReactCommon/turbomodule/core
- ReactNativeDependencies
- Yoga
- RNTrueSheet (3.11.4):
- RNTrueSheet (3.11.5):
- hermes-engine
- RCTRequired
- RCTTypeSafety
Expand Down Expand Up @@ -2481,7 +2481,7 @@ SPEC CHECKSUMS:
RNGestureHandler: 2ff61eac036eaf89f6818bf4ed9c39771a17d134
RNReanimated: f735b1747a7a93bda7ca102c6d37a3cf54b6d5e8
RNScreens: 991cc417cd396602a6cf59a42139e5a9d91462a9
RNTrueSheet: 4dcd3292bcfe21502d48e0fd26b4fb3f68da0ad9
RNTrueSheet: 4dfa8b05506db77a67c8a2a54bc563338450408c
RNWorklets: 4931990f73bc8f347586918b2e13f11dfadf3b75
Yoga: 77dfa8673de2874e1855002ae59c68b8be9b007b

Expand Down
4 changes: 2 additions & 2 deletions example/shared/src/components/sheets/BasicSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ export const BasicSheet = forwardRef((props: BasicSheetProps, ref: Ref<TrueSheet
<Button text={`Add Content (${contentCount})`} onPress={addContent} />
{contentCount > 0 && <Button text="Remove Content" onPress={removeContent} />}
<ButtonGroup>
<Button text="Peek" onPress={() => resize(0)} />
<Button text="Auto" onPress={() => resize(1)} />
<Button text="Large" onPress={() => resize(2)} />
<Button text="80%" onPress={() => resize(1)} />
<Button text="Auto" onPress={() => resize(0)} />
</ButtonGroup>
<TrueSheetPeek />
<Spacer />
Expand Down
42 changes: 27 additions & 15 deletions ios/TrueSheetViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,12 @@ - (void)viewDidAppear:(BOOL)animated {

dispatch_async(dispatch_get_main_queue(), ^{
NSInteger index = [self currentDetentIndex];
[self learnOffsetForDetentIndex:index];

CGFloat detent = [self detentValueForIndex:index];
[self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
[self.delegate viewControllerDidFocus];

[self->_grabberView updateAccessibilityValueWithIndex:index detentCount:self->_detents.count];
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"did present"];
[self settleAtDetentIndex:index debug:@"did present"];
});

[self setupGestureRecognizer];
Expand Down Expand Up @@ -473,18 +471,17 @@ - (void)viewWillLayoutSubviews {
if (!_isTransitioning && !_isInteractiveDismiss) {
_isTrackingPositionFromLayout = YES;

UIViewController *presented = self.presentedViewController;
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
BOOL realtime = !hasPresentedController;

if (_pendingContentSizeChange || _pendingDetentsChange) {
_pendingContentSizeChange = NO;
_pendingDetentsChange = NO;
realtime = NO;
[self learnOffsetForDetentIndex:self.currentDetentIndex];
[self settleAtDetentIndex:self.currentDetentIndex debug:@"layout"];
} else {
UIViewController *presented = self.presentedViewController;
BOOL hasPresentedController = presented != nil && !presented.isBeingPresented && !presented.isBeingDismissed;
[self emitChangePositionDelegateWithPosition:self.currentPosition
realtime:!hasPresentedController
debug:@"layout"];
}

[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:realtime debug:@"layout"];
}
}

Expand All @@ -503,11 +500,10 @@ - (void)viewDidLayoutSubviews {
_pendingDetentIndex = -1;

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self learnOffsetForDetentIndex:pendingIndex];
CGFloat detent = [self detentValueForIndex:pendingIndex];
[self.delegate viewControllerDidChangeDetent:pendingIndex position:self.currentPosition detent:detent];
[self->_grabberView updateAccessibilityValueWithIndex:pendingIndex detentCount:self->_detents.count];
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"pending detent change"];
[self settleAtDetentIndex:pendingIndex debug:@"pending detent change"];
});
}

Expand Down Expand Up @@ -582,9 +578,8 @@ - (void)handlePanGesture:(UIPanGestureRecognizer *)gesture {
if (!_isTransitioning) {
dispatch_async(dispatch_get_main_queue(), ^{
NSInteger index = self.currentDetentIndex;
[self learnOffsetForDetentIndex:index];
[self->_grabberView updateAccessibilityValueWithIndex:index detentCount:self->_detents.count];
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"drag end"];
[self settleAtDetentIndex:index debug:@"drag end"];
});
}

Expand All @@ -602,6 +597,13 @@ - (void)setupTransitionTracker {

_isTransitioning = YES;

// Learn the resolver-vs-actual offset before emitting transition positions so
// the interpolated index lands exactly on the target detent. The presented
// frame is already final here (UIKit animates the layer, not the frame).
if (!self.isBeingDismissed) {
[self learnOffsetForDetentIndex:self.currentDetentIndex];
}

CGRect dismissedFrame = CGRectMake(0, self.screenHeight, 0, 0);
CGRect presentedFrame = CGRectMake(0, self.currentPosition, 0, 0);

Expand Down Expand Up @@ -810,6 +812,16 @@ - (void)learnOffsetForDetentIndex:(NSInteger)index {
[_detentCalculator learnOffsetForDetentIndex:index];
}

/**
* Learn the resolver-vs-actual offset at a settled detent, then emit the corrected
* position. Only valid when presentedView's frame is final — never mid-drag or
* mid-animation, where learning would store a bogus offset.
*/
- (void)settleAtDetentIndex:(NSInteger)index debug:(NSString *)debug {
[self learnOffsetForDetentIndex:index];
[self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:debug];
}

- (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
return [_detentCalculator findSegmentForPosition:position outIndex:outIndex outProgress:outProgress];
}
Expand Down
Loading