@lodev09/react-native-true-sheet 4.0.0-beta.1 → 4.0.0-beta.10
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/README.md +25 -4
- package/android/build.gradle +22 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +24 -7
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +211 -182
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +14 -19
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +106 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +18 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +82 -39
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +275 -88
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +26 -1
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +75 -3
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +56 -34
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +137 -0
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +1 -1
- package/android/src/main/java/com/lodev09/truesheet/utils/TouchEventDeduper.kt +31 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +0 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +54 -29
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +6 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +6 -4
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +7 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +46 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +36 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +4 -35
- package/ios/TrueSheetContainerView.h +14 -7
- package/ios/TrueSheetContainerView.mm +10 -7
- package/ios/TrueSheetContentView.h +31 -15
- package/ios/TrueSheetContentView.mm +133 -179
- package/ios/TrueSheetFooterView.h +6 -0
- package/ios/TrueSheetFooterView.mm +48 -2
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +7 -16
- package/ios/TrueSheetOverlayView.h +32 -0
- package/ios/TrueSheetOverlayView.mm +161 -0
- package/ios/TrueSheetView.mm +171 -29
- package/ios/TrueSheetViewController.h +10 -0
- package/ios/TrueSheetViewController.mm +256 -74
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetOverlayContainerView.h +31 -0
- package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
- package/ios/events/TrueSheetLifecycleEvents.h +2 -0
- package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
- package/ios/tvos/TrueSheetStubs.mm +12 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
- package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
- package/lib/module/TrueSheet.js +149 -34
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +122 -53
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetOverlay.js +30 -0
- package/lib/module/TrueSheetOverlay.js.map +1 -0
- package/lib/module/TrueSheetOverlay.web.js +35 -0
- package/lib/module/TrueSheetOverlay.web.js.map +1 -0
- package/lib/module/TrueSheetPeek.web.js +3 -3
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +5 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +10 -0
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigation.js +31 -21
- package/lib/module/mocks/navigation.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js +21 -0
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -0
- package/lib/module/mocks/reanimated.js +1 -1
- package/lib/module/mocks/reanimated.js.map +1 -1
- package/lib/module/navigation/{TrueSheetView.js → TrueSheetNavigatorContent.js} +14 -12
- package/lib/module/navigation/TrueSheetNavigatorContent.js.map +1 -0
- package/lib/module/navigation/actions.js +50 -0
- package/lib/module/navigation/actions.js.map +1 -0
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/{TrueSheetRouter.js → createTrueSheetRouter.js} +11 -24
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -0
- package/lib/module/navigation/expo-router/base-types.js +2 -0
- package/lib/module/navigation/expo-router/base-types.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +50 -0
- package/lib/module/navigation/expo-router/index.js.map +1 -0
- package/lib/module/navigation/expo-router/types.js +4 -0
- package/lib/module/navigation/expo-router/types.js.map +1 -0
- package/lib/module/navigation/index.js +43 -2
- package/lib/module/navigation/index.js.map +1 -1
- package/lib/module/navigation/navigator.js +10 -0
- package/lib/module/navigation/navigator.js.map +1 -0
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js +2 -2
- package/lib/module/navigation/screen/ReanimatedTrueSheetScreen.js.map +1 -1
- package/lib/module/navigation/screen/TrueSheetScreen.js +2 -2
- package/lib/module/navigation/screen/TrueSheetScreen.js.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +5 -4
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.js +1 -1
- package/lib/module/navigation/useTrueSheetNavigation.js.map +1 -1
- package/lib/module/web/dom.js +10 -0
- package/lib/module/web/dom.js.map +1 -0
- package/lib/module/web/vaul/constants.js +4 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +10 -4
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +35 -2
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +6 -0
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +72 -1
- package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
- package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
- package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +4 -0
- package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/index.d.ts +4 -0
- package/lib/typescript/src/mocks/index.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigation.d.ts +5 -1
- package/lib/typescript/src/mocks/navigation.d.ts.map +1 -1
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +22 -0
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -0
- package/lib/typescript/src/mocks/reanimated.d.ts.map +1 -1
- package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts +5 -0
- package/lib/typescript/src/navigation/TrueSheetNavigatorContent.d.ts.map +1 -0
- package/lib/typescript/src/navigation/actions.d.ts +71 -0
- package/lib/typescript/src/navigation/actions.d.ts.map +1 -0
- package/lib/typescript/src/navigation/base-types.d.ts +9 -0
- package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +3 -0
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
- package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +37 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
- package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
- package/lib/typescript/src/navigation/index.d.ts +42 -3
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/navigator.d.ts +7 -0
- package/lib/typescript/src/navigation/navigator.d.ts.map +1 -0
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/ReanimatedTrueSheetScreen.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts +1 -1
- package/lib/typescript/src/navigation/screen/TrueSheetScreen.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/types.d.ts +3 -4
- package/lib/typescript/src/navigation/screen/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +5 -6
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +65 -5
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts +1 -1
- package/lib/typescript/src/navigation/useTrueSheetNavigation.d.ts.map +1 -1
- package/lib/typescript/src/web/dom.d.ts +7 -0
- package/lib/typescript/src/web/dom.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +1 -0
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +7 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/package.json +32 -9
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +184 -46
- package/src/TrueSheet.types.ts +77 -1
- package/src/TrueSheet.web.tsx +135 -64
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +4 -3
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +5 -0
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +7 -0
- package/src/mocks/navigation.ts +32 -25
- package/src/mocks/navigationExpoRouter.ts +41 -0
- package/src/mocks/reanimated.ts +8 -9
- package/src/navigation/{TrueSheetView.tsx → TrueSheetNavigatorContent.tsx} +24 -17
- package/src/navigation/actions.ts +78 -0
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +194 -0
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/index.ts +90 -0
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +68 -2
- package/src/navigation/navigator.ts +18 -0
- package/src/navigation/screen/ReanimatedTrueSheetScreen.tsx +2 -2
- package/src/navigation/screen/TrueSheetScreen.tsx +2 -2
- package/src/navigation/screen/types.ts +4 -6
- package/src/navigation/screen/useSheetScreenState.ts +10 -15
- package/src/navigation/types.ts +99 -16
- package/src/navigation/useTrueSheetNavigation.ts +1 -1
- package/src/web/dom.ts +9 -0
- package/src/web/vaul/constants.ts +4 -0
- package/src/web/vaul/context.ts +1 -0
- package/src/web/vaul/helpers.ts +13 -6
- package/src/web/vaul/index.tsx +46 -1
- package/lib/module/navigation/TrueSheetRouter.js.map +0 -1
- package/lib/module/navigation/TrueSheetView.js.map +0 -1
- package/lib/module/navigation/createTrueSheetNavigator.js +0 -63
- package/lib/module/navigation/createTrueSheetNavigator.js.map +0 -1
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts +0 -57
- package/lib/typescript/src/navigation/TrueSheetRouter.d.ts.map +0 -1
- package/lib/typescript/src/navigation/TrueSheetView.d.ts +0 -10
- package/lib/typescript/src/navigation/TrueSheetView.d.ts.map +0 -1
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts +0 -35
- package/lib/typescript/src/navigation/createTrueSheetNavigator.d.ts.map +0 -1
- package/src/navigation/TrueSheetRouter.ts +0 -234
- package/src/navigation/createTrueSheetNavigator.tsx +0 -93
package/ios/TrueSheetModule.mm
CHANGED
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
|
|
19
19
|
#import <TrueSheetSpec/TrueSheetSpec.h>
|
|
20
20
|
|
|
21
|
-
// Static registry
|
|
22
|
-
|
|
21
|
+
// Static registry of live hosts by tag — weak so an unmounted host deallocates
|
|
22
|
+
// (RN zeroes the tag before teardown, so hosts can't unregister themselves)
|
|
23
|
+
static NSMapTable<NSNumber *, TrueSheetView *> *viewRegistry;
|
|
23
24
|
|
|
24
25
|
@interface TrueSheetModule () <NativeTrueSheetModuleSpec>
|
|
25
26
|
@end
|
|
@@ -30,7 +31,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
30
31
|
|
|
31
32
|
+ (void)initialize {
|
|
32
33
|
if (self == [TrueSheetModule class]) {
|
|
33
|
-
viewRegistry = [
|
|
34
|
+
viewRegistry = [NSMapTable strongToWeakObjectsMapTable];
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -151,7 +152,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
151
152
|
// Find the root presented sheet (one without a parent TrueSheet)
|
|
152
153
|
TrueSheetView *rootSheet = nil;
|
|
153
154
|
|
|
154
|
-
for (TrueSheetView *view in viewRegistry.
|
|
155
|
+
for (TrueSheetView *view in viewRegistry.objectEnumerator) {
|
|
155
156
|
if (!view.viewController.isPresented) {
|
|
156
157
|
continue;
|
|
157
158
|
}
|
|
@@ -191,7 +192,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
191
192
|
}
|
|
192
193
|
|
|
193
194
|
@synchronized(viewRegistry) {
|
|
194
|
-
return viewRegistry
|
|
195
|
+
return [viewRegistry objectForKey:reactTag];
|
|
195
196
|
}
|
|
196
197
|
}
|
|
197
198
|
|
|
@@ -201,17 +202,7 @@ RCT_EXPORT_MODULE(TrueSheetModule)
|
|
|
201
202
|
}
|
|
202
203
|
|
|
203
204
|
@synchronized(viewRegistry) {
|
|
204
|
-
viewRegistry
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
+ (void)unregisterViewWithTag:(NSNumber *)tag {
|
|
209
|
-
if (!tag) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
@synchronized(viewRegistry) {
|
|
214
|
-
[viewRegistry removeObjectForKey:tag];
|
|
205
|
+
[viewRegistry setObject:view forKey:tag];
|
|
215
206
|
}
|
|
216
207
|
}
|
|
217
208
|
|
|
@@ -0,0 +1,32 @@
|
|
|
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 <React/RCTViewComponentView.h>
|
|
12
|
+
#import <UIKit/UIKit.h>
|
|
13
|
+
|
|
14
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Hidden host that renders its children in a container attached directly to
|
|
18
|
+
* the window, above every presented sheet.
|
|
19
|
+
*/
|
|
20
|
+
@interface TrueSheetOverlayView : RCTViewComponentView
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Re-stacks every overlay container above the window's other subviews.
|
|
24
|
+
* Called when a sheet presents, since presenting adds its transition view on top.
|
|
25
|
+
*/
|
|
26
|
+
+ (void)bringOverlaysToFrontInWindow:(nullable UIWindow *)window;
|
|
27
|
+
|
|
28
|
+
@end
|
|
29
|
+
|
|
30
|
+
NS_ASSUME_NONNULL_END
|
|
31
|
+
|
|
32
|
+
#endif
|
|
@@ -0,0 +1,161 @@
|
|
|
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 "TrueSheetOverlayView.h"
|
|
12
|
+
#import "core/TrueSheetOverlayContainerView.h"
|
|
13
|
+
|
|
14
|
+
#import <react/renderer/components/TrueSheetSpec/EventEmitters.h>
|
|
15
|
+
#import <react/renderer/components/TrueSheetSpec/Props.h>
|
|
16
|
+
#import <react/renderer/components/TrueSheetSpec/RCTComponentViewHelpers.h>
|
|
17
|
+
#import <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h>
|
|
18
|
+
#import <react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h>
|
|
19
|
+
|
|
20
|
+
#import <React/RCTSurfaceTouchHandler.h>
|
|
21
|
+
|
|
22
|
+
using namespace facebook::react;
|
|
23
|
+
|
|
24
|
+
static NSHashTable<TrueSheetOverlayView *> *gOverlayViews;
|
|
25
|
+
|
|
26
|
+
@interface TrueSheetOverlayView () <TrueSheetOverlayContainerViewDelegate>
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
@implementation TrueSheetOverlayView {
|
|
30
|
+
TrueSheetOverlayContainerView *_containerView;
|
|
31
|
+
// Owns `_containerView` so it has a view controller in its responder chain. The
|
|
32
|
+
// container is a sibling of `rootViewController.view`, and a window's responder chain
|
|
33
|
+
// skips its root view controller — without an owner, `-[UIView reactViewController]`
|
|
34
|
+
// finds nothing and content that installs a view controller cannot attach.
|
|
35
|
+
UIViewController *_containerController;
|
|
36
|
+
RCTSurfaceTouchHandler *_touchHandler;
|
|
37
|
+
TrueSheetOverlayViewShadowNode::ConcreteState::Shared _state;
|
|
38
|
+
CGSize _lastStateSize;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
42
|
+
return concreteComponentDescriptorProvider<TrueSheetOverlayViewComponentDescriptor>();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
+ (void)bringOverlaysToFrontInWindow:(nullable UIWindow *)window {
|
|
46
|
+
if (!window)
|
|
47
|
+
return;
|
|
48
|
+
|
|
49
|
+
for (TrueSheetOverlayView *overlay in gOverlayViews) {
|
|
50
|
+
if (overlay->_containerView.superview == window) {
|
|
51
|
+
[window bringSubviewToFront:overlay->_containerView];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
57
|
+
if (self = [super initWithFrame:frame]) {
|
|
58
|
+
static const auto defaultProps = std::make_shared<const TrueSheetOverlayViewProps>();
|
|
59
|
+
_props = defaultProps;
|
|
60
|
+
|
|
61
|
+
_containerView = [[TrueSheetOverlayContainerView alloc] initWithFrame:CGRectZero];
|
|
62
|
+
_containerView.delegate = self;
|
|
63
|
+
|
|
64
|
+
// Assigning the container as its view is what puts the controller in the responder
|
|
65
|
+
// chain; the containment below is for lifecycle only.
|
|
66
|
+
_containerController = [UIViewController new];
|
|
67
|
+
_containerController.view = _containerView;
|
|
68
|
+
|
|
69
|
+
_touchHandler = [[RCTSurfaceTouchHandler alloc] init];
|
|
70
|
+
[_touchHandler attachToView:_containerView];
|
|
71
|
+
|
|
72
|
+
_lastStateSize = CGSizeZero;
|
|
73
|
+
|
|
74
|
+
// The host stays in the React tree for layout only — children render in the container
|
|
75
|
+
self.hidden = YES;
|
|
76
|
+
|
|
77
|
+
if (!gOverlayViews) {
|
|
78
|
+
gOverlayViews = [NSHashTable weakObjectsHashTable];
|
|
79
|
+
}
|
|
80
|
+
[gOverlayViews addObject:self];
|
|
81
|
+
}
|
|
82
|
+
return self;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (void)didMoveToWindow {
|
|
86
|
+
[super didMoveToWindow];
|
|
87
|
+
|
|
88
|
+
UIWindow *window = self.window;
|
|
89
|
+
if (window) {
|
|
90
|
+
_containerView.frame = window.bounds;
|
|
91
|
+
[window addSubview:_containerView];
|
|
92
|
+
|
|
93
|
+
UIViewController *root = window.rootViewController;
|
|
94
|
+
if (root != nil && _containerController.parentViewController != root) {
|
|
95
|
+
[root addChildViewController:_containerController];
|
|
96
|
+
[_containerController didMoveToParentViewController:root];
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
[_containerController willMoveToParentViewController:nil];
|
|
100
|
+
[_containerView removeFromSuperview];
|
|
101
|
+
[_containerController removeFromParentViewController];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#pragma mark - State
|
|
106
|
+
|
|
107
|
+
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState {
|
|
108
|
+
_state = std::static_pointer_cast<TrueSheetOverlayViewShadowNode::ConcreteState const>(state);
|
|
109
|
+
|
|
110
|
+
if (self.window) {
|
|
111
|
+
[self updateStateWithSize:self.window.bounds.size];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
- (void)overlayContainerViewDidLayout:(CGSize)size {
|
|
116
|
+
[self updateStateWithSize:size];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
- (void)updateStateWithSize:(CGSize)size {
|
|
120
|
+
if (!_state)
|
|
121
|
+
return;
|
|
122
|
+
|
|
123
|
+
if (fabs(size.width - _lastStateSize.width) < 0.5 && fabs(size.height - _lastStateSize.height) < 0.5)
|
|
124
|
+
return;
|
|
125
|
+
|
|
126
|
+
_lastStateSize = size;
|
|
127
|
+
|
|
128
|
+
auto stateData = _state->getData();
|
|
129
|
+
stateData.containerWidth = static_cast<float>(size.width);
|
|
130
|
+
stateData.containerHeight = static_cast<float>(size.height);
|
|
131
|
+
_state->updateState(std::move(stateData), EventQueue::UpdateMode::unstable_Immediate);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
#pragma mark - Child Component Mounting
|
|
135
|
+
|
|
136
|
+
- (void)mountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
137
|
+
[_containerView insertSubview:childComponentView atIndex:index];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
- (void)unmountChildComponentView:(UIView<RCTComponentViewProtocol> *)childComponentView index:(NSInteger)index {
|
|
141
|
+
[childComponentView removeFromSuperview];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
- (void)prepareForRecycle {
|
|
145
|
+
[super prepareForRecycle];
|
|
146
|
+
|
|
147
|
+
[_containerController willMoveToParentViewController:nil];
|
|
148
|
+
[_containerView removeFromSuperview];
|
|
149
|
+
[_containerController removeFromParentViewController];
|
|
150
|
+
_lastStateSize = CGSizeZero;
|
|
151
|
+
_state.reset();
|
|
152
|
+
self.hidden = YES;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
@end
|
|
156
|
+
|
|
157
|
+
Class<RCTComponentViewProtocol> TrueSheetOverlayViewCls(void) {
|
|
158
|
+
return TrueSheetOverlayView.class;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
#endif
|
package/ios/TrueSheetView.mm
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#import "TrueSheetContentView.h"
|
|
14
14
|
#import "TrueSheetFooterView.h"
|
|
15
15
|
#import "TrueSheetModule.h"
|
|
16
|
+
#import "TrueSheetOverlayView.h"
|
|
16
17
|
#import "TrueSheetViewController.h"
|
|
17
18
|
#import "core/RNScreensEventObserver.h"
|
|
18
19
|
#import "events/TrueSheetDragEvents.h"
|
|
@@ -53,7 +54,7 @@ using namespace facebook::react;
|
|
|
53
54
|
NSInteger _initialDetentIndex;
|
|
54
55
|
TrueSheetViewInsetAdjustment _insetAdjustment;
|
|
55
56
|
ScrollableOptions *_scrollableOptions;
|
|
56
|
-
|
|
57
|
+
NSInteger _scrollableHandle;
|
|
57
58
|
BOOL _initialDetentAnimated;
|
|
58
59
|
BOOL _isSheetUpdatePending;
|
|
59
60
|
BOOL _pendingLayoutUpdate;
|
|
@@ -110,18 +111,72 @@ using namespace facebook::react;
|
|
|
110
111
|
return;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
|
|
114
|
-
|
|
114
|
+
[self presentInitialIfNeeded];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// JS holds initialDetentIndex back one commit so effect-driven content (e.g. a
|
|
118
|
+
// navigation footer set via setOptions) commits together with it — by the time
|
|
119
|
+
// the prop lands the tree is final and the sheet presents at its final height.
|
|
120
|
+
// Called from didMoveToWindow (re-attach) and finalizeUpdates (the prop
|
|
121
|
+
// arriving after attach, which didMoveToWindow won't re-fire for).
|
|
122
|
+
- (void)presentInitialIfNeeded {
|
|
123
|
+
if (_initialDetentIndex < 0 || _didInitiallyPresent)
|
|
124
|
+
return;
|
|
115
125
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
126
|
+
UIViewController *vc = [self findPresentingViewController];
|
|
127
|
+
|
|
128
|
+
// Only present if the view controller is in the same window
|
|
129
|
+
if (!vc || vc.view.window != self.window) {
|
|
130
|
+
// Animate next time when sheet finally moves to the correct window
|
|
131
|
+
_initialDetentAnimated = YES;
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// A sheet still animating out of the presenter (e.g. our own controller,
|
|
136
|
+
// recycled mid-dismissal) blocks presentation — retry once its transition ends.
|
|
137
|
+
UIViewController *dismissing = vc.presentedViewController;
|
|
138
|
+
if (dismissing && dismissing.isBeingDismissed) {
|
|
139
|
+
_initialDetentAnimated = YES;
|
|
140
|
+
|
|
141
|
+
__weak __typeof(self) weakSelf = self;
|
|
142
|
+
void (^retry)(void) = ^{
|
|
143
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
144
|
+
[weakSelf presentInitialIfNeeded];
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
id<UIViewControllerTransitionCoordinator> coordinator = dismissing.transitionCoordinator;
|
|
149
|
+
if (coordinator) {
|
|
150
|
+
[coordinator animateAlongsideTransition:nil
|
|
151
|
+
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
152
|
+
retry();
|
|
153
|
+
}];
|
|
120
154
|
} else {
|
|
121
|
-
|
|
122
|
-
_initialDetentAnimated = YES;
|
|
155
|
+
retry();
|
|
123
156
|
}
|
|
157
|
+
return;
|
|
124
158
|
}
|
|
159
|
+
|
|
160
|
+
_didInitiallyPresent = YES;
|
|
161
|
+
|
|
162
|
+
// Deferred a runloop turn so sibling mounts from the current transaction
|
|
163
|
+
// (e.g. a footer committed alongside the prop) land before measuring.
|
|
164
|
+
__weak __typeof(self) weakSelf = self;
|
|
165
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
166
|
+
__typeof(self) strongSelf = weakSelf;
|
|
167
|
+
if (!strongSelf || strongSelf->_controller.isPresented || strongSelf->_controller.isBeingPresented)
|
|
168
|
+
return;
|
|
169
|
+
|
|
170
|
+
[strongSelf presentAtIndex:strongSelf->_initialDetentIndex
|
|
171
|
+
animated:strongSelf->_initialDetentAnimated
|
|
172
|
+
completion:^(BOOL success, NSError *_Nullable error) {
|
|
173
|
+
// Present can fail if the view detached during this turn —
|
|
174
|
+
// reset so the next attach/prop update retries.
|
|
175
|
+
if (!success) {
|
|
176
|
+
strongSelf->_didInitiallyPresent = NO;
|
|
177
|
+
}
|
|
178
|
+
}];
|
|
179
|
+
});
|
|
125
180
|
}
|
|
126
181
|
|
|
127
182
|
- (void)dealloc {
|
|
@@ -146,8 +201,6 @@ using namespace facebook::react;
|
|
|
146
201
|
|
|
147
202
|
[_snapshotView removeFromSuperview];
|
|
148
203
|
_snapshotView = nil;
|
|
149
|
-
|
|
150
|
-
[TrueSheetModule unregisterViewWithTag:@(self.tag)];
|
|
151
204
|
}
|
|
152
205
|
|
|
153
206
|
#pragma mark - RCTComponentViewProtocol
|
|
@@ -163,12 +216,8 @@ using namespace facebook::react;
|
|
|
163
216
|
|
|
164
217
|
// Detents (-1 represents "auto")
|
|
165
218
|
NSMutableArray *detents = [NSMutableArray new];
|
|
166
|
-
_hasAutoDetent = NO;
|
|
167
219
|
for (const auto &detent : newProps.detents) {
|
|
168
220
|
[detents addObject:@(detent)];
|
|
169
|
-
if (detent == -1) {
|
|
170
|
-
_hasAutoDetent = YES;
|
|
171
|
-
}
|
|
172
221
|
}
|
|
173
222
|
|
|
174
223
|
if (oldProps) {
|
|
@@ -248,17 +297,23 @@ using namespace facebook::react;
|
|
|
248
297
|
_initialDetentIndex = newProps.initialDetentIndex;
|
|
249
298
|
_initialDetentAnimated = newProps.initialDetentAnimated;
|
|
250
299
|
|
|
300
|
+
_scrollableHandle = newProps.scrollableHandle;
|
|
301
|
+
|
|
251
302
|
const auto &scrollableOpts = newProps.scrollableOptions;
|
|
252
303
|
BOOL scrollingExpandsSheet = scrollableOpts.scrollingExpandsSheet;
|
|
304
|
+
BOOL contentInsetAdjustment = scrollableOpts.contentInsetAdjustmentBehavior;
|
|
253
305
|
auto topEdgeEffect = scrollableOpts.topScrollEdgeEffect;
|
|
254
306
|
auto bottomEdgeEffect = scrollableOpts.bottomScrollEdgeEffect;
|
|
255
|
-
BOOL hasScrollableOptions = scrollableOpts.keyboardScrollOffset > 0 ||
|
|
307
|
+
BOOL hasScrollableOptions = scrollableOpts.keyboardScrollOffset > 0 || scrollableOpts.keyboardOffset != 0 ||
|
|
308
|
+
!scrollingExpandsSheet || !contentInsetAdjustment ||
|
|
256
309
|
topEdgeEffect != TrueSheetViewTopScrollEdgeEffect::Hidden ||
|
|
257
310
|
bottomEdgeEffect != TrueSheetViewBottomScrollEdgeEffect::Hidden;
|
|
258
311
|
|
|
259
312
|
if (hasScrollableOptions) {
|
|
260
313
|
ScrollableOptions *options = [[ScrollableOptions alloc] init];
|
|
314
|
+
options.contentInsetAdjustment = contentInsetAdjustment;
|
|
261
315
|
options.keyboardScrollOffset = scrollableOpts.keyboardScrollOffset;
|
|
316
|
+
options.keyboardOffset = scrollableOpts.keyboardOffset;
|
|
262
317
|
options.scrollingExpandsSheet = scrollingExpandsSheet;
|
|
263
318
|
options.topScrollEdgeEffect = topEdgeEffect;
|
|
264
319
|
options.bottomScrollEdgeEffect = bottomEdgeEffect;
|
|
@@ -342,13 +397,47 @@ using namespace facebook::react;
|
|
|
342
397
|
_pendingPropsUpdate = YES;
|
|
343
398
|
} else if (_initialDetentIndex >= 0) {
|
|
344
399
|
_pendingLayoutUpdate = NO;
|
|
400
|
+
if (self.window) {
|
|
401
|
+
[self presentInitialIfNeeded];
|
|
402
|
+
}
|
|
345
403
|
}
|
|
346
404
|
}
|
|
347
405
|
|
|
348
406
|
- (void)prepareForRecycle {
|
|
349
407
|
[super prepareForRecycle];
|
|
350
408
|
|
|
351
|
-
|
|
409
|
+
// Pooled mid-dismissal: the next mount can reuse this host — and its
|
|
410
|
+
// controller — while the previous sheet is still animating out. Detach until
|
|
411
|
+
// that transition ends so its tail (didBlur, didDismiss, position) can't reach
|
|
412
|
+
// the new mount, then hand the controller back and let auto-present retry.
|
|
413
|
+
if (_controller.isBeingDismissed) {
|
|
414
|
+
_controller.delegate = nil;
|
|
415
|
+
|
|
416
|
+
__weak __typeof(self) weakSelf = self;
|
|
417
|
+
void (^reattach)(void) = ^{
|
|
418
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
419
|
+
__typeof(self) strongSelf = weakSelf;
|
|
420
|
+
if (!strongSelf)
|
|
421
|
+
return;
|
|
422
|
+
|
|
423
|
+
strongSelf->_controller.delegate = strongSelf;
|
|
424
|
+
strongSelf->_controller.activeDetentIndex = -1;
|
|
425
|
+
if (strongSelf.window) {
|
|
426
|
+
[strongSelf presentInitialIfNeeded];
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
id<UIViewControllerTransitionCoordinator> coordinator = _controller.transitionCoordinator;
|
|
432
|
+
if (coordinator) {
|
|
433
|
+
[coordinator animateAlongsideTransition:nil
|
|
434
|
+
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
435
|
+
reattach();
|
|
436
|
+
}];
|
|
437
|
+
} else {
|
|
438
|
+
reattach();
|
|
439
|
+
}
|
|
440
|
+
}
|
|
352
441
|
|
|
353
442
|
_lastStateSize = CGSizeZero;
|
|
354
443
|
_didInitiallyPresent = NO;
|
|
@@ -389,7 +478,6 @@ using namespace facebook::react;
|
|
|
389
478
|
[_touchHandler attachToView:_containerView];
|
|
390
479
|
[_controller.view addSubview:_containerView];
|
|
391
480
|
[_controller.view bringSubviewToFront:_containerView];
|
|
392
|
-
_containerView.accessibilityViewIsModal = YES;
|
|
393
481
|
_controller.accessibilityContentView = _containerView;
|
|
394
482
|
[_controller setupAccessibilityContainer];
|
|
395
483
|
|
|
@@ -403,9 +491,8 @@ using namespace facebook::react;
|
|
|
403
491
|
_controller.headerHeight = @(headerHeight);
|
|
404
492
|
}
|
|
405
493
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
_controller.footerHeight = @(footerHeight);
|
|
494
|
+
if ([_containerView footerHeight] > 0) {
|
|
495
|
+
[self syncFooterMetrics];
|
|
409
496
|
}
|
|
410
497
|
|
|
411
498
|
CGFloat peekContentHeight = [_containerView peekContentHeight];
|
|
@@ -471,7 +558,7 @@ using namespace facebook::react;
|
|
|
471
558
|
[_controller setupSheetSizing];
|
|
472
559
|
[_controller setupSheetProps];
|
|
473
560
|
|
|
474
|
-
//
|
|
561
|
+
// Detect before measuring so a first-time detection doesn't shift sizes mid-animation.
|
|
475
562
|
[self setupScrollable];
|
|
476
563
|
|
|
477
564
|
// Measure synchronously so the presentation starts at the right height —
|
|
@@ -480,24 +567,41 @@ using namespace facebook::react;
|
|
|
480
567
|
if (_containerView) {
|
|
481
568
|
_controller.contentHeight = @([_containerView contentHeight]);
|
|
482
569
|
_controller.headerHeight = @([_containerView headerHeight]);
|
|
483
|
-
|
|
570
|
+
[self syncFooterMetrics];
|
|
484
571
|
_controller.peekContentHeight = @([_containerView peekContentHeight]);
|
|
485
572
|
}
|
|
486
573
|
_pendingSizeChange = NO;
|
|
487
574
|
|
|
575
|
+
[self refreshFooterBottomInset];
|
|
488
576
|
[_controller setupSheetDetents];
|
|
489
577
|
[_controller setupActiveDetentWithIndex:index];
|
|
490
578
|
|
|
491
579
|
[_screensEventObserver capturePresenterScreenFromView:self];
|
|
492
580
|
[_screensEventObserver startObservingWithState:_state.get()->getData()];
|
|
493
581
|
|
|
582
|
+
UIWindow *window = presentingViewController.view.window;
|
|
494
583
|
[presentingViewController presentViewController:_controller
|
|
495
584
|
animated:animated
|
|
496
585
|
completion:^{
|
|
586
|
+
[TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
|
|
497
587
|
if (completion) {
|
|
498
588
|
completion(YES, nil);
|
|
499
589
|
}
|
|
500
590
|
}];
|
|
591
|
+
|
|
592
|
+
// Presenting adds the sheet's transition view on top of the window once the
|
|
593
|
+
// transition starts — re-front overlays alongside it so the first frame is
|
|
594
|
+
// already ordered (and again on completion in case the transition was queued)
|
|
595
|
+
id<UIViewControllerTransitionCoordinator> coordinator = _controller.transitionCoordinator;
|
|
596
|
+
if (coordinator) {
|
|
597
|
+
[coordinator
|
|
598
|
+
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
599
|
+
[TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
|
|
600
|
+
}
|
|
601
|
+
completion:nil];
|
|
602
|
+
} else {
|
|
603
|
+
[TrueSheetOverlayView bringOverlaysToFrontInWindow:window];
|
|
604
|
+
}
|
|
501
605
|
}
|
|
502
606
|
|
|
503
607
|
- (void)resizeToIndex:(NSInteger)index completion:(nullable TrueSheetCompletionBlock)completion {
|
|
@@ -583,6 +687,10 @@ using namespace facebook::react;
|
|
|
583
687
|
* Debounced sheet update to handle rapid content/header size changes.
|
|
584
688
|
*/
|
|
585
689
|
- (void)setupSheetDetentsForSizeChange {
|
|
690
|
+
// Keep the footer's absorbed inset in sync with the latest measured heights
|
|
691
|
+
// even before presenting, so the sheet presents at its final size.
|
|
692
|
+
[self refreshFooterBottomInset];
|
|
693
|
+
|
|
586
694
|
// Retargeting the in-flight presentation makes UIKit snap the whole stack
|
|
587
695
|
// (including the sheet behind) instead of animating. Defer to didPresent —
|
|
588
696
|
// presentAtIndex measured synchronously, so this is usually a no-op.
|
|
@@ -610,6 +718,7 @@ using namespace facebook::react;
|
|
|
610
718
|
// Refresh here (not just on peek size events) since the peek's offset
|
|
611
719
|
// within the content can change without its own size changing.
|
|
612
720
|
self->_controller.peekContentHeight = @([self->_containerView peekContentHeight]);
|
|
721
|
+
[self refreshFooterBottomInset];
|
|
613
722
|
[self->_controller setupSheetDetentsForSizeChange];
|
|
614
723
|
});
|
|
615
724
|
}
|
|
@@ -625,7 +734,7 @@ using namespace facebook::react;
|
|
|
625
734
|
}
|
|
626
735
|
|
|
627
736
|
- (void)containerViewFooterDidChangeSize:(CGSize)newSize {
|
|
628
|
-
|
|
737
|
+
[self syncFooterMetrics];
|
|
629
738
|
[self setupSheetDetentsForSizeChange];
|
|
630
739
|
[_controller setupAccessibilityContainer];
|
|
631
740
|
}
|
|
@@ -634,7 +743,7 @@ using namespace facebook::react;
|
|
|
634
743
|
[self setupSheetDetentsForSizeChange];
|
|
635
744
|
}
|
|
636
745
|
|
|
637
|
-
// When the ScrollView changes (e.g. conditional remount), re-
|
|
746
|
+
// When the ScrollView changes (e.g. conditional remount), re-detect the new ScrollView.
|
|
638
747
|
- (void)containerViewScrollViewDidChange {
|
|
639
748
|
[self setupScrollable];
|
|
640
749
|
}
|
|
@@ -687,6 +796,10 @@ using namespace facebook::react;
|
|
|
687
796
|
}
|
|
688
797
|
}
|
|
689
798
|
|
|
799
|
+
- (void)viewControllerDidAttemptDismiss {
|
|
800
|
+
[TrueSheetLifecycleEvents emitDismissAttempt:_eventEmitter];
|
|
801
|
+
}
|
|
802
|
+
|
|
690
803
|
- (void)viewControllerDidDismiss {
|
|
691
804
|
[_containerView cleanupKeyboardObserver];
|
|
692
805
|
if (!_dismissedByNavigation) {
|
|
@@ -716,6 +829,10 @@ using namespace facebook::react;
|
|
|
716
829
|
[self updateStateWithSize:size];
|
|
717
830
|
}
|
|
718
831
|
|
|
832
|
+
- (void)viewControllerSafeAreaInsetsDidChange {
|
|
833
|
+
[self refreshFooterBottomInset];
|
|
834
|
+
}
|
|
835
|
+
|
|
719
836
|
- (void)viewControllerWillFocus {
|
|
720
837
|
[TrueSheetFocusEvents emitWillFocus:_eventEmitter];
|
|
721
838
|
}
|
|
@@ -781,16 +898,40 @@ using namespace facebook::react;
|
|
|
781
898
|
if (!_containerView)
|
|
782
899
|
return;
|
|
783
900
|
|
|
784
|
-
_containerView.insetAdjustment = _insetAdjustment;
|
|
785
901
|
_containerView.scrollableOptions = _scrollableOptions;
|
|
786
|
-
_containerView.
|
|
787
|
-
_containerView.
|
|
902
|
+
_containerView.scrollableHandle = _scrollableHandle;
|
|
903
|
+
_containerView.contentInsetAdjustment = (_scrollableOptions ? _scrollableOptions.contentInsetAdjustment : YES) &&
|
|
904
|
+
_insetAdjustment == TrueSheetViewInsetAdjustment::Automatic;
|
|
905
|
+
[self refreshFooterBottomInset];
|
|
788
906
|
[_containerView setupScrollable];
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// footerHeight and appliedFooterBottomInset must stay paired — detent math
|
|
910
|
+
// subtracts exactly the inset baked into the measured footer height (see
|
|
911
|
+
// autoDetentBottomInset/peekDetentBottomInset).
|
|
912
|
+
- (void)syncFooterMetrics {
|
|
913
|
+
_controller.footerHeight = @([_containerView footerHeight]);
|
|
914
|
+
_controller.appliedFooterBottomInset = [_containerView footerAppliedBottomInset];
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
// Recomputes the inset the footer absorbs and pushes it down. The inset
|
|
918
|
+
// depends on measured heights and detents (see maxNaturalDetentHeight), so
|
|
919
|
+
// this runs on size and detent changes — not just prop updates.
|
|
920
|
+
- (void)refreshFooterBottomInset {
|
|
921
|
+
if (!_containerView)
|
|
922
|
+
return;
|
|
923
|
+
|
|
924
|
+
// appliedFooterBottomInset is NOT set here — it tracks what's actually
|
|
925
|
+
// baked into the footer's measured height (reported with its layout), not
|
|
926
|
+
// what was just pushed. Setting it at push time desyncs the pair and lets
|
|
927
|
+
// a seeded inset read as natural content height (see maxNaturalDetentHeight).
|
|
928
|
+
CGFloat inset = _controller.footerBottomInset;
|
|
929
|
+
_containerView.footerBottomInset = inset;
|
|
789
930
|
|
|
790
931
|
// Publish the inset so a footer set later (e.g. navigation setOptions) is
|
|
791
932
|
// padded on its very first layout instead of resizing the sheet twice.
|
|
792
933
|
if (_insetAdjustment == TrueSheetViewInsetAdjustment::Automatic) {
|
|
793
|
-
TrueSheetInsets::setBottomSafeArea(
|
|
934
|
+
TrueSheetInsets::setBottomSafeArea(inset);
|
|
794
935
|
}
|
|
795
936
|
}
|
|
796
937
|
|
|
@@ -801,6 +942,7 @@ using namespace facebook::react;
|
|
|
801
942
|
if (_pendingDetents) {
|
|
802
943
|
_controller.detents = _pendingDetents;
|
|
803
944
|
_pendingDetents = nil;
|
|
945
|
+
[self refreshFooterBottomInset];
|
|
804
946
|
}
|
|
805
947
|
|
|
806
948
|
UIView *presenterView = _controller.presentingViewController.view;
|
|
@@ -26,6 +26,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
26
26
|
- (void)viewControllerDidPresentAtIndex:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent;
|
|
27
27
|
- (void)viewControllerWillDismiss;
|
|
28
28
|
- (void)viewControllerDidDismiss;
|
|
29
|
+
- (void)viewControllerDidAttemptDismiss;
|
|
29
30
|
- (void)viewControllerDidChangeDetent:(NSInteger)index position:(CGFloat)position detent:(CGFloat)detent;
|
|
30
31
|
- (void)viewControllerDidDrag:(UIGestureRecognizerState)state
|
|
31
32
|
index:(NSInteger)index
|
|
@@ -36,6 +37,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
36
37
|
detent:(CGFloat)detent
|
|
37
38
|
realtime:(BOOL)realtime;
|
|
38
39
|
- (void)viewControllerDidChangeSize:(CGSize)size;
|
|
40
|
+
- (void)viewControllerSafeAreaInsetsDidChange;
|
|
39
41
|
- (void)viewControllerWillFocus;
|
|
40
42
|
- (void)viewControllerDidFocus;
|
|
41
43
|
- (void)viewControllerWillBlur;
|
|
@@ -99,6 +101,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
99
101
|
*/
|
|
100
102
|
@property (nonatomic, readonly) CGFloat footerBottomInset;
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* The inset currently baked into the footer's measured height, reported
|
|
106
|
+
* alongside its layout (see TrueSheetFooterView.appliedBottomInset). Detent
|
|
107
|
+
* resolution subtracts this exact value so the baked inset is never
|
|
108
|
+
* double-counted.
|
|
109
|
+
*/
|
|
110
|
+
@property (nonatomic, assign) CGFloat appliedFooterBottomInset;
|
|
111
|
+
|
|
102
112
|
- (void)setupAccessibilityContainer;
|
|
103
113
|
- (void)applyActiveDetent;
|
|
104
114
|
- (void)setupActiveDetentWithIndex:(NSInteger)index;
|