@lodev09/react-native-true-sheet 3.7.3 → 3.7.4-beta.1

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.
Files changed (27) hide show
  1. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +0 -4
  2. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +29 -30
  3. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensFragmentObserver.kt +65 -14
  4. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +1 -1
  5. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +4 -0
  6. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +13 -0
  7. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +11 -0
  8. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +12 -0
  9. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +10 -0
  10. package/ios/TrueSheetContainerView.mm +7 -15
  11. package/ios/TrueSheetContentView.h +2 -2
  12. package/ios/TrueSheetContentView.mm +84 -89
  13. package/ios/TrueSheetHeaderView.mm +1 -3
  14. package/ios/TrueSheetView.mm +72 -14
  15. package/ios/TrueSheetViewController.h +0 -15
  16. package/ios/TrueSheetViewController.mm +76 -146
  17. package/ios/core/RNScreensEventObserver.h +43 -0
  18. package/ios/core/RNScreensEventObserver.mm +119 -0
  19. package/ios/core/TrueSheetBlurView.mm +26 -22
  20. package/ios/utils/LayoutUtil.h +23 -0
  21. package/ios/utils/LayoutUtil.mm +28 -3
  22. package/lib/module/TrueSheet.js +16 -5
  23. package/lib/module/TrueSheet.js.map +1 -1
  24. package/lib/typescript/src/TrueSheet.d.ts +4 -0
  25. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/TrueSheet.tsx +16 -3
@@ -34,6 +34,7 @@
34
34
  BOOL _isDragging;
35
35
  BOOL _isTransitioning;
36
36
  BOOL _isTrackingPositionFromLayout;
37
+ BOOL _isWillDismissEmitted;
37
38
 
38
39
  __weak TrueSheetViewController *_parentSheetController;
39
40
 
@@ -57,11 +58,12 @@
57
58
  _lastPosition = 0;
58
59
  _isDragging = NO;
59
60
  _isPresented = NO;
61
+ _isTransitioning = NO;
62
+ _isWillDismissEmitted = NO;
60
63
  _pendingContentSizeChange = NO;
61
64
  _activeDetentIndex = -1;
62
65
  _pendingDetentIndex = -1;
63
66
 
64
- _isTransitioning = NO;
65
67
  _transitionFakeView = [UIView new];
66
68
  _isTrackingPositionFromLayout = NO;
67
69
 
@@ -165,6 +167,9 @@
165
167
  [super viewDidLoad];
166
168
  self.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
167
169
 
170
+ _blurView = [[TrueSheetBlurView alloc] init];
171
+ [_blurView addToView:self.view];
172
+
168
173
  _grabberView = [[TrueSheetGrabberView alloc] init];
169
174
  _grabberView.hidden = YES;
170
175
  [_grabberView addToView:self.view];
@@ -181,23 +186,16 @@
181
186
  UIViewController *presenter = self.presentingViewController;
182
187
  if ([presenter isKindOfClass:[TrueSheetViewController class]]) {
183
188
  _parentSheetController = (TrueSheetViewController *)presenter;
184
- if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
185
- [_parentSheetController.delegate viewControllerWillBlur];
186
- }
189
+ [_parentSheetController.delegate viewControllerWillBlur];
187
190
  }
188
191
 
189
192
  dispatch_async(dispatch_get_main_queue(), ^{
190
- if ([self.delegate respondsToSelector:@selector(viewControllerWillPresentAtIndex:position:detent:)]) {
191
- NSInteger index = self.currentDetentIndex;
192
- CGFloat position = self.currentPosition;
193
- CGFloat detent = [self detentValueForIndex:index];
194
-
195
- [self.delegate viewControllerWillPresentAtIndex:index position:position detent:detent];
196
- }
193
+ NSInteger index = self.currentDetentIndex;
194
+ CGFloat position = self.currentPosition;
195
+ CGFloat detent = [self detentValueForIndex:index];
197
196
 
198
- if ([self.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
199
- [self.delegate viewControllerWillFocus];
200
- }
197
+ [self.delegate viewControllerWillPresentAtIndex:index position:position detent:detent];
198
+ [self.delegate viewControllerWillFocus];
201
199
  });
202
200
  }
203
201
 
@@ -208,22 +206,13 @@
208
206
  [super viewDidAppear:animated];
209
207
 
210
208
  if (!_isPresented) {
211
- if (_parentSheetController) {
212
- if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
213
- [_parentSheetController.delegate viewControllerDidBlur];
214
- }
215
- }
209
+ [_parentSheetController.delegate viewControllerDidBlur];
216
210
 
217
211
  dispatch_async(dispatch_get_main_queue(), ^{
218
- if ([self.delegate respondsToSelector:@selector(viewControllerDidPresentAtIndex:position:detent:)]) {
219
- NSInteger index = [self currentDetentIndex];
220
- CGFloat detent = [self detentValueForIndex:index];
221
- [self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
222
- }
223
-
224
- if ([self.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
225
- [self.delegate viewControllerDidFocus];
226
- }
212
+ NSInteger index = [self currentDetentIndex];
213
+ CGFloat detent = [self detentValueForIndex:index];
214
+ [self.delegate viewControllerDidPresentAtIndex:index position:self.currentPosition detent:detent];
215
+ [self.delegate viewControllerDidFocus];
227
216
 
228
217
  [self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"did present"];
229
218
  });
@@ -237,52 +226,46 @@
237
226
  return self.presentingViewController == nil || self.isBeingDismissed;
238
227
  }
239
228
 
240
- - (void)viewWillDisappear:(BOOL)animated {
241
- [super viewWillDisappear:animated];
229
+ - (void)emitWillDismissEvents {
230
+ if (self.isDismissing && !_isWillDismissEmitted) {
231
+ _isWillDismissEmitted = YES;
242
232
 
233
+ [self.delegate viewControllerWillBlur];
234
+ [self.delegate viewControllerWillDismiss];
235
+ [_parentSheetController.delegate viewControllerWillFocus];
236
+ }
237
+ }
238
+
239
+ - (void)emitDidDismissEvents {
243
240
  if (self.isDismissing) {
244
- dispatch_async(dispatch_get_main_queue(), ^{
245
- if ([self.delegate respondsToSelector:@selector(viewControllerWillBlur)]) {
246
- [self.delegate viewControllerWillBlur];
247
- }
241
+ _isPresented = NO;
242
+ _isWillDismissEmitted = NO;
248
243
 
249
- if ([self.delegate respondsToSelector:@selector(viewControllerWillDismiss)]) {
250
- [self.delegate viewControllerWillDismiss];
251
- }
252
- });
244
+ [_parentSheetController.delegate viewControllerDidFocus];
245
+ _parentSheetController = nil;
253
246
 
254
- if (_parentSheetController) {
255
- if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerWillFocus)]) {
256
- [_parentSheetController.delegate viewControllerWillFocus];
257
- }
258
- }
247
+ [self.delegate viewControllerDidBlur];
248
+ [self.delegate viewControllerDidDismiss];
259
249
  }
260
-
261
- [self setupTransitionTracker];
262
250
  }
263
251
 
264
- - (void)viewDidDisappear:(BOOL)animated {
265
- [super viewDidDisappear:animated];
266
-
267
- if (self.isDismissing) {
268
- _isPresented = NO;
269
- _activeDetentIndex = -1;
252
+ - (void)viewWillDisappear:(BOOL)animated {
253
+ [super viewWillDisappear:animated];
270
254
 
271
- if (_parentSheetController) {
272
- if ([_parentSheetController.delegate respondsToSelector:@selector(viewControllerDidFocus)]) {
273
- [_parentSheetController.delegate viewControllerDidFocus];
274
- }
275
- _parentSheetController = nil;
255
+ // Dispatch to allow pan gesture to set _isDragging before checking
256
+ // handleTransitionTracker will emit when sheet is transitioning to dismiss
257
+ dispatch_async(dispatch_get_main_queue(), ^{
258
+ if (!self->_isDragging) {
259
+ [self emitWillDismissEvents];
276
260
  }
261
+ });
277
262
 
278
- if ([self.delegate respondsToSelector:@selector(viewControllerDidBlur)]) {
279
- [self.delegate viewControllerDidBlur];
280
- }
263
+ [self setupTransitionTracker];
264
+ }
281
265
 
282
- if ([self.delegate respondsToSelector:@selector(viewControllerDidDismiss)]) {
283
- [self.delegate viewControllerDidDismiss];
284
- }
285
- }
266
+ - (void)viewDidDisappear:(BOOL)animated {
267
+ [super viewDidDisappear:animated];
268
+ [self emitDidDismissEvents];
286
269
  }
287
270
 
288
271
  - (void)viewWillLayoutSubviews {
@@ -309,21 +292,17 @@
309
292
  - (void)viewDidLayoutSubviews {
310
293
  [super viewDidLayoutSubviews];
311
294
 
312
- if ([self.delegate respondsToSelector:@selector(viewControllerDidChangeSize:)]) {
313
- [self.delegate viewControllerDidChangeSize:self.view.frame.size];
314
- }
295
+ [self.delegate viewControllerDidChangeSize:self.view.frame.size];
315
296
 
316
297
  if (_pendingDetentIndex >= 0) {
317
298
  NSInteger pendingIndex = _pendingDetentIndex;
318
299
  _pendingDetentIndex = -1;
319
300
 
320
301
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
321
- if ([self.delegate respondsToSelector:@selector(viewControllerDidChangeDetent:position:detent:)]) {
322
- [self storeResolvedPositionForIndex:pendingIndex];
323
- CGFloat detent = [self detentValueForIndex:pendingIndex];
324
- [self.delegate viewControllerDidChangeDetent:pendingIndex position:self.currentPosition detent:detent];
325
- [self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"pending detent change"];
326
- }
302
+ [self storeResolvedPositionForIndex:pendingIndex];
303
+ CGFloat detent = [self detentValueForIndex:pendingIndex];
304
+ [self.delegate viewControllerDidChangeDetent:pendingIndex position:self.currentPosition detent:detent];
305
+ [self emitChangePositionDelegateWithPosition:self.currentPosition realtime:NO debug:@"pending detent change"];
327
306
  });
328
307
  }
329
308
 
@@ -382,9 +361,7 @@
382
361
  NSInteger index = self.currentDetentIndex;
383
362
  CGFloat detent = [self detentValueForIndex:index];
384
363
 
385
- if ([self.delegate respondsToSelector:@selector(viewControllerDidDrag:index:position:detent:)]) {
386
- [self.delegate viewControllerDidDrag:gesture.state index:index position:self.currentPosition detent:detent];
387
- }
364
+ [self.delegate viewControllerDidDrag:gesture.state index:index position:self.currentPosition detent:detent];
388
365
 
389
366
  switch (gesture.state) {
390
367
  case UIGestureRecognizerStateBegan:
@@ -449,7 +426,10 @@
449
426
 
450
427
  if (self.currentPosition >= self.screenHeight) {
451
428
  CGFloat position = fmax(_lastPosition, layerPosition);
429
+
430
+ [self emitWillDismissEvents];
452
431
  [self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition out"];
432
+
453
433
  } else {
454
434
  CGFloat position = fmax(self.currentPosition, layerPosition);
455
435
  [self emitChangePositionDelegateWithPosition:position realtime:YES debug:@"transition in"];
@@ -472,9 +452,7 @@
472
452
 
473
453
  CGFloat index = [self interpolatedIndexForPosition:position];
474
454
  CGFloat detent = [self interpolatedDetentForPosition:position];
475
- if ([self.delegate respondsToSelector:@selector(viewControllerDidChangePosition:position:detent:realtime:)]) {
476
- [self.delegate viewControllerDidChangePosition:index position:position detent:detent realtime:realtime];
477
- }
455
+ [self.delegate viewControllerDidChangePosition:index position:position detent:detent realtime:realtime];
478
456
  }
479
457
  }
480
458
 
@@ -671,32 +649,6 @@
671
649
  }
672
650
 
673
651
  - (void)setupBackground {
674
- #if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1)
675
- BOOL useBackgroundEffect = NO;
676
- if (@available(iOS 26.1, *)) {
677
- useBackgroundEffect = !self.isDesignCompatibilityMode;
678
- }
679
-
680
- // iOS 26.1+: use native backgroundEffect when only backgroundBlur is set (no backgroundColor)
681
- // Fall back to TrueSheetBlurView when blur intensity is set (not 100%) since
682
- // sheet.backgroundEffect doesn't support intensity control
683
- if (@available(iOS 26.1, *)) {
684
- if (useBackgroundEffect) {
685
- BOOL hasCustomIntensity = self.blurIntensity && [self.blurIntensity floatValue] < 100;
686
- if (!self.backgroundColor && self.backgroundBlur && self.backgroundBlur.length > 0) {
687
- if (hasCustomIntensity) {
688
- // Clear native effect to allow custom blur view with intensity
689
- self.sheet.backgroundEffect = [UIColorEffect effectWithColor:[UIColor clearColor]];
690
- } else {
691
- UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromString:self.backgroundBlur];
692
- self.sheet.backgroundEffect = [UIBlurEffect effectWithStyle:style];
693
- return;
694
- }
695
- }
696
- }
697
- }
698
- #endif
699
-
700
652
  NSString *effectiveBackgroundBlur = self.backgroundBlur;
701
653
  if (@available(iOS 26.0, *)) {
702
654
  // iOS 26+ has default liquid glass effect
@@ -704,28 +656,23 @@
704
656
  effectiveBackgroundBlur = @"system-material";
705
657
  }
706
658
 
707
- BOOL blurChanged = ![_blurView.backgroundBlur isEqualToString:effectiveBackgroundBlur];
659
+ BOOL hasBlur = effectiveBackgroundBlur && effectiveBackgroundBlur.length > 0;
708
660
 
709
- if (_blurView && blurChanged) {
710
- [_blurView removeFromSuperview];
711
- _blurView = nil;
712
- }
713
-
714
- if (effectiveBackgroundBlur && effectiveBackgroundBlur.length > 0) {
715
- if (!_blurView) {
716
- _blurView = [[TrueSheetBlurView alloc] init];
717
- [_blurView addToView:self.view];
718
- }
719
- _blurView.backgroundBlur = effectiveBackgroundBlur;
720
- _blurView.blurIntensity = self.blurIntensity;
721
- _blurView.blurInteraction = self.blurInteraction;
722
- [_blurView applyBlurEffect];
723
- }
661
+ _blurView.backgroundBlur = hasBlur ? effectiveBackgroundBlur : nil;
662
+ _blurView.blurIntensity = self.blurIntensity;
663
+ _blurView.blurInteraction = self.blurInteraction;
664
+ [_blurView applyBlurEffect];
724
665
 
725
666
  #if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_1)
726
667
  if (@available(iOS 26.1, *)) {
727
- if (useBackgroundEffect && self.backgroundColor) {
728
- self.sheet.backgroundEffect = [UIColorEffect effectWithColor:self.backgroundColor];
668
+ if (!self.isDesignCompatibilityMode) {
669
+ if (self.backgroundColor) {
670
+ self.sheet.backgroundEffect = [UIColorEffect effectWithColor:self.backgroundColor];
671
+ } else if (hasBlur) {
672
+ self.sheet.backgroundEffect = [UIColorEffect effectWithColor:[UIColor clearColor]];
673
+ } else {
674
+ self.sheet.backgroundEffect = nil;
675
+ }
729
676
  return;
730
677
  }
731
678
  }
@@ -788,31 +735,14 @@
788
735
 
789
736
  - (void)sheetPresentationControllerDidChangeSelectedDetentIdentifier:
790
737
  (UISheetPresentationController *)sheetPresentationController {
791
- if ([self.delegate respondsToSelector:@selector(viewControllerDidChangeDetent:position:detent:)]) {
792
- dispatch_async(dispatch_get_main_queue(), ^{
793
- NSInteger index = self.currentDetentIndex;
794
- if (index >= 0) {
795
- CGFloat detent = [self detentValueForIndex:index];
796
- [self.delegate viewControllerDidChangeDetent:index position:self.currentPosition detent:detent];
797
- }
798
- });
799
- }
800
- }
801
-
802
- #pragma mark - RNSLifecycleListenerProtocol
803
-
804
- #if RNS_LIFECYCLE_LISTENER_PROTOCOL_AVAILABLE
805
-
806
- - (void)screenWillDisappear:(UIViewController *)screen isPresenterUnmounting:(BOOL)isPresenterUnmounting {
807
- // Skip if not presented, being dismissed, or if the presenter (modal) itself is being unmounted
808
- if (!_isPresented || self.isBeingDismissed || isPresenterUnmounting) {
809
- return;
810
- }
811
- if ([self.delegate respondsToSelector:@selector(viewControllerDidDetectScreenDisappear)]) {
812
- [self.delegate viewControllerDidDetectScreenDisappear];
813
- }
738
+ dispatch_async(dispatch_get_main_queue(), ^{
739
+ NSInteger index = self.currentDetentIndex;
740
+ if (index >= 0) {
741
+ CGFloat detent = [self detentValueForIndex:index];
742
+ [self.delegate viewControllerDidChangeDetent:index position:self.currentPosition detent:detent];
743
+ }
744
+ });
814
745
  }
815
- #endif
816
746
 
817
747
  #pragma mark - RNSDismissibleModalProtocol
818
748
 
@@ -0,0 +1,43 @@
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
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+
11
+ #import <UIKit/UIKit.h>
12
+ #import <react/renderer/components/TrueSheetSpec/TrueSheetViewState.h>
13
+
14
+ NS_ASSUME_NONNULL_BEGIN
15
+
16
+ @class TrueSheetView;
17
+
18
+ @protocol RNScreensEventObserverDelegate <NSObject>
19
+
20
+ - (void)presenterScreenWillDisappear;
21
+ - (void)presenterScreenWillAppear;
22
+
23
+ @end
24
+
25
+ /**
26
+ * Observes react-native-screens lifecycle events via C++ EventDispatcher.
27
+ * Detects when the presenting screen unmounts while sheet is presented.
28
+ */
29
+ @interface RNScreensEventObserver : NSObject
30
+
31
+ @property (nonatomic, weak) TrueSheetView<RNScreensEventObserverDelegate> *delegate;
32
+
33
+ - (void)startObservingWithState:(const facebook::react::TrueSheetViewState &)state;
34
+ - (void)stopObserving;
35
+
36
+ - (void)capturePresenterScreenFromView:(UIView *)view;
37
+ - (BOOL)shouldDismissForScreenTag:(NSInteger)screenTag;
38
+
39
+ @end
40
+
41
+ NS_ASSUME_NONNULL_END
42
+
43
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -0,0 +1,119 @@
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
+ #ifdef RCT_NEW_ARCH_ENABLED
10
+
11
+ #import "RNScreensEventObserver.h"
12
+ #import "TrueSheetView.h"
13
+
14
+ #import <react/renderer/core/EventDispatcher.h>
15
+ #import <react/renderer/core/ShadowNodeFamily.h>
16
+
17
+ using namespace facebook::react;
18
+
19
+ @implementation RNScreensEventObserver {
20
+ std::weak_ptr<const EventDispatcher> _eventDispatcher;
21
+ std::shared_ptr<const EventListener> _eventListener;
22
+ NSInteger _presenterScreenTag;
23
+ __weak UIViewController *_presenterScreenController;
24
+ BOOL _dismissedByNavigation;
25
+ }
26
+
27
+ - (instancetype)init {
28
+ if (self = [super init]) {
29
+ _presenterScreenTag = 0;
30
+ _presenterScreenController = nil;
31
+ }
32
+ return self;
33
+ }
34
+
35
+ - (void)dealloc {
36
+ [self stopObserving];
37
+ }
38
+
39
+ - (void)startObservingWithState:(const TrueSheetViewState &)state {
40
+ if (!_eventDispatcher.expired()) {
41
+ return;
42
+ }
43
+
44
+ if (auto dispatcherPtr = state.getEventDispatcher().lock()) {
45
+ _eventDispatcher = state.getEventDispatcher();
46
+
47
+ __weak RNScreensEventObserver *weakSelf = self;
48
+
49
+ _eventListener = std::make_shared<const EventListener>([weakSelf](const RawEvent &event) {
50
+ RNScreensEventObserver *strongSelf = weakSelf;
51
+ if (!strongSelf) {
52
+ return false;
53
+ }
54
+
55
+ if (auto family = event.shadowNodeFamily.lock()) {
56
+ Tag screenTag = family->getTag();
57
+
58
+ if (event.type == "topWillDisappear") {
59
+ if ([strongSelf shouldDismissForScreenTag:screenTag]) {
60
+ strongSelf->_dismissedByNavigation = YES;
61
+ [strongSelf.delegate presenterScreenWillDisappear];
62
+ }
63
+ } else if (event.type == "topWillAppear") {
64
+ if (screenTag == strongSelf->_presenterScreenTag && strongSelf->_dismissedByNavigation) {
65
+ strongSelf->_dismissedByNavigation = NO;
66
+ [strongSelf.delegate presenterScreenWillAppear];
67
+ }
68
+ }
69
+ }
70
+ return false;
71
+ });
72
+
73
+ dispatcherPtr->addListener(_eventListener);
74
+ }
75
+ }
76
+
77
+ - (void)stopObserving {
78
+ if (_eventListener) {
79
+ if (auto dispatcher = _eventDispatcher.lock()) {
80
+ dispatcher->removeListener(_eventListener);
81
+ }
82
+ _eventListener = nullptr;
83
+ }
84
+ _eventDispatcher.reset();
85
+ }
86
+
87
+ - (void)capturePresenterScreenFromView:(UIView *)view {
88
+ _presenterScreenTag = 0;
89
+ _presenterScreenController = nil;
90
+
91
+ for (UIView *current = view.superview; current; current = current.superview) {
92
+ NSString *className = NSStringFromClass([current class]);
93
+
94
+ if ([className isEqualToString:@"RNSScreenView"]) {
95
+ _presenterScreenTag = current.tag;
96
+ for (UIResponder *r = current.nextResponder; r; r = r.nextResponder) {
97
+ if ([r isKindOfClass:[UIViewController class]]) {
98
+ _presenterScreenController = (UIViewController *)r;
99
+ break;
100
+ }
101
+ }
102
+ break;
103
+ }
104
+ }
105
+ }
106
+
107
+ - (BOOL)shouldDismissForScreenTag:(NSInteger)screenTag {
108
+ if (_presenterScreenTag != screenTag) {
109
+ return NO;
110
+ }
111
+
112
+ // Skip if screen is still top of nav stack (e.g. modal dismiss - sheet dismisses naturally with modal)
113
+ // Dismiss if a new screen was pushed or popped
114
+ return _presenterScreenController.navigationController.topViewController != _presenterScreenController;
115
+ }
116
+
117
+ @end
118
+
119
+ #endif // RCT_NEW_ARCH_ENABLED
@@ -34,41 +34,45 @@
34
34
  [parentView insertSubview:self atIndex:0];
35
35
  }
36
36
 
37
+ - (void)clearAnimator {
38
+ if (_blurAnimator) {
39
+ [_blurAnimator stopAnimation:YES];
40
+ _blurAnimator = nil;
41
+ }
42
+ }
43
+
37
44
  - (void)applyBlurEffect {
38
45
  self.userInteractionEnabled = self.blurInteraction;
39
46
 
40
- // Create animator only once
41
- if (!_blurAnimator) {
42
- UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromString:self.backgroundBlur];
43
- UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:style];
47
+ if (!self.backgroundBlur || self.backgroundBlur.length == 0) {
48
+ [self clearAnimator];
49
+ self.effect = nil;
50
+ return;
51
+ }
52
+
53
+ UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromString:self.backgroundBlur];
54
+ UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:style];
55
+
56
+ CGFloat intensity =
57
+ (self.blurIntensity && [self.blurIntensity floatValue] >= 0) ? [self.blurIntensity floatValue] / 100.0 : 1.0;
44
58
 
59
+ if (intensity >= 1.0) {
60
+ [self clearAnimator];
61
+ self.effect = blurEffect;
62
+ return;
63
+ }
64
+
65
+ if (!_blurAnimator) {
45
66
  __weak __typeof(self) weakSelf = self;
46
67
  _blurAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:1.0
47
68
  curve:UIViewAnimationCurveLinear
48
69
  animations:^{
49
70
  weakSelf.effect = blurEffect;
50
71
  }];
51
- _blurAnimator.pausesOnCompletion = YES;
52
72
  }
53
73
 
54
- // Update intensity
55
- CGFloat intensity =
56
- (self.blurIntensity && [self.blurIntensity floatValue] >= 0) ? [self.blurIntensity floatValue] / 100.0 : 1.0;
74
+ _blurAnimator.pausesOnCompletion = YES;
57
75
  _blurAnimator.fractionComplete = intensity;
58
76
  }
59
77
 
60
- - (void)willMoveToSuperview:(UIView *)newSuperview {
61
- [super willMoveToSuperview:newSuperview];
62
-
63
- // Clean up when removed from superview
64
- if (!newSuperview) {
65
- if (_blurAnimator) {
66
- [_blurAnimator stopAnimation:YES];
67
- _blurAnimator = nil;
68
- }
69
-
70
- self.effect = nil;
71
- }
72
- }
73
-
74
78
  @end
@@ -40,6 +40,29 @@ NS_ASSUME_NONNULL_BEGIN
40
40
  */
41
41
  + (void)pinView:(UIView *)view toParentView:(UIView *)parentView withTopView:(UIView *)topView edges:(UIRectEdge)edges;
42
42
 
43
+ /**
44
+ * Pins a view to its parent view with insets
45
+ * @param view The view to pin
46
+ * @param parentView The parent view to pin to
47
+ * @param edges The edges to pin (UIRectEdge flags)
48
+ * @param insets The insets to apply to each edge
49
+ */
50
+ + (void)pinView:(UIView *)view toParentView:(UIView *)parentView edges:(UIRectEdge)edges insets:(UIEdgeInsets)insets;
51
+
52
+ /**
53
+ * Pins a view to its parent view with its top edge anchored below a top sibling view, with insets
54
+ * @param view The view to pin
55
+ * @param parentView The parent view to pin to
56
+ * @param topView The view to position below (top sibling)
57
+ * @param edges The edges to pin to parent (excluding top, which is pinned to topView)
58
+ * @param insets The insets to apply to each edge
59
+ */
60
+ + (void)pinView:(UIView *)view
61
+ toParentView:(UIView *)parentView
62
+ withTopView:(UIView *)topView
63
+ edges:(UIRectEdge)edges
64
+ insets:(UIEdgeInsets)insets;
65
+
43
66
  /**
44
67
  * Unpins a view by removing its constraints and re-enabling autoresizing mask translation
45
68
  * @param view The view to unpin
@@ -38,7 +38,32 @@
38
38
  }
39
39
  }
40
40
 
41
+ + (void)pinView:(UIView *)view toParentView:(UIView *)parentView edges:(UIRectEdge)edges insets:(UIEdgeInsets)insets {
42
+ view.translatesAutoresizingMaskIntoConstraints = NO;
43
+
44
+ if (edges & UIRectEdgeTop) {
45
+ [view.topAnchor constraintEqualToAnchor:parentView.topAnchor constant:insets.top].active = YES;
46
+ }
47
+ if (edges & UIRectEdgeBottom) {
48
+ [view.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor constant:-insets.bottom].active = YES;
49
+ }
50
+ if (edges & UIRectEdgeLeft) {
51
+ [view.leadingAnchor constraintEqualToAnchor:parentView.leadingAnchor constant:insets.left].active = YES;
52
+ }
53
+ if (edges & UIRectEdgeRight) {
54
+ [view.trailingAnchor constraintEqualToAnchor:parentView.trailingAnchor constant:-insets.right].active = YES;
55
+ }
56
+ }
57
+
41
58
  + (void)pinView:(UIView *)view toParentView:(UIView *)parentView withTopView:(UIView *)topView edges:(UIRectEdge)edges {
59
+ [self pinView:view toParentView:parentView withTopView:topView edges:edges insets:UIEdgeInsetsZero];
60
+ }
61
+
62
+ + (void)pinView:(UIView *)view
63
+ toParentView:(UIView *)parentView
64
+ withTopView:(UIView *)topView
65
+ edges:(UIRectEdge)edges
66
+ insets:(UIEdgeInsets)insets {
42
67
  view.translatesAutoresizingMaskIntoConstraints = NO;
43
68
 
44
69
  // Pin top edge to bottom of top sibling
@@ -46,13 +71,13 @@
46
71
 
47
72
  // Pin other edges to parent based on edges parameter
48
73
  if (edges & UIRectEdgeBottom) {
49
- [view.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor].active = YES;
74
+ [view.bottomAnchor constraintEqualToAnchor:parentView.bottomAnchor constant:-insets.bottom].active = YES;
50
75
  }
51
76
  if (edges & UIRectEdgeLeft) {
52
- [view.leadingAnchor constraintEqualToAnchor:parentView.leadingAnchor].active = YES;
77
+ [view.leadingAnchor constraintEqualToAnchor:parentView.leadingAnchor constant:insets.left].active = YES;
53
78
  }
54
79
  if (edges & UIRectEdgeRight) {
55
- [view.trailingAnchor constraintEqualToAnchor:parentView.trailingAnchor].active = YES;
80
+ [view.trailingAnchor constraintEqualToAnchor:parentView.trailingAnchor constant:-insets.right].active = YES;
56
81
  }
57
82
  }
58
83