@lodev09/react-native-true-sheet 3.4.1 → 3.5.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@
9
9
  #import "TrueSheetViewController.h"
10
10
  #import "TrueSheetContentView.h"
11
11
  #import "core/TrueSheetBlurView.h"
12
+ #import "core/TrueSheetDetentCalculator.h"
12
13
  #import "core/TrueSheetGrabberView.h"
13
14
  #import "utils/GestureUtil.h"
14
15
  #import "utils/WindowUtil.h"
@@ -36,8 +37,7 @@
36
37
 
37
38
  TrueSheetBlurView *_blurView;
38
39
  TrueSheetGrabberView *_grabberView;
39
-
40
- NSMutableArray<NSNumber *> *_resolvedDetentPositions;
40
+ TrueSheetDetentCalculator *_detentCalculator;
41
41
  }
42
42
 
43
43
  #pragma mark - Initialization
@@ -65,7 +65,7 @@
65
65
 
66
66
  _blurInteraction = YES;
67
67
  _insetAdjustment = @"automatic";
68
- _resolvedDetentPositions = [NSMutableArray array];
68
+ _detentCalculator = [[TrueSheetDetentCalculator alloc] initWithMeasurements:self];
69
69
  }
70
70
  return self;
71
71
  }
@@ -464,131 +464,27 @@
464
464
  }
465
465
 
466
466
  - (void)storeResolvedPositionForIndex:(NSInteger)index {
467
- if (index >= 0 && index < (NSInteger)_resolvedDetentPositions.count) {
468
- _resolvedDetentPositions[index] = @(self.currentPosition);
469
- }
467
+ [_detentCalculator storeResolvedPositionForIndex:index];
470
468
  }
471
469
 
472
470
  - (CGFloat)estimatedPositionForIndex:(NSInteger)index {
473
- if (index < 0 || index >= (NSInteger)_resolvedDetentPositions.count)
474
- return 0;
475
-
476
- CGFloat storedPos = [_resolvedDetentPositions[index] doubleValue];
477
- if (storedPos > 0) {
478
- return storedPos;
479
- }
480
-
481
- CGFloat detentValue = [self detentValueForIndex:index];
482
- CGFloat basePosition = self.screenHeight - (detentValue * self.screenHeight);
483
-
484
- for (NSInteger i = 0; i < (NSInteger)_resolvedDetentPositions.count; i++) {
485
- CGFloat pos = [_resolvedDetentPositions[i] doubleValue];
486
- if (pos > 0) {
487
- CGFloat knownDetent = [self detentValueForIndex:i];
488
- CGFloat expectedPos = self.screenHeight - (knownDetent * self.screenHeight);
489
- CGFloat offset = pos - expectedPos;
490
- return basePosition + offset;
491
- }
492
- }
493
-
494
- return basePosition;
471
+ return [_detentCalculator estimatedPositionForIndex:index];
495
472
  }
496
473
 
497
474
  - (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
498
- NSInteger count = _resolvedDetentPositions.count;
499
- if (count == 0) {
500
- *outIndex = -1;
501
- *outProgress = 0;
502
- return NO;
503
- }
504
-
505
- CGFloat firstPos = [self estimatedPositionForIndex:0];
506
-
507
- // Above first detent - interpolating toward closed
508
- if (position > firstPos) {
509
- CGFloat range = self.screenHeight - firstPos;
510
- *outIndex = -1;
511
- *outProgress = range > 0 ? (position - firstPos) / range : 0;
512
- return NO;
513
- }
514
-
515
- // Single detent - at or above the detent
516
- if (count == 1) {
517
- *outIndex = 0;
518
- *outProgress = 0;
519
- return NO;
520
- }
521
-
522
- CGFloat lastPos = [self estimatedPositionForIndex:count - 1];
523
-
524
- // Below last detent
525
- if (position < lastPos) {
526
- *outIndex = count - 1;
527
- *outProgress = 0;
528
- return NO;
529
- }
530
-
531
- // Between detents
532
- for (NSInteger i = 0; i < count - 1; i++) {
533
- CGFloat pos = [self estimatedPositionForIndex:i];
534
- CGFloat nextPos = [self estimatedPositionForIndex:i + 1];
535
-
536
- if (position <= pos && position >= nextPos) {
537
- CGFloat range = pos - nextPos;
538
- *outIndex = i;
539
- *outProgress = range > 0 ? (pos - position) / range : 0;
540
- return YES;
541
- }
542
- }
543
-
544
- *outIndex = count - 1;
545
- *outProgress = 0;
546
- return NO;
475
+ return [_detentCalculator findSegmentForPosition:position outIndex:outIndex outProgress:outProgress];
547
476
  }
548
477
 
549
478
  - (CGFloat)interpolatedIndexForPosition:(CGFloat)position {
550
- NSInteger index;
551
- CGFloat progress;
552
- BOOL found = [self findSegmentForPosition:position outIndex:&index outProgress:&progress];
553
-
554
- if (!found) {
555
- if (index == -1) {
556
- return -progress;
557
- }
558
- return index;
559
- }
560
-
561
- return index + fmax(0, fmin(1, progress));
479
+ return [_detentCalculator interpolatedIndexForPosition:position];
562
480
  }
563
481
 
564
482
  - (CGFloat)interpolatedDetentForPosition:(CGFloat)position {
565
- NSInteger index;
566
- CGFloat progress;
567
- BOOL found = [self findSegmentForPosition:position outIndex:&index outProgress:&progress];
568
-
569
- if (!found) {
570
- if (index == -1) {
571
- CGFloat firstDetent = [self detentValueForIndex:0];
572
- return fmax(0, firstDetent * (1 - progress));
573
- }
574
- return [self detentValueForIndex:index];
575
- }
576
-
577
- CGFloat detent = [self detentValueForIndex:index];
578
- CGFloat nextDetent = [self detentValueForIndex:index + 1];
579
- return detent + progress * (nextDetent - detent);
483
+ return [_detentCalculator interpolatedDetentForPosition:position];
580
484
  }
581
485
 
582
486
  - (CGFloat)detentValueForIndex:(NSInteger)index {
583
- if (index >= 0 && index < (NSInteger)_detents.count) {
584
- CGFloat value = [_detents[index] doubleValue];
585
- if (value == -1) {
586
- CGFloat autoHeight = [self.contentHeight floatValue] + [self.headerHeight floatValue];
587
- return autoHeight / self.screenHeight;
588
- }
589
- return value;
590
- }
591
- return 0;
487
+ return [_detentCalculator detentValueForIndex:index];
592
488
  }
593
489
 
594
490
  #pragma mark - Sheet Configuration
@@ -611,7 +507,7 @@
611
507
  return;
612
508
 
613
509
  NSMutableArray<UISheetPresentationControllerDetent *> *detents = [NSMutableArray array];
614
- [_resolvedDetentPositions removeAllObjects];
510
+ [_detentCalculator clearResolvedPositions];
615
511
 
616
512
  CGFloat autoHeight = [self.contentHeight floatValue] + [self.headerHeight floatValue];
617
513
 
@@ -621,9 +517,9 @@
621
517
  withAutoHeight:autoHeight
622
518
  atIndex:index];
623
519
  [detents addObject:sheetDetent];
624
- [_resolvedDetentPositions addObject:@(0)];
625
520
  }
626
521
 
522
+ [_detentCalculator setDetentCount:self.detents.count];
627
523
  sheet.detents = detents;
628
524
 
629
525
  if (self.dimmed && [self.dimmedDetentIndex integerValue] == 0) {
@@ -0,0 +1,87 @@
1
+ //
2
+ // Created by Jovanni Lo (@lodev09)
3
+ // Copyright (c) 2024-present. All rights reserved.
4
+ //
5
+ // This source code is licensed under the MIT license found in the
6
+ // LICENSE file in the root directory of this source tree.
7
+ //
8
+
9
+ #import <UIKit/UIKit.h>
10
+
11
+ NS_ASSUME_NONNULL_BEGIN
12
+
13
+ /**
14
+ Protocol that provides dynamic measurements for detent calculations.
15
+ Implemented by TrueSheetViewController to supply real-time values.
16
+ */
17
+ @protocol TrueSheetDetentMeasurements <NSObject>
18
+
19
+ @property (nonatomic, readonly) CGFloat screenHeight;
20
+ @property (nonatomic, readonly) CGFloat currentPosition;
21
+ @property (nonatomic, strong, readonly) NSArray<NSNumber *> *detents;
22
+ @property (nonatomic, strong, readonly, nullable) NSNumber *contentHeight;
23
+ @property (nonatomic, strong, readonly, nullable) NSNumber *headerHeight;
24
+
25
+ @end
26
+
27
+ /**
28
+ Encapsulates all detent-related calculations for the sheet.
29
+ Uses the TrueSheetDetentMeasurements protocol to read dynamic values.
30
+ */
31
+ @interface TrueSheetDetentCalculator : NSObject
32
+
33
+ - (instancetype)initWithMeasurements:(id<TrueSheetDetentMeasurements>)measurements;
34
+
35
+ /**
36
+ Returns the detent value (0-1 fraction) for a given index.
37
+ For auto (-1) detents, calculates based on content + header height.
38
+ */
39
+ - (CGFloat)detentValueForIndex:(NSInteger)index;
40
+
41
+ /**
42
+ Returns the estimated screen position (Y coordinate) for a detent index.
43
+ Uses stored resolved positions if available, otherwise calculates from detent value.
44
+ */
45
+ - (CGFloat)estimatedPositionForIndex:(NSInteger)index;
46
+
47
+ /**
48
+ Stores the current resolved position for a detent index.
49
+ Called when the sheet settles at a detent.
50
+ */
51
+ - (void)storeResolvedPositionForIndex:(NSInteger)index;
52
+
53
+ /**
54
+ Finds the segment between detents for a given position.
55
+ Returns YES if position is between two detents, NO if at edges.
56
+ outIndex: The lower detent index of the segment (-1 if above first detent)
57
+ outProgress: Progress within the segment (0-1)
58
+ */
59
+ - (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress;
60
+
61
+ /**
62
+ Returns a continuous index value representing position between detents.
63
+ Examples: 0.5 means halfway between detent 0 and 1
64
+ -0.3 means 30% toward closed from detent 0
65
+ */
66
+ - (CGFloat)interpolatedIndexForPosition:(CGFloat)position;
67
+
68
+ /**
69
+ Returns the interpolated detent value for a given position.
70
+ Useful for getting a smooth detent value during drag.
71
+ */
72
+ - (CGFloat)interpolatedDetentForPosition:(CGFloat)position;
73
+
74
+ /**
75
+ Clears all stored resolved positions.
76
+ Called when detents configuration changes.
77
+ */
78
+ - (void)clearResolvedPositions;
79
+
80
+ /**
81
+ Sets the count of detents (initializes storage for resolved positions).
82
+ */
83
+ - (void)setDetentCount:(NSInteger)count;
84
+
85
+ @end
86
+
87
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,170 @@
1
+ //
2
+ // Created by Jovanni Lo (@lodev09)
3
+ // Copyright (c) 2024-present. All rights reserved.
4
+ //
5
+ // This source code is licensed under the MIT license found in the
6
+ // LICENSE file in the root directory of this source tree.
7
+ //
8
+
9
+ #import "TrueSheetDetentCalculator.h"
10
+
11
+ @implementation TrueSheetDetentCalculator {
12
+ __weak id<TrueSheetDetentMeasurements> _measurements;
13
+ NSMutableArray<NSNumber *> *_resolvedDetentPositions;
14
+ }
15
+
16
+ - (instancetype)initWithMeasurements:(id<TrueSheetDetentMeasurements>)measurements {
17
+ if (self = [super init]) {
18
+ _measurements = measurements;
19
+ _resolvedDetentPositions = [NSMutableArray array];
20
+ }
21
+ return self;
22
+ }
23
+
24
+ #pragma mark - Public Methods
25
+
26
+ - (CGFloat)detentValueForIndex:(NSInteger)index {
27
+ NSArray<NSNumber *> *detents = _measurements.detents;
28
+ if (index >= 0 && index < (NSInteger)detents.count) {
29
+ CGFloat value = [detents[index] doubleValue];
30
+ if (value == -1) {
31
+ CGFloat autoHeight = [_measurements.contentHeight floatValue] + [_measurements.headerHeight floatValue];
32
+ return autoHeight / _measurements.screenHeight;
33
+ }
34
+ return value;
35
+ }
36
+ return 0;
37
+ }
38
+
39
+ - (CGFloat)estimatedPositionForIndex:(NSInteger)index {
40
+ if (index < 0 || index >= (NSInteger)_resolvedDetentPositions.count) {
41
+ return 0;
42
+ }
43
+
44
+ CGFloat storedPos = [_resolvedDetentPositions[index] doubleValue];
45
+ if (storedPos > 0) {
46
+ return storedPos;
47
+ }
48
+
49
+ CGFloat screenHeight = _measurements.screenHeight;
50
+ CGFloat detentValue = [self detentValueForIndex:index];
51
+ CGFloat basePosition = screenHeight - (detentValue * screenHeight);
52
+
53
+ // Try to find offset from a known resolved position
54
+ for (NSInteger i = 0; i < (NSInteger)_resolvedDetentPositions.count; i++) {
55
+ CGFloat pos = [_resolvedDetentPositions[i] doubleValue];
56
+ if (pos > 0) {
57
+ CGFloat knownDetent = [self detentValueForIndex:i];
58
+ CGFloat expectedPos = screenHeight - (knownDetent * screenHeight);
59
+ CGFloat offset = pos - expectedPos;
60
+ return basePosition + offset;
61
+ }
62
+ }
63
+
64
+ return basePosition;
65
+ }
66
+
67
+ - (void)storeResolvedPositionForIndex:(NSInteger)index {
68
+ if (index >= 0 && index < (NSInteger)_resolvedDetentPositions.count) {
69
+ _resolvedDetentPositions[index] = @(_measurements.currentPosition);
70
+ }
71
+ }
72
+
73
+ - (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
74
+ NSInteger count = _resolvedDetentPositions.count;
75
+ if (count == 0) {
76
+ *outIndex = -1;
77
+ *outProgress = 0;
78
+ return NO;
79
+ }
80
+
81
+ CGFloat screenHeight = _measurements.screenHeight;
82
+ CGFloat firstPos = [self estimatedPositionForIndex:0];
83
+
84
+ // Above first detent - interpolating toward closed
85
+ if (position > firstPos) {
86
+ CGFloat range = screenHeight - firstPos;
87
+ *outIndex = -1;
88
+ *outProgress = range > 0 ? (position - firstPos) / range : 0;
89
+ return NO;
90
+ }
91
+
92
+ // Single detent - at or above the detent
93
+ if (count == 1) {
94
+ *outIndex = 0;
95
+ *outProgress = 0;
96
+ return NO;
97
+ }
98
+
99
+ CGFloat lastPos = [self estimatedPositionForIndex:count - 1];
100
+
101
+ // Below last detent
102
+ if (position < lastPos) {
103
+ *outIndex = count - 1;
104
+ *outProgress = 0;
105
+ return NO;
106
+ }
107
+
108
+ // Between detents
109
+ for (NSInteger i = 0; i < count - 1; i++) {
110
+ CGFloat pos = [self estimatedPositionForIndex:i];
111
+ CGFloat nextPos = [self estimatedPositionForIndex:i + 1];
112
+
113
+ if (position <= pos && position >= nextPos) {
114
+ CGFloat range = pos - nextPos;
115
+ *outIndex = i;
116
+ *outProgress = range > 0 ? (pos - position) / range : 0;
117
+ return YES;
118
+ }
119
+ }
120
+
121
+ *outIndex = count - 1;
122
+ *outProgress = 0;
123
+ return NO;
124
+ }
125
+
126
+ - (CGFloat)interpolatedIndexForPosition:(CGFloat)position {
127
+ NSInteger index;
128
+ CGFloat progress;
129
+ BOOL found = [self findSegmentForPosition:position outIndex:&index outProgress:&progress];
130
+
131
+ if (!found) {
132
+ if (index == -1) {
133
+ return -progress;
134
+ }
135
+ return index;
136
+ }
137
+
138
+ return index + fmax(0, fmin(1, progress));
139
+ }
140
+
141
+ - (CGFloat)interpolatedDetentForPosition:(CGFloat)position {
142
+ NSInteger index;
143
+ CGFloat progress;
144
+ BOOL found = [self findSegmentForPosition:position outIndex:&index outProgress:&progress];
145
+
146
+ if (!found) {
147
+ if (index == -1) {
148
+ CGFloat firstDetent = [self detentValueForIndex:0];
149
+ return fmax(0, firstDetent * (1 - progress));
150
+ }
151
+ return [self detentValueForIndex:index];
152
+ }
153
+
154
+ CGFloat detent = [self detentValueForIndex:index];
155
+ CGFloat nextDetent = [self detentValueForIndex:index + 1];
156
+ return detent + progress * (nextDetent - detent);
157
+ }
158
+
159
+ - (void)clearResolvedPositions {
160
+ [_resolvedDetentPositions removeAllObjects];
161
+ }
162
+
163
+ - (void)setDetentCount:(NSInteger)count {
164
+ [_resolvedDetentPositions removeAllObjects];
165
+ for (NSInteger i = 0; i < count; i++) {
166
+ [_resolvedDetentPositions addObject:@(0)];
167
+ }
168
+ }
169
+
170
+ @end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-true-sheet",
3
- "version": "3.4.1",
3
+ "version": "3.5.0-beta.0",
4
4
  "description": "The true native bottom sheet experience for your React Native Apps.",
5
5
  "source": "./src/index.ts",
6
6
  "main": "./lib/module/index.js",
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <alpha xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:duration="100"
4
- android:fromAlpha="1.0"
5
- android:toAlpha="0.0"
6
- android:interpolator="@android:interpolator/accelerate_cubic" />
@@ -1,13 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <set xmlns:android="http://schemas.android.com/apk/res/android">
3
- <translate
4
- android:duration="250"
5
- android:fromYDelta="20%p"
6
- android:toYDelta="0"
7
- android:interpolator="@android:interpolator/decelerate_cubic" />
8
- <alpha
9
- android:duration="150"
10
- android:fromAlpha="0.0"
11
- android:toAlpha="1.0"
12
- android:interpolator="@android:interpolator/decelerate_cubic" />
13
- </set>
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <set xmlns:android="http://schemas.android.com/apk/res/android">
3
- <translate
4
- android:duration="200"
5
- android:fromYDelta="0"
6
- android:toYDelta="20%p"
7
- android:interpolator="@android:interpolator/accelerate_cubic" />
8
- <alpha
9
- android:duration="150"
10
- android:startOffset="50"
11
- android:fromAlpha="1.0"
12
- android:toAlpha="0.0"
13
- android:interpolator="@android:interpolator/accelerate_cubic" />
14
- </set>