@lodev09/react-native-true-sheet 3.8.0-beta.2 → 3.8.0-beta.3
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.
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +22 -2
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +29 -31
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +10 -9
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +6 -12
- package/ios/TrueSheetContentView.mm +6 -0
- package/ios/TrueSheetModule.mm +33 -9
- package/ios/TrueSheetView.h +3 -1
- package/ios/TrueSheetView.mm +41 -33
- package/ios/TrueSheetViewController.h +1 -0
- package/ios/TrueSheetViewController.mm +4 -8
- package/ios/core/RNScreensEventObserver.mm +23 -15
- package/lib/module/TrueSheet.js +40 -0
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +26 -40
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetProvider.js +1 -0
- package/lib/module/TrueSheetProvider.js.map +1 -1
- package/lib/module/TrueSheetProvider.web.js +7 -32
- package/lib/module/TrueSheetProvider.web.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +3 -0
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/reanimated.js +2 -0
- package/lib/module/mocks/reanimated.js.map +1 -1
- package/lib/module/navigation/TrueSheetRouter.js +42 -8
- package/lib/module/navigation/TrueSheetRouter.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +8 -17
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/reanimated/ReanimatedTrueSheet.web.js +2 -2
- package/lib/module/reanimated/ReanimatedTrueSheet.web.js.map +1 -1
- package/lib/module/specs/NativeTrueSheetModule.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +29 -2
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +0 -48
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts +4 -2
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.d.ts +3 -2
- package/lib/typescript/src/TrueSheetProvider.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts +6 -14
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +6 -3
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/reanimated.d.ts +4 -2
- package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts +4 -3
- package/lib/typescript/src/reanimated/ReanimatedTrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts +9 -1
- package/lib/typescript/src/specs/NativeTrueSheetModule.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/TrueSheet.tsx +39 -5
- package/src/TrueSheet.types.ts +0 -50
- package/src/TrueSheet.web.tsx +32 -50
- package/src/TrueSheetProvider.tsx +7 -2
- package/src/TrueSheetProvider.web.tsx +19 -38
- package/src/index.ts +1 -1
- package/src/mocks/index.ts +7 -6
- package/src/mocks/reanimated.ts +4 -5
- package/src/navigation/TrueSheetRouter.ts +51 -16
- package/src/navigation/screen/useSheetScreenState.ts +5 -11
- package/src/reanimated/ReanimatedTrueSheet.web.tsx +28 -30
- package/src/specs/NativeTrueSheetModule.ts +10 -1
|
@@ -54,7 +54,7 @@ class TrueSheetModule(reactContext: ReactApplicationContext) :
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
* Dismiss a sheet
|
|
57
|
+
* Dismiss a sheet and all sheets presented on top of it
|
|
58
58
|
*
|
|
59
59
|
* @param viewTag Native view tag of the sheet component
|
|
60
60
|
* @param promise Promise that resolves when sheet is fully dismissed
|
|
@@ -73,6 +73,26 @@ class TrueSheetModule(reactContext: ReactApplicationContext) :
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Dismiss only the sheets presented on top of this sheet, keeping this sheet presented
|
|
78
|
+
*
|
|
79
|
+
* @param viewTag Native view tag of the sheet component
|
|
80
|
+
* @param promise Promise that resolves when all child sheets are fully dismissed
|
|
81
|
+
* @throws VIEW_NOT_FOUND if the view with the given tag is not found
|
|
82
|
+
* @throws INVALID_VIEW_TYPE if the view is not a TrueSheetView
|
|
83
|
+
* @throws OPERATION_FAILED if the operation fails for any other reason
|
|
84
|
+
*/
|
|
85
|
+
@ReactMethod
|
|
86
|
+
fun dismissStackByRef(viewTag: Double, animated: Boolean, promise: Promise) {
|
|
87
|
+
val tag = viewTag.toInt()
|
|
88
|
+
|
|
89
|
+
withTrueSheetView(tag, promise) { view ->
|
|
90
|
+
view.dismissStack(animated) {
|
|
91
|
+
promise.resolve(null)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
76
96
|
/**
|
|
77
97
|
* Resize a sheet to a different index by reference
|
|
78
98
|
*
|
|
@@ -111,7 +131,7 @@ class TrueSheetModule(reactContext: ReactApplicationContext) :
|
|
|
111
131
|
return@post
|
|
112
132
|
}
|
|
113
133
|
|
|
114
|
-
rootSheet.
|
|
134
|
+
rootSheet.dismiss(animated) {
|
|
115
135
|
promise.resolve(null)
|
|
116
136
|
}
|
|
117
137
|
} catch (e: Exception) {
|
|
@@ -135,11 +135,13 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
135
135
|
val child = getChildAt(index)
|
|
136
136
|
if (child is TrueSheetContainerView) {
|
|
137
137
|
child.delegate = null
|
|
138
|
-
viewController.createSheetSnapshot()
|
|
139
138
|
|
|
140
|
-
//
|
|
141
|
-
if (viewController.
|
|
142
|
-
|
|
139
|
+
// Skip if already dismissing - snapshot preserves visuals during dismiss
|
|
140
|
+
if (!viewController.isBeingDismissed) {
|
|
141
|
+
viewController.createSheetSnapshot()
|
|
142
|
+
if (viewController.isPresented) {
|
|
143
|
+
dismiss(true) {}
|
|
144
|
+
}
|
|
143
145
|
}
|
|
144
146
|
}
|
|
145
147
|
viewController.removeView(child)
|
|
@@ -171,13 +173,10 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
171
173
|
cleanupScreenEventObserver()
|
|
172
174
|
didInitiallyPresent = false
|
|
173
175
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
viewController.dismiss()
|
|
179
|
-
} else {
|
|
180
|
-
viewController.delegate = null
|
|
176
|
+
viewController.dismissPromise = { viewController.delegate = null }
|
|
177
|
+
|
|
178
|
+
if (viewController.isPresented && !viewController.isBeingDismissed) {
|
|
179
|
+
viewController.dismiss(animated = false)
|
|
181
180
|
}
|
|
182
181
|
}
|
|
183
182
|
|
|
@@ -344,7 +343,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
344
343
|
viewController.coordinatorLayout?.let { rootContainerView?.addView(it) }
|
|
345
344
|
|
|
346
345
|
// Register with observer to track sheet stack hierarchy
|
|
347
|
-
viewController.parentSheetView = TrueSheetStackManager.
|
|
346
|
+
viewController.parentSheetView = TrueSheetStackManager.registerSheet(this)
|
|
348
347
|
}
|
|
349
348
|
viewController.presentPromise = promiseCallback
|
|
350
349
|
viewController.present(detentIndex, animated)
|
|
@@ -352,30 +351,26 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
352
351
|
|
|
353
352
|
@UiThread
|
|
354
353
|
fun dismiss(animated: Boolean = true, promiseCallback: () -> Unit) {
|
|
355
|
-
//
|
|
356
|
-
|
|
357
|
-
// See: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss
|
|
358
|
-
val sheetsAbove = TrueSheetStackManager.getSheetsAbove(this)
|
|
359
|
-
if (sheetsAbove.isNotEmpty()) {
|
|
360
|
-
for (sheet in sheetsAbove) {
|
|
361
|
-
sheet.viewController.dismiss(animated)
|
|
362
|
-
}
|
|
363
|
-
promiseCallback()
|
|
364
|
-
return
|
|
365
|
-
}
|
|
354
|
+
// Dismiss all sheets above first
|
|
355
|
+
dismissStack(animated) {}
|
|
366
356
|
|
|
357
|
+
// Then dismiss itself
|
|
367
358
|
viewController.dismissPromise = promiseCallback
|
|
368
359
|
viewController.dismiss(animated)
|
|
369
360
|
}
|
|
370
361
|
|
|
371
362
|
@UiThread
|
|
372
|
-
fun
|
|
373
|
-
|
|
374
|
-
|
|
363
|
+
fun dismissStack(animated: Boolean = true, promiseCallback: () -> Unit) {
|
|
364
|
+
val sheetsAbove = TrueSheetStackManager.getSheetsAbove(this)
|
|
365
|
+
if (sheetsAbove.isNotEmpty()) {
|
|
366
|
+
// Create snapshot only for topmost sheet (first in reversed list)
|
|
367
|
+
sheetsAbove.firstOrNull()?.viewController?.createSheetSnapshot()
|
|
375
368
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
369
|
+
for (sheet in sheetsAbove) {
|
|
370
|
+
sheet.viewController.dismiss(animated)
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
promiseCallback()
|
|
379
374
|
}
|
|
380
375
|
|
|
381
376
|
@UiThread
|
|
@@ -397,7 +392,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
397
392
|
if (viewController.containerView == null) return@post
|
|
398
393
|
|
|
399
394
|
viewController.setupSheetDetentsForSizeChange()
|
|
400
|
-
TrueSheetStackManager.
|
|
395
|
+
TrueSheetStackManager.updateParentTranslation(this)
|
|
401
396
|
}
|
|
402
397
|
}
|
|
403
398
|
|
|
@@ -451,6 +446,9 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
451
446
|
// ==================== TrueSheetViewControllerDelegate ====================
|
|
452
447
|
|
|
453
448
|
override fun viewControllerWillPresent(index: Int, position: Float, detent: Float) {
|
|
449
|
+
// Update parent sheet translation now that content is measured
|
|
450
|
+
TrueSheetStackManager.updateParentTranslation(this)
|
|
451
|
+
|
|
454
452
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
455
453
|
eventDispatcher?.dispatchEvent(WillPresentEvent(surfaceId, id, index, position, detent))
|
|
456
454
|
}
|
|
@@ -477,7 +475,7 @@ class TrueSheetView(private val reactContext: ThemedReactContext) :
|
|
|
477
475
|
val surfaceId = UIManagerHelper.getSurfaceId(this)
|
|
478
476
|
eventDispatcher?.dispatchEvent(DidDismissEvent(surfaceId, id))
|
|
479
477
|
|
|
480
|
-
TrueSheetStackManager.
|
|
478
|
+
TrueSheetStackManager.unregisterSheet(this, hadParent)
|
|
481
479
|
}
|
|
482
480
|
|
|
483
481
|
override fun viewControllerDidChangeDetent(index: Int, position: Float, detent: Float) {
|
|
@@ -131,7 +131,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
131
131
|
private set
|
|
132
132
|
|
|
133
133
|
private var interactionState: InteractionState = InteractionState.Idle
|
|
134
|
-
|
|
134
|
+
internal var isBeingDismissed = false
|
|
135
|
+
private set
|
|
135
136
|
var wasHiddenByScreen = false
|
|
136
137
|
private var shouldAnimatePresent = false
|
|
137
138
|
private var isPresentAnimating = false
|
|
@@ -340,7 +341,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
340
341
|
sheetView = null
|
|
341
342
|
|
|
342
343
|
interactionState = InteractionState.Idle
|
|
343
|
-
|
|
344
|
+
isBeingDismissed = false
|
|
344
345
|
isPresented = false
|
|
345
346
|
isSheetVisible = false
|
|
346
347
|
wasHiddenByScreen = false
|
|
@@ -479,8 +480,8 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
479
480
|
|
|
480
481
|
private fun handleStateChanged(sheetView: View, newState: Int) {
|
|
481
482
|
if (newState == BottomSheetBehavior.STATE_HIDDEN) {
|
|
482
|
-
if (
|
|
483
|
-
|
|
483
|
+
if (isBeingDismissed) return
|
|
484
|
+
isBeingDismissed = true
|
|
484
485
|
dismissKeyboard()
|
|
485
486
|
emitWillDismissEvents()
|
|
486
487
|
finishDismiss()
|
|
@@ -502,7 +503,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
502
503
|
|
|
503
504
|
private fun handleSlide(sheetView: View, slideOffset: Float) {
|
|
504
505
|
// Skip during dismiss animation
|
|
505
|
-
if (
|
|
506
|
+
if (isBeingDismissed) return
|
|
506
507
|
|
|
507
508
|
val behavior = behavior ?: return
|
|
508
509
|
|
|
@@ -705,9 +706,9 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
705
706
|
}
|
|
706
707
|
|
|
707
708
|
fun dismiss(animated: Boolean = true) {
|
|
708
|
-
if (
|
|
709
|
+
if (isBeingDismissed) return
|
|
709
710
|
|
|
710
|
-
|
|
711
|
+
isBeingDismissed = true
|
|
711
712
|
dismissKeyboard()
|
|
712
713
|
emitWillDismissEvents()
|
|
713
714
|
|
|
@@ -894,7 +895,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
894
895
|
if (!dimmed) return
|
|
895
896
|
if (contentHeight == 0) return
|
|
896
897
|
|
|
897
|
-
val keyboardOffset = if (
|
|
898
|
+
val keyboardOffset = if (isBeingDismissed) 0 else currentKeyboardInset
|
|
898
899
|
val top = (sheetTop ?: sheetView?.top ?: return) + keyboardOffset
|
|
899
900
|
|
|
900
901
|
if (animated) {
|
|
@@ -981,7 +982,7 @@ class TrueSheetViewController(private val reactContext: ThemedReactContext) :
|
|
|
981
982
|
if (!shouldHandleKeyboard(checkFocus = false)) return
|
|
982
983
|
|
|
983
984
|
setupSheetDetents()
|
|
984
|
-
if (!
|
|
985
|
+
if (!isBeingDismissed && detentIndexBeforeKeyboard >= 0) {
|
|
985
986
|
setStateForDetentIndex(detentIndexBeforeKeyboard)
|
|
986
987
|
}
|
|
987
988
|
}
|
|
@@ -31,18 +31,14 @@ object TrueSheetStackManager {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
35
|
-
* Returns the visible parent sheet to stack on, or null if none.
|
|
34
|
+
* Registers a sheet in the stack and returns its parent sheet if any.
|
|
36
35
|
* Only returns a parent if it's in the same container (e.g., same Screen).
|
|
37
36
|
*/
|
|
38
37
|
@JvmStatic
|
|
39
|
-
fun
|
|
38
|
+
fun registerSheet(sheetView: TrueSheetView): TrueSheetView? {
|
|
40
39
|
synchronized(presentedSheetStack) {
|
|
41
40
|
val parentSheet = findTopmostSheet()?.takeIf { it.rootContainerView == sheetView.rootContainerView }
|
|
42
41
|
|
|
43
|
-
val childSheetTop = sheetView.viewController.detentCalculator.getSheetTopForDetentIndex(detentIndex)
|
|
44
|
-
parentSheet?.updateTranslationForChild(childSheetTop)
|
|
45
|
-
|
|
46
42
|
if (!presentedSheetStack.contains(sheetView)) {
|
|
47
43
|
presentedSheetStack.add(sheetView)
|
|
48
44
|
}
|
|
@@ -52,11 +48,10 @@ object TrueSheetStackManager {
|
|
|
52
48
|
}
|
|
53
49
|
|
|
54
50
|
/**
|
|
55
|
-
*
|
|
56
|
-
* Resets parent sheet translation if this sheet was stacked on it.
|
|
51
|
+
* Unregisters a sheet from the stack and resets parent translation if needed.
|
|
57
52
|
*/
|
|
58
53
|
@JvmStatic
|
|
59
|
-
fun
|
|
54
|
+
fun unregisterSheet(sheetView: TrueSheetView, hadParent: Boolean) {
|
|
60
55
|
synchronized(presentedSheetStack) {
|
|
61
56
|
presentedSheetStack.remove(sheetView)
|
|
62
57
|
if (hadParent) {
|
|
@@ -66,12 +61,11 @@ object TrueSheetStackManager {
|
|
|
66
61
|
}
|
|
67
62
|
|
|
68
63
|
/**
|
|
69
|
-
*
|
|
70
|
-
* Updates parent sheet translations to match the new sheet position.
|
|
64
|
+
* Updates parent sheet translation based on the child sheet's position.
|
|
71
65
|
* Only affects parent sheets in the same container.
|
|
72
66
|
*/
|
|
73
67
|
@JvmStatic
|
|
74
|
-
fun
|
|
68
|
+
fun updateParentTranslation(sheetView: TrueSheetView) {
|
|
75
69
|
synchronized(presentedSheetStack) {
|
|
76
70
|
val index = presentedSheetStack.indexOf(sheetView)
|
|
77
71
|
val parentSheet = getParentSheetAt(index, sheetView.rootContainerView) ?: return
|
|
@@ -138,9 +138,15 @@ using namespace facebook::react;
|
|
|
138
138
|
CGFloat newHeight = containerView.bounds.size.height - scrollViewFrameInContainer.origin.y;
|
|
139
139
|
|
|
140
140
|
if (newHeight > 0) {
|
|
141
|
+
// Preserve contentOffset before changing frame to prevent scroll jump
|
|
142
|
+
CGPoint savedContentOffset = _pinnedScrollView.scrollView.contentOffset;
|
|
143
|
+
|
|
141
144
|
CGRect frame = _pinnedScrollView.frame;
|
|
142
145
|
frame.size.height = newHeight;
|
|
143
146
|
_pinnedScrollView.frame = frame;
|
|
147
|
+
|
|
148
|
+
// Restore contentOffset after frame change
|
|
149
|
+
_pinnedScrollView.scrollView.contentOffset = savedContentOffset;
|
|
144
150
|
}
|
|
145
151
|
}
|
|
146
152
|
|
package/ios/TrueSheetModule.mm
CHANGED
|
@@ -93,6 +93,30 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
+
- (void)dismissStackByRef:(double)viewTag
|
|
97
|
+
animated:(BOOL)animated
|
|
98
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
99
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
100
|
+
RCTExecuteOnMainQueue(^{
|
|
101
|
+
TrueSheetView *trueSheetView = [TrueSheetModule getTrueSheetViewByTag:@((NSInteger)viewTag)];
|
|
102
|
+
|
|
103
|
+
if (!trueSheetView) {
|
|
104
|
+
reject(@"SHEET_NOT_FOUND", [NSString stringWithFormat:@"No sheet found with tag %d", (int)viewTag], nil);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
[trueSheetView
|
|
109
|
+
dismissStackAnimated:animated
|
|
110
|
+
completion:^(BOOL success, NSError *_Nullable error) {
|
|
111
|
+
if (success) {
|
|
112
|
+
resolve(nil);
|
|
113
|
+
} else {
|
|
114
|
+
reject(@"DISMISS_FAILED", error.localizedDescription ?: @"Failed to dismiss stack", error);
|
|
115
|
+
}
|
|
116
|
+
}];
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
96
120
|
- (void)resizeByRef:(double)viewTag
|
|
97
121
|
index:(double)index
|
|
98
122
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
@@ -141,15 +165,15 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
141
165
|
return;
|
|
142
166
|
}
|
|
143
167
|
|
|
144
|
-
[rootSheet
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
[rootSheet emitDismissedPosition];
|
|
169
|
+
[rootSheet dismissAnimated:animated
|
|
170
|
+
completion:^(BOOL success, NSError *_Nullable error) {
|
|
171
|
+
if (success) {
|
|
172
|
+
resolve(nil);
|
|
173
|
+
} else {
|
|
174
|
+
reject(@"DISMISS_FAILED", error.localizedDescription ?: @"Failed to dismiss sheets", error);
|
|
175
|
+
}
|
|
176
|
+
}];
|
|
153
177
|
}
|
|
154
178
|
});
|
|
155
179
|
}
|
package/ios/TrueSheetView.h
CHANGED
|
@@ -32,9 +32,11 @@ typedef void (^TrueSheetCompletionBlock)(BOOL success, NSError *_Nullable error)
|
|
|
32
32
|
|
|
33
33
|
- (void)dismissAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion;
|
|
34
34
|
|
|
35
|
+
- (void)emitDismissedPosition;
|
|
36
|
+
|
|
35
37
|
- (void)resizeToIndex:(NSInteger)index completion:(nullable TrueSheetCompletionBlock)completion;
|
|
36
38
|
|
|
37
|
-
- (void)
|
|
39
|
+
- (void)dismissStackAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion;
|
|
38
40
|
|
|
39
41
|
@end
|
|
40
42
|
|
package/ios/TrueSheetView.mm
CHANGED
|
@@ -111,7 +111,7 @@ using namespace facebook::react;
|
|
|
111
111
|
UIViewController *vc = [self findPresentingViewController];
|
|
112
112
|
|
|
113
113
|
// Only present if the view controller is in the same window and not being dismissed
|
|
114
|
-
if (vc && vc.view.window == self.window && !
|
|
114
|
+
if (vc && vc.view.window == self.window && !_controller.isBeingDismissed) {
|
|
115
115
|
_didInitiallyPresent = YES;
|
|
116
116
|
[self presentAtIndex:_initialDetentIndex animated:_initialDetentAnimated completion:nil];
|
|
117
117
|
} else {
|
|
@@ -407,12 +407,7 @@ using namespace facebook::react;
|
|
|
407
407
|
- (void)presentAtIndex:(NSInteger)index
|
|
408
408
|
animated:(BOOL)animated
|
|
409
409
|
completion:(nullable TrueSheetCompletionBlock)completion {
|
|
410
|
-
if (_controller.isBeingPresented) {
|
|
411
|
-
RCTLogWarn(@"TrueSheet: sheet is being presented. Wait for it to transition before presenting again.");
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
if (_controller.isPresented) {
|
|
410
|
+
if (_controller.isBeingPresented || _controller.isPresented) {
|
|
416
411
|
RCTLogWarn(@"TrueSheet: sheet is already presented. Use resize() to change detent.");
|
|
417
412
|
if (completion) {
|
|
418
413
|
completion(YES, nil);
|
|
@@ -420,6 +415,9 @@ using namespace facebook::react;
|
|
|
420
415
|
return;
|
|
421
416
|
}
|
|
422
417
|
|
|
418
|
+
// Reset navigation dismiss flag when presenting (handles view recycling edge cases)
|
|
419
|
+
_dismissedByNavigation = NO;
|
|
420
|
+
|
|
423
421
|
UIViewController *presentingViewController = [self findPresentingViewController];
|
|
424
422
|
if (!presentingViewController) {
|
|
425
423
|
NSError *error = [NSError errorWithDomain:@"com.lodev09.TrueSheet"
|
|
@@ -446,27 +444,6 @@ using namespace facebook::react;
|
|
|
446
444
|
}];
|
|
447
445
|
}
|
|
448
446
|
|
|
449
|
-
- (void)dismissAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion {
|
|
450
|
-
if (_controller.isBeingDismissed) {
|
|
451
|
-
RCTLogWarn(@"TrueSheet: sheet is being dismissed. No need to dismiss it again.");
|
|
452
|
-
return;
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (!_controller.isPresented) {
|
|
456
|
-
if (completion) {
|
|
457
|
-
completion(YES, nil);
|
|
458
|
-
}
|
|
459
|
-
return;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
[_controller dismissViewControllerAnimated:animated
|
|
463
|
-
completion:^{
|
|
464
|
-
if (completion) {
|
|
465
|
-
completion(YES, nil);
|
|
466
|
-
}
|
|
467
|
-
}];
|
|
468
|
-
}
|
|
469
|
-
|
|
470
447
|
- (void)resizeToIndex:(NSInteger)index completion:(nullable TrueSheetCompletionBlock)completion {
|
|
471
448
|
if (!_controller.isPresented) {
|
|
472
449
|
RCTLogWarn(@"TrueSheet: Cannot resize. Sheet is not presented.");
|
|
@@ -489,16 +466,20 @@ using namespace facebook::react;
|
|
|
489
466
|
return _controller;
|
|
490
467
|
}
|
|
491
468
|
|
|
492
|
-
- (void)
|
|
493
|
-
|
|
469
|
+
- (void)emitDismissedPosition {
|
|
470
|
+
[self viewControllerDidChangePosition:-1 position:_controller.screenHeight detent:0 realtime:NO];
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
- (void)dismissAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion {
|
|
474
|
+
if (_controller.isBeingDismissed || !_controller.isPresented) {
|
|
475
|
+
RCTLogWarn(@"TrueSheet: sheet is already dismissed. No need to dismiss it again.");
|
|
476
|
+
|
|
494
477
|
if (completion) {
|
|
495
478
|
completion(YES, nil);
|
|
496
479
|
}
|
|
497
480
|
return;
|
|
498
481
|
}
|
|
499
482
|
|
|
500
|
-
[self viewControllerDidChangePosition:-1 position:_controller.screenHeight detent:0 realtime:NO];
|
|
501
|
-
|
|
502
483
|
// Dismiss from the presenting view controller to dismiss this sheet and all its children
|
|
503
484
|
UIViewController *presenter = _controller.presentingViewController;
|
|
504
485
|
[presenter dismissViewControllerAnimated:animated
|
|
@@ -509,6 +490,33 @@ using namespace facebook::react;
|
|
|
509
490
|
}];
|
|
510
491
|
}
|
|
511
492
|
|
|
493
|
+
- (void)dismissStackAnimated:(BOOL)animated completion:(nullable TrueSheetCompletionBlock)completion {
|
|
494
|
+
if (_controller.isBeingDismissed || !_controller.isPresented) {
|
|
495
|
+
RCTLogWarn(@"TrueSheet: sheet is already dismissed. No need to dismiss it again.");
|
|
496
|
+
|
|
497
|
+
if (completion) {
|
|
498
|
+
completion(YES, nil);
|
|
499
|
+
}
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Only dismiss presented children, not this sheet itself
|
|
504
|
+
if (!_controller.presentedViewController) {
|
|
505
|
+
if (completion) {
|
|
506
|
+
completion(YES, nil);
|
|
507
|
+
}
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Calling dismiss on _controller dismisses all VCs presented on top of it, but keeps _controller presented
|
|
512
|
+
[_controller dismissViewControllerAnimated:animated
|
|
513
|
+
completion:^{
|
|
514
|
+
if (completion) {
|
|
515
|
+
completion(YES, nil);
|
|
516
|
+
}
|
|
517
|
+
}];
|
|
518
|
+
}
|
|
519
|
+
|
|
512
520
|
#pragma mark - TrueSheetContainerViewDelegate
|
|
513
521
|
|
|
514
522
|
/**
|
|
@@ -627,7 +635,7 @@ using namespace facebook::react;
|
|
|
627
635
|
- (void)presenterScreenWillDisappear {
|
|
628
636
|
if (_controller.isPresented && !_controller.isBeingDismissed) {
|
|
629
637
|
_dismissedByNavigation = YES;
|
|
630
|
-
[self
|
|
638
|
+
[self dismissAnimated:YES completion:nil];
|
|
631
639
|
}
|
|
632
640
|
}
|
|
633
641
|
|
|
@@ -69,6 +69,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
69
69
|
@property (nonatomic, assign) BOOL isPresented;
|
|
70
70
|
@property (nonatomic, assign) NSInteger activeDetentIndex;
|
|
71
71
|
@property (nonatomic, readonly) BOOL isTopmostPresentedController;
|
|
72
|
+
|
|
72
73
|
@property (nonatomic, readonly) CGFloat screenHeight;
|
|
73
74
|
|
|
74
75
|
- (void)applyActiveDetent;
|
|
@@ -222,12 +222,8 @@
|
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
|
|
225
|
-
- (BOOL)isDismissing {
|
|
226
|
-
return self.presentingViewController == nil || self.isBeingDismissed;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
225
|
- (void)emitWillDismissEvents {
|
|
230
|
-
if (self.
|
|
226
|
+
if (self.isBeingDismissed && !_isWillDismissEmitted) {
|
|
231
227
|
_isWillDismissEmitted = YES;
|
|
232
228
|
|
|
233
229
|
[self.delegate viewControllerWillBlur];
|
|
@@ -237,7 +233,7 @@
|
|
|
237
233
|
}
|
|
238
234
|
|
|
239
235
|
- (void)emitDidDismissEvents {
|
|
240
|
-
if (self.
|
|
236
|
+
if (self.isBeingDismissed) {
|
|
241
237
|
_isPresented = NO;
|
|
242
238
|
_isWillDismissEmitted = NO;
|
|
243
239
|
|
|
@@ -398,12 +394,12 @@
|
|
|
398
394
|
CGRect dismissedFrame = CGRectMake(0, self.screenHeight, 0, 0);
|
|
399
395
|
CGRect presentedFrame = CGRectMake(0, self.currentPosition, 0, 0);
|
|
400
396
|
|
|
401
|
-
_transitionFakeView.frame = self.
|
|
397
|
+
_transitionFakeView.frame = self.isBeingDismissed ? presentedFrame : dismissedFrame;
|
|
402
398
|
[self storeResolvedPositionForIndex:self.currentDetentIndex];
|
|
403
399
|
|
|
404
400
|
auto animation = ^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
|
|
405
401
|
[[context containerView] addSubview:self->_transitionFakeView];
|
|
406
|
-
self->_transitionFakeView.frame = self.
|
|
402
|
+
self->_transitionFakeView.frame = self.isBeingDismissed ? dismissedFrame : presentedFrame;
|
|
407
403
|
|
|
408
404
|
self->_transitioningTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleTransitionTracker)];
|
|
409
405
|
[self->_transitioningTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
|
@@ -19,14 +19,14 @@ using namespace facebook::react;
|
|
|
19
19
|
@implementation RNScreensEventObserver {
|
|
20
20
|
std::weak_ptr<const EventDispatcher> _eventDispatcher;
|
|
21
21
|
std::shared_ptr<const EventListener> _eventListener;
|
|
22
|
-
|
|
22
|
+
NSMutableSet<NSNumber *> *_screenTags;
|
|
23
23
|
__weak UIViewController *_presenterScreenController;
|
|
24
24
|
BOOL _dismissedByNavigation;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
- (instancetype)init {
|
|
28
28
|
if (self = [super init]) {
|
|
29
|
-
|
|
29
|
+
_screenTags = [NSMutableSet new];
|
|
30
30
|
_presenterScreenController = nil;
|
|
31
31
|
}
|
|
32
32
|
return self;
|
|
@@ -61,7 +61,7 @@ using namespace facebook::react;
|
|
|
61
61
|
[strongSelf.delegate presenterScreenWillDisappear];
|
|
62
62
|
}
|
|
63
63
|
} else if (event.type == "topWillAppear") {
|
|
64
|
-
if (
|
|
64
|
+
if ([strongSelf->_screenTags containsObject:@(screenTag)] && strongSelf->_dismissedByNavigation) {
|
|
65
65
|
strongSelf->_dismissedByNavigation = NO;
|
|
66
66
|
[strongSelf.delegate presenterScreenWillAppear];
|
|
67
67
|
}
|
|
@@ -85,33 +85,41 @@ using namespace facebook::react;
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
- (void)capturePresenterScreenFromView:(UIView *)view {
|
|
88
|
-
|
|
88
|
+
[_screenTags removeAllObjects];
|
|
89
89
|
_presenterScreenController = nil;
|
|
90
90
|
|
|
91
91
|
for (UIView *current = view.superview; current; current = current.superview) {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
if ([NSStringFromClass([current class]) isEqualToString:@"RNSScreenView"]) {
|
|
93
|
+
[_screenTags addObject:@(current.tag)];
|
|
94
|
+
|
|
95
|
+
// Capture the view controller from the first (immediate presenter) screen
|
|
96
|
+
if (!_presenterScreenController) {
|
|
97
|
+
for (UIResponder *r = current.nextResponder; r; r = r.nextResponder) {
|
|
98
|
+
if ([r isKindOfClass:[UIViewController class]]) {
|
|
99
|
+
_presenterScreenController = (UIViewController *)r;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
|
-
break;
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
- (BOOL)shouldDismissForScreenTag:(NSInteger)screenTag {
|
|
108
|
-
if (
|
|
109
|
+
if (![_screenTags containsObject:@(screenTag)]) {
|
|
109
110
|
return NO;
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
UINavigationController *navController = _presenterScreenController.navigationController;
|
|
114
|
+
|
|
115
|
+
// If nav controller is nil or being dismissed, dismiss the sheet
|
|
116
|
+
if (!navController || navController.isBeingDismissed) {
|
|
117
|
+
return YES;
|
|
118
|
+
}
|
|
119
|
+
|
|
112
120
|
// Skip if screen is still top of nav stack (e.g. modal dismiss - sheet dismisses naturally with modal)
|
|
113
121
|
// Dismiss if a new screen was pushed or popped
|
|
114
|
-
return
|
|
122
|
+
return navController.topViewController != _presenterScreenController;
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
@end
|