@lodev09/react-native-true-sheet 4.0.0-beta.3 → 4.0.0-beta.30
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 +28 -5
- package/android/build.gradle +23 -1
- package/android/proguard-rules.pro +3 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +38 -109
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +127 -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 +29 -3
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +234 -66
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +491 -186
- package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +64 -10
- package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +135 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +23 -23
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +130 -6
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +14 -17
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +148 -0
- package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +33 -40
- package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
- package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
- package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
- package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
- package/android/src/main/jni/TrueSheetSpec.h +1 -0
- package/android/src/main/jni/TrueSheetStateUpdater.cpp +37 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +114 -30
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +14 -3
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +5 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +10 -6
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -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 +33 -0
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
- package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
- package/ios/TrueSheetContainerView.h +23 -7
- package/ios/TrueSheetContainerView.mm +45 -25
- package/ios/TrueSheetContentView.h +44 -15
- package/ios/TrueSheetContentView.mm +221 -190
- package/ios/TrueSheetFooterView.mm +133 -28
- package/ios/TrueSheetModule.h +2 -7
- package/ios/TrueSheetModule.mm +18 -18
- package/ios/TrueSheetOverlayView.h +42 -0
- package/ios/TrueSheetOverlayView.mm +175 -0
- package/ios/TrueSheetView.h +2 -0
- package/ios/TrueSheetView.mm +268 -52
- package/ios/TrueSheetViewController.h +36 -3
- package/ios/TrueSheetViewController.mm +797 -102
- package/ios/core/RNScreensEventObserver.mm +31 -28
- package/ios/core/TrueSheetBlurView.h +0 -2
- package/ios/core/TrueSheetBlurView.mm +3 -36
- package/ios/core/TrueSheetDetentCalculator.h +18 -1
- package/ios/core/TrueSheetDetentCalculator.mm +44 -9
- package/ios/core/TrueSheetDimView.h +27 -0
- package/ios/core/TrueSheetDimView.mm +41 -0
- package/ios/core/TrueSheetGrabberView.mm +13 -3
- package/ios/core/TrueSheetKeyboardObserver.h +7 -0
- package/ios/core/TrueSheetKeyboardObserver.mm +107 -15
- 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 +222 -54
- package/lib/module/TrueSheet.js.map +1 -1
- package/lib/module/TrueSheet.web.js +364 -275
- package/lib/module/TrueSheet.web.js.map +1 -1
- package/lib/module/TrueSheetBackground.js +15 -0
- package/lib/module/TrueSheetBackground.js.map +1 -0
- 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 +20 -15
- package/lib/module/TrueSheetPeek.web.js.map +1 -1
- package/lib/module/TrueSheetProvider.web.js +13 -9
- package/lib/module/TrueSheetProvider.web.js.map +1 -1
- package/lib/module/detentBackgrounds.js +21 -0
- package/lib/module/detentBackgrounds.js.map +1 -0
- package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
- package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/lib/module/fabric/TrueSheetViewNativeComponent.ts +89 -73
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/mocks/index.js +18 -2
- package/lib/module/mocks/index.js.map +1 -1
- package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
- package/lib/module/navigation/base-types.js +2 -0
- package/lib/module/navigation/base-types.js.map +1 -0
- package/lib/module/navigation/createTrueSheetRouter.js +158 -100
- package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
- 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/extendTrueSheetRouter.js +71 -0
- package/lib/module/navigation/expo-router/extendTrueSheetRouter.js.map +1 -0
- package/lib/module/navigation/expo-router/index.js +18 -7
- package/lib/module/navigation/expo-router/index.js.map +1 -1
- 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.map +1 -1
- package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
- package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
- package/lib/module/scrollable.js +28 -0
- package/lib/module/scrollable.js.map +1 -0
- package/lib/module/web/constants.js +1 -1
- package/lib/module/web/constants.js.map +1 -1
- package/lib/module/web/keyboard.js +69 -0
- package/lib/module/web/keyboard.js.map +1 -0
- package/lib/module/web/layout.js +168 -0
- package/lib/module/web/layout.js.map +1 -0
- package/lib/module/web/vaul/constants.js +9 -0
- package/lib/module/web/vaul/constants.js.map +1 -1
- package/lib/module/web/vaul/context.js +4 -0
- package/lib/module/web/vaul/context.js.map +1 -1
- package/lib/module/web/vaul/helpers.js +1 -0
- package/lib/module/web/vaul/helpers.js.map +1 -1
- package/lib/module/web/vaul/index.js +83 -21
- package/lib/module/web/vaul/index.js.map +1 -1
- package/lib/module/web/vaul/use-snap-points.js +57 -28
- package/lib/module/web/vaul/use-snap-points.js.map +1 -1
- package/lib/typescript/src/TrueSheet.d.ts +18 -1
- package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheet.types.d.ts +209 -35
- 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/TrueSheetBackground.d.ts +3 -0
- package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
- 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 +8 -4
- package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
- package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
- package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
- package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
- package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -3
- package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.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 +76 -65
- 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/navigationExpoRouter.d.ts +3 -3
- package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
- 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 +36 -10
- package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
- 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/extendTrueSheetRouter.d.ts +33 -0
- package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts.map +1 -0
- package/lib/typescript/src/navigation/expo-router/index.d.ts +9 -6
- package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
- 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 +3 -4
- package/lib/typescript/src/navigation/index.d.ts.map +1 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
- package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
- package/lib/typescript/src/navigation/types.d.ts +43 -6
- package/lib/typescript/src/navigation/types.d.ts.map +1 -1
- package/lib/typescript/src/scrollable.d.ts +2 -0
- package/lib/typescript/src/scrollable.d.ts.map +1 -0
- package/lib/typescript/src/web/constants.d.ts +1 -1
- package/lib/typescript/src/web/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/keyboard.d.ts +15 -0
- package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
- package/lib/typescript/src/web/layout.d.ts +20 -0
- package/lib/typescript/src/web/layout.d.ts.map +1 -0
- package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
- package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/context.d.ts +5 -1
- package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
- package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/index.d.ts +20 -1
- package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/types.d.ts +9 -0
- package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts +6 -4
- package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
- package/package.json +18 -8
- package/react-native.config.js +1 -0
- package/src/TrueSheet.tsx +257 -62
- package/src/TrueSheet.types.ts +225 -35
- package/src/TrueSheet.web.tsx +445 -333
- package/src/TrueSheetBackground.tsx +17 -0
- package/src/TrueSheetOverlay.tsx +24 -0
- package/src/TrueSheetOverlay.web.tsx +33 -0
- package/src/TrueSheetPeek.web.tsx +25 -23
- package/src/TrueSheetProvider.web.tsx +22 -13
- package/src/detentBackgrounds.ts +24 -0
- package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
- package/src/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
- package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
- package/src/fabric/TrueSheetViewNativeComponent.ts +89 -73
- package/src/index.ts +1 -0
- package/src/mocks/index.ts +17 -2
- package/src/mocks/navigationExpoRouter.ts +6 -5
- package/src/navigation/base-types.ts +20 -0
- package/src/navigation/createTrueSheetRouter.ts +220 -144
- package/src/navigation/expo-router/base-types.ts +18 -0
- package/src/navigation/expo-router/extendTrueSheetRouter.ts +99 -0
- package/src/navigation/expo-router/index.ts +41 -30
- package/src/navigation/expo-router/types.ts +116 -0
- package/src/navigation/index.ts +6 -7
- package/src/navigation/screen/useSheetScreenState.ts +2 -0
- package/src/navigation/types.ts +81 -23
- package/src/scrollable.ts +25 -0
- package/src/web/constants.ts +1 -1
- package/src/web/keyboard.ts +79 -0
- package/src/web/layout.ts +196 -0
- package/src/web/vaul/constants.ts +9 -0
- package/src/web/vaul/context.ts +7 -1
- package/src/web/vaul/helpers.ts +5 -0
- package/src/web/vaul/index.tsx +113 -16
- package/src/web/vaul/types.ts +10 -0
- package/src/web/vaul/use-snap-points.ts +86 -33
|
@@ -22,8 +22,7 @@ using namespace facebook::react;
|
|
|
22
22
|
NSInteger _presenterScreenTag;
|
|
23
23
|
NSMutableSet<NSNumber *> *_screenTags;
|
|
24
24
|
__weak UIViewController *_presenterScreenController;
|
|
25
|
-
|
|
26
|
-
__weak UIWindow *_window;
|
|
25
|
+
NSHashTable<UIViewController *> *_parentScreenControllers;
|
|
27
26
|
BOOL _dismissedByNavigation;
|
|
28
27
|
BOOL _pendingInteractiveDismiss;
|
|
29
28
|
BOOL _pendingInteractiveAppear;
|
|
@@ -39,8 +38,7 @@ using namespace facebook::react;
|
|
|
39
38
|
_presenterScreenTag = 0;
|
|
40
39
|
_screenTags = [NSMutableSet new];
|
|
41
40
|
_presenterScreenController = nil;
|
|
42
|
-
|
|
43
|
-
_window = nil;
|
|
41
|
+
_parentScreenControllers = [NSHashTable weakObjectsHashTable];
|
|
44
42
|
}
|
|
45
43
|
return self;
|
|
46
44
|
}
|
|
@@ -143,8 +141,7 @@ using namespace facebook::react;
|
|
|
143
141
|
_presenterScreenTag = 0;
|
|
144
142
|
[_screenTags removeAllObjects];
|
|
145
143
|
_presenterScreenController = nil;
|
|
146
|
-
|
|
147
|
-
_window = view.window;
|
|
144
|
+
[_parentScreenControllers removeAllObjects];
|
|
148
145
|
|
|
149
146
|
Class screenViewClass = NSClassFromString(@"RNSScreenView");
|
|
150
147
|
if (!screenViewClass)
|
|
@@ -165,8 +162,8 @@ using namespace facebook::react;
|
|
|
165
162
|
if (!_presenterScreenController) {
|
|
166
163
|
_presenterScreenTag = current.tag;
|
|
167
164
|
_presenterScreenController = screenVC;
|
|
168
|
-
} else if (
|
|
169
|
-
|
|
165
|
+
} else if (screenVC) {
|
|
166
|
+
[_parentScreenControllers addObject:screenVC];
|
|
170
167
|
}
|
|
171
168
|
}
|
|
172
169
|
}
|
|
@@ -239,27 +236,16 @@ using namespace facebook::react;
|
|
|
239
236
|
}
|
|
240
237
|
|
|
241
238
|
- (BOOL)shouldDismissForScreenTag:(NSInteger)screenTag {
|
|
242
|
-
// For parent screens
|
|
243
|
-
//
|
|
239
|
+
// For parent screens, dismiss if one was popped or covered in its own stack. The presenter
|
|
240
|
+
// may already be detached (e.g. its nested stack unmounting), so it can't be checked here.
|
|
241
|
+
// Skip if all are still top (e.g. modal dismiss - sheet dismisses naturally)
|
|
244
242
|
if (screenTag != _presenterScreenTag) {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
return NO;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (!_parentScreenController) {
|
|
254
|
-
return NO;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// If presenter view is no longer in window, the nested stack is being removed
|
|
258
|
-
UIView *presenterView = [_window viewWithTag:_presenterScreenTag];
|
|
259
|
-
if (presenterView == nil || presenterView.window == nil) {
|
|
260
|
-
return YES;
|
|
243
|
+
for (UIViewController *screen in _parentScreenControllers) {
|
|
244
|
+
UINavigationController *nav = screen.navigationController;
|
|
245
|
+
if (nav && nav.topViewController != screen) {
|
|
246
|
+
return YES;
|
|
247
|
+
}
|
|
261
248
|
}
|
|
262
|
-
|
|
263
249
|
return NO;
|
|
264
250
|
}
|
|
265
251
|
|
|
@@ -272,7 +258,24 @@ using namespace facebook::react;
|
|
|
272
258
|
|
|
273
259
|
// Dismiss if presenter is no longer top of nav stack (pushed/popped)
|
|
274
260
|
// Skip if still top (e.g. modal dismiss - sheet dismisses naturally)
|
|
275
|
-
|
|
261
|
+
if (navController.topViewController != _presenterScreenController) {
|
|
262
|
+
return YES;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// A presenter inside a screen container (e.g. a bottom tab) stays the top of
|
|
266
|
+
// that container while an enclosing stack pushes a screen over it.
|
|
267
|
+
for (UINavigationController *outerNav = navController.navigationController; outerNav;
|
|
268
|
+
outerNav = outerNav.navigationController) {
|
|
269
|
+
UIViewController *ancestor = _presenterScreenController;
|
|
270
|
+
while (ancestor && ancestor != outerNav.topViewController) {
|
|
271
|
+
ancestor = ancestor.parentViewController;
|
|
272
|
+
}
|
|
273
|
+
if (!ancestor) {
|
|
274
|
+
return YES;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
return NO;
|
|
276
279
|
}
|
|
277
280
|
|
|
278
281
|
@end
|
|
@@ -14,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
14
14
|
@interface TrueSheetBlurView : UIVisualEffectView
|
|
15
15
|
|
|
16
16
|
@property (nonatomic, assign) facebook::react::TrueSheetViewBackgroundBlur backgroundBlur;
|
|
17
|
-
@property (nonatomic, strong, nullable) NSNumber *blurIntensity;
|
|
18
|
-
@property (nonatomic, assign) BOOL blurInteraction;
|
|
19
17
|
|
|
20
18
|
/// Adds the blur view to a parent view with proper constraints
|
|
21
19
|
- (void)addToView:(UIView *)parentView;
|
|
@@ -13,15 +13,13 @@
|
|
|
13
13
|
|
|
14
14
|
using namespace facebook::react;
|
|
15
15
|
|
|
16
|
-
@implementation TrueSheetBlurView
|
|
17
|
-
UIViewPropertyAnimator *_blurAnimator;
|
|
18
|
-
}
|
|
16
|
+
@implementation TrueSheetBlurView
|
|
19
17
|
|
|
20
18
|
#pragma mark - Initialization
|
|
21
19
|
|
|
22
20
|
- (instancetype)init {
|
|
23
21
|
if (self = [super init]) {
|
|
24
|
-
|
|
22
|
+
self.userInteractionEnabled = NO;
|
|
25
23
|
}
|
|
26
24
|
return self;
|
|
27
25
|
}
|
|
@@ -38,45 +36,14 @@ using namespace facebook::react;
|
|
|
38
36
|
[parentView insertSubview:self atIndex:0];
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
- (void)clearAnimator {
|
|
42
|
-
if (_blurAnimator) {
|
|
43
|
-
[_blurAnimator stopAnimation:YES];
|
|
44
|
-
_blurAnimator = nil;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
39
|
- (void)applyBlurEffect {
|
|
49
|
-
self.userInteractionEnabled = self.blurInteraction;
|
|
50
|
-
|
|
51
40
|
if (self.backgroundBlur == TrueSheetViewBackgroundBlur::None) {
|
|
52
|
-
[self clearAnimator];
|
|
53
41
|
self.effect = nil;
|
|
54
42
|
return;
|
|
55
43
|
}
|
|
56
44
|
|
|
57
45
|
UIBlurEffectStyle style = [BlurUtil blurEffectStyleFromEnum:self.backgroundBlur];
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
CGFloat intensity =
|
|
61
|
-
(self.blurIntensity && [self.blurIntensity floatValue] >= 0) ? [self.blurIntensity floatValue] / 100.0 : 1.0;
|
|
62
|
-
|
|
63
|
-
if (intensity >= 1.0) {
|
|
64
|
-
[self clearAnimator];
|
|
65
|
-
self.effect = blurEffect;
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (!_blurAnimator) {
|
|
70
|
-
__weak __typeof(self) weakSelf = self;
|
|
71
|
-
_blurAnimator = [[UIViewPropertyAnimator alloc] initWithDuration:1.0
|
|
72
|
-
curve:UIViewAnimationCurveLinear
|
|
73
|
-
animations:^{
|
|
74
|
-
weakSelf.effect = blurEffect;
|
|
75
|
-
}];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_blurAnimator.pausesOnCompletion = YES;
|
|
79
|
-
_blurAnimator.fractionComplete = intensity;
|
|
46
|
+
self.effect = [UIBlurEffect effectWithStyle:style];
|
|
80
47
|
}
|
|
81
48
|
|
|
82
49
|
@end
|
|
@@ -24,6 +24,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
24
24
|
@property (nonatomic, readonly) BOOL absoluteHeader;
|
|
25
25
|
@property (nonatomic, strong, readonly, nullable) NSNumber *footerHeight;
|
|
26
26
|
@property (nonatomic, readonly) BOOL absoluteFooter;
|
|
27
|
+
@property (nonatomic, readonly) BOOL footerInsetAdjustment;
|
|
28
|
+
@property (nonatomic, readonly) BOOL footerAvoidsKeyboard;
|
|
29
|
+
@property (nonatomic, readonly) CGFloat appliedFooterBottomInset;
|
|
27
30
|
@property (nonatomic, strong, readonly, nullable) NSNumber *peekContentHeight;
|
|
28
31
|
|
|
29
32
|
@end
|
|
@@ -43,12 +46,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
43
46
|
For peek (-2) detents, calculates based on header + footer height.
|
|
44
47
|
*/
|
|
45
48
|
- (CGFloat)detentValueForIndex:(NSInteger)index;
|
|
49
|
+
- (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex;
|
|
46
50
|
|
|
47
51
|
/**
|
|
48
52
|
Returns the height for auto (-1) detents: content + header + footer height.
|
|
49
53
|
An absolute (floating) header or footer contributes no height.
|
|
54
|
+
A pinned footer contributes only its overlap above the keyboard. UIKit adds
|
|
55
|
+
the keyboard expansion separately. Pass 0 for the keyboard-free height.
|
|
50
56
|
*/
|
|
51
|
-
- (CGFloat)
|
|
57
|
+
- (CGFloat)autoHeightForKeyboardHeight:(CGFloat)keyboardHeight;
|
|
52
58
|
|
|
53
59
|
/**
|
|
54
60
|
Returns the height for peek (-2) detents: header + footer + peek content height.
|
|
@@ -70,6 +76,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
70
76
|
*/
|
|
71
77
|
- (CGFloat)resolvedHeightForIndex:(NSInteger)index;
|
|
72
78
|
|
|
79
|
+
/**
|
|
80
|
+
Returns the offset learned for this detent itself, or 0 when none is learned.
|
|
81
|
+
*/
|
|
82
|
+
- (CGFloat)learnedOffsetForIndex:(NSInteger)index;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
Sets the offset this detent is expected to rest with, unless its learned
|
|
86
|
+
offset is already within a point of it.
|
|
87
|
+
*/
|
|
88
|
+
- (void)assumeOffset:(CGFloat)offset forIndex:(NSInteger)index;
|
|
89
|
+
|
|
73
90
|
/**
|
|
74
91
|
Finds the segment between detents for a given position.
|
|
75
92
|
Returns YES if position is between two detents, NO if at edges.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
if (index >= 0 && index < (NSInteger)detents.count) {
|
|
20
20
|
CGFloat value = [detents[index] doubleValue];
|
|
21
21
|
if (value == -1) {
|
|
22
|
-
return [self
|
|
22
|
+
return [self autoHeightForKeyboardHeight:0] / self.delegate.screenHeight;
|
|
23
23
|
}
|
|
24
24
|
if (value == -2) {
|
|
25
25
|
return [self peekHeight] / self.delegate.screenHeight;
|
|
@@ -29,10 +29,15 @@
|
|
|
29
29
|
return 0;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
- (CGFloat)
|
|
33
|
-
// An absolute (floating) header or footer overlaps the content, so it contributes no height
|
|
32
|
+
- (CGFloat)autoHeightForKeyboardHeight:(CGFloat)keyboardHeight {
|
|
33
|
+
// An absolute (floating) header or footer overlaps the content, so it contributes no height —
|
|
34
|
+
// unless the footer pads the scrollable, then the content ends above it like a relative footer
|
|
34
35
|
CGFloat headerHeight = self.delegate.absoluteHeader ? 0 : [self.delegate.headerHeight floatValue];
|
|
35
|
-
|
|
36
|
+
BOOL floatingFooter = self.delegate.absoluteFooter && !self.delegate.footerInsetAdjustment;
|
|
37
|
+
CGFloat footerHeight = floatingFooter ? 0 : [self.delegate.footerHeight floatValue];
|
|
38
|
+
if (self.delegate.absoluteFooter && !floatingFooter && !self.delegate.footerAvoidsKeyboard) {
|
|
39
|
+
footerHeight -= MIN(keyboardHeight, MAX(0, footerHeight - self.delegate.appliedFooterBottomInset));
|
|
40
|
+
}
|
|
36
41
|
return [self.delegate.contentHeight floatValue] + headerHeight + footerHeight;
|
|
37
42
|
}
|
|
38
43
|
|
|
@@ -75,13 +80,25 @@
|
|
|
75
80
|
return detentValue * self.delegate.screenHeight;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
- (CGFloat)
|
|
79
|
-
// Use this detent's own offset if available
|
|
83
|
+
- (CGFloat)learnedOffsetForIndex:(NSInteger)index {
|
|
80
84
|
if (index >= 0 && index < (NSInteger)_resolvedDetentOffsets.count) {
|
|
81
|
-
|
|
82
|
-
if (offset != 0)
|
|
83
|
-
return offset;
|
|
85
|
+
return [_resolvedDetentOffsets[index] doubleValue];
|
|
84
86
|
}
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
- (void)assumeOffset:(CGFloat)offset forIndex:(NSInteger)index {
|
|
91
|
+
if (index >= 0 && index < (NSInteger)_resolvedDetentOffsets.count &&
|
|
92
|
+
fabs([_resolvedDetentOffsets[index] doubleValue] - offset) > 1) {
|
|
93
|
+
_resolvedDetentOffsets[index] = @(offset);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
- (CGFloat)offsetForIndex:(NSInteger)index {
|
|
98
|
+
// Use this detent's own offset if available
|
|
99
|
+
CGFloat learnedOffset = [self learnedOffsetForIndex:index];
|
|
100
|
+
if (learnedOffset != 0)
|
|
101
|
+
return learnedOffset;
|
|
85
102
|
|
|
86
103
|
// Fall back to any known offset
|
|
87
104
|
for (NSInteger i = 0; i < (NSInteger)_resolvedDetentOffsets.count; i++) {
|
|
@@ -93,6 +110,24 @@
|
|
|
93
110
|
return 0;
|
|
94
111
|
}
|
|
95
112
|
|
|
113
|
+
- (NSInteger)backgroundIndexForPosition:(CGFloat)position displayedIndex:(NSInteger)displayedIndex {
|
|
114
|
+
NSInteger count = self.delegate.detents.count;
|
|
115
|
+
if (count == 0)
|
|
116
|
+
return -1;
|
|
117
|
+
|
|
118
|
+
CGFloat height = self.delegate.screenHeight - position;
|
|
119
|
+
NSInteger nextIndex = MAX(0, MIN(displayedIndex, count - 1));
|
|
120
|
+
CGFloat distance = fabs(height - [self resolvedHeightForIndex:nextIndex]);
|
|
121
|
+
for (NSInteger index = 0; index < count; index++) {
|
|
122
|
+
CGFloat nextDistance = fabs(height - [self resolvedHeightForIndex:index]);
|
|
123
|
+
if (nextDistance < distance) {
|
|
124
|
+
nextIndex = index;
|
|
125
|
+
distance = nextDistance;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return nextIndex;
|
|
129
|
+
}
|
|
130
|
+
|
|
96
131
|
- (BOOL)findSegmentForPosition:(CGFloat)position outIndex:(NSInteger *)outIndex outProgress:(CGFloat *)outProgress {
|
|
97
132
|
NSArray<NSNumber *> *detents = self.delegate.detents;
|
|
98
133
|
NSInteger count = detents.count;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Jovanni Lo (@lodev09)
|
|
3
|
+
// Copyright (c) 2024-present. All rights reserved.
|
|
4
|
+
//
|
|
5
|
+
// This source code is licensed under the MIT license found in the
|
|
6
|
+
// LICENSE file in the root directory of this source tree.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import <UIKit/UIKit.h>
|
|
10
|
+
|
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Dim behind the sheet for a custom color or opacity, which
|
|
15
|
+
* UISheetPresentationController has no API for. Starts hidden.
|
|
16
|
+
*/
|
|
17
|
+
@interface TrueSheetDimView : UIView
|
|
18
|
+
|
|
19
|
+
/// Called when the dim is tapped
|
|
20
|
+
@property (nonatomic, copy, nullable) void (^onTap)(void);
|
|
21
|
+
|
|
22
|
+
/// Adds the dim view behind everything in the presentation container
|
|
23
|
+
- (void)addToView:(UIView *)parentView;
|
|
24
|
+
|
|
25
|
+
@end
|
|
26
|
+
|
|
27
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Created by Jovanni Lo (@lodev09)
|
|
3
|
+
// Copyright (c) 2024-present. All rights reserved.
|
|
4
|
+
//
|
|
5
|
+
// This source code is licensed under the MIT license found in the
|
|
6
|
+
// LICENSE file in the root directory of this source tree.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#import "TrueSheetDimView.h"
|
|
10
|
+
|
|
11
|
+
@implementation TrueSheetDimView
|
|
12
|
+
|
|
13
|
+
#pragma mark - Initialization
|
|
14
|
+
|
|
15
|
+
- (instancetype)init {
|
|
16
|
+
if (self = [super init]) {
|
|
17
|
+
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
18
|
+
self.alpha = 0;
|
|
19
|
+
|
|
20
|
+
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap)];
|
|
21
|
+
[self addGestureRecognizer:tap];
|
|
22
|
+
}
|
|
23
|
+
return self;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#pragma mark - Public
|
|
27
|
+
|
|
28
|
+
- (void)addToView:(UIView *)parentView {
|
|
29
|
+
self.frame = parentView.bounds;
|
|
30
|
+
[parentView insertSubview:self atIndex:0];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#pragma mark - Actions
|
|
34
|
+
|
|
35
|
+
- (void)handleTap {
|
|
36
|
+
if (_onTap) {
|
|
37
|
+
_onTap();
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@end
|
|
@@ -91,6 +91,13 @@ static const CGFloat kHitPaddingVertical = 10.0;
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
// Pad the pill evenly like the system grabber. The view frame itself stays inside
|
|
95
|
+
// the sheet, since the reveal check reads the frame rather than this rect.
|
|
96
|
+
- (CGRect)accessibilityFrame {
|
|
97
|
+
CGRect rect = CGRectInset(_vibrancyView.frame, -kHitPaddingHorizontal, -kHitPaddingVertical);
|
|
98
|
+
return UIAccessibilityConvertFrameToScreenCoordinates(rect, self);
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
- (void)accessibilityIncrement {
|
|
95
102
|
if (_onIncrement) {
|
|
96
103
|
_onIncrement();
|
|
@@ -144,14 +151,17 @@ static const CGFloat kHitPaddingVertical = 10.0;
|
|
|
144
151
|
CGFloat topMargin = [self effectiveTopMargin];
|
|
145
152
|
CGFloat parentWidth = self.superview ? self.superview.bounds.size.width : UIScreen.mainScreen.bounds.size.width;
|
|
146
153
|
|
|
154
|
+
// Keep the frame inside the sheet. A frame above the top edge reads as a clipped element
|
|
155
|
+
// to VoiceOver, and the sheet reveals it on focus by moving to the next detent.
|
|
147
156
|
CGFloat frameWidth = pillWidth + kHitPaddingHorizontal * 2;
|
|
148
|
-
CGFloat
|
|
149
|
-
CGFloat
|
|
157
|
+
CGFloat frameY = MAX(0, topMargin - kHitPaddingVertical);
|
|
158
|
+
CGFloat pillY = topMargin - frameY;
|
|
159
|
+
CGFloat frameHeight = pillY + pillHeight + kHitPaddingVertical;
|
|
150
160
|
|
|
151
161
|
self.frame = CGRectMake((parentWidth - frameWidth) / 2.0, frameY, frameWidth, frameHeight);
|
|
152
162
|
self.backgroundColor = UIColor.clearColor;
|
|
153
163
|
|
|
154
|
-
CGRect pillRect = CGRectMake(kHitPaddingHorizontal,
|
|
164
|
+
CGRect pillRect = CGRectMake(kHitPaddingHorizontal, pillY, pillWidth, pillHeight);
|
|
155
165
|
_vibrancyView.frame = pillRect;
|
|
156
166
|
_vibrancyView.layer.cornerRadius = [self effectiveCornerRadius];
|
|
157
167
|
_vibrancyView.clipsToBounds = YES;
|
|
@@ -26,6 +26,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
26
26
|
@property (nonatomic, weak, nullable) TrueSheetViewController *viewController;
|
|
27
27
|
@property (nonatomic, readonly) CGFloat currentHeight;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* What's left of the keyboard's last show/hide animation, so a change landing
|
|
31
|
+
* outside it (e.g. after a React commit) can finish on the same curve.
|
|
32
|
+
*/
|
|
33
|
+
@property (nonatomic, readonly) NSTimeInterval remainingAnimationDuration;
|
|
34
|
+
@property (nonatomic, readonly) UIViewAnimationOptions animationCurve;
|
|
35
|
+
|
|
29
36
|
- (void)addDelegate:(id<TrueSheetKeyboardObserverDelegate>)delegate;
|
|
30
37
|
- (void)removeDelegate:(id<TrueSheetKeyboardObserverDelegate>)delegate;
|
|
31
38
|
|
|
@@ -12,15 +12,27 @@
|
|
|
12
12
|
#import "../TrueSheetViewController.h"
|
|
13
13
|
#import "../utils/UIView+FirstResponder.h"
|
|
14
14
|
|
|
15
|
+
// Between the minimized keyboard (up to ~180pt) and the shortest docked iPad
|
|
16
|
+
// keyboard (~340pt)
|
|
17
|
+
static const CGFloat kMinimizedKeyboardMaxHeight = 250.0;
|
|
18
|
+
|
|
19
|
+
static const NSTimeInterval kUnanimatedKeyboardDuration = 0.25;
|
|
20
|
+
|
|
15
21
|
@implementation TrueSheetKeyboardObserver {
|
|
16
22
|
NSHashTable<id<TrueSheetKeyboardObserverDelegate>> *_delegates;
|
|
17
23
|
CGFloat _currentHeight;
|
|
24
|
+
CGFloat _pendingMinimizedKeyboardOverlap;
|
|
25
|
+
CFTimeInterval _animationEndTime;
|
|
18
26
|
}
|
|
19
27
|
|
|
20
28
|
- (CGFloat)currentHeight {
|
|
21
29
|
return _currentHeight;
|
|
22
30
|
}
|
|
23
31
|
|
|
32
|
+
- (NSTimeInterval)remainingAnimationDuration {
|
|
33
|
+
return MAX(0, _animationEndTime - CACurrentMediaTime());
|
|
34
|
+
}
|
|
35
|
+
|
|
24
36
|
- (instancetype)init {
|
|
25
37
|
if (self = [super init]) {
|
|
26
38
|
_delegates = [NSHashTable weakObjectsHashTable];
|
|
@@ -37,14 +49,20 @@
|
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
- (void)start {
|
|
40
|
-
[
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
53
|
+
[center addObserver:self
|
|
54
|
+
selector:@selector(keyboardWillChangeFrame:)
|
|
55
|
+
name:UIKeyboardWillChangeFrameNotification
|
|
56
|
+
object:nil];
|
|
57
|
+
[center addObserver:self selector:@selector(keyboardWillShow:) name:UIKeyboardWillShowNotification object:nil];
|
|
58
|
+
[center addObserver:self selector:@selector(keyboardWillHide:) name:UIKeyboardWillHideNotification object:nil];
|
|
44
59
|
}
|
|
45
60
|
|
|
46
61
|
- (void)stop {
|
|
47
|
-
|
|
62
|
+
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
|
|
63
|
+
[center removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
|
|
64
|
+
[center removeObserver:self name:UIKeyboardWillShowNotification object:nil];
|
|
65
|
+
[center removeObserver:self name:UIKeyboardWillHideNotification object:nil];
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
- (void)dealloc {
|
|
@@ -59,15 +77,21 @@
|
|
|
59
77
|
return firstResponder != nil;
|
|
60
78
|
}
|
|
61
79
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
80
|
+
// iPadOS 26 reports its minimized keyboard (the pill left after hiding the
|
|
81
|
+
// keyboard while the input keeps focus) as a short docked keyboard. An input
|
|
82
|
+
// accessory view does stay docked across the bottom edge, so it still counts.
|
|
83
|
+
- (BOOL)isMinimizedKeyboardFrame:(CGRect)frame {
|
|
84
|
+
if (@available(iOS 26.0, *)) {
|
|
85
|
+
if (UIDevice.currentDevice.userInterfaceIdiom != UIUserInterfaceIdiomPad) {
|
|
86
|
+
return NO;
|
|
87
|
+
}
|
|
88
|
+
UIView *firstResponder = [_viewController.view findFirstResponder];
|
|
89
|
+
return frame.size.height < kMinimizedKeyboardMaxHeight && !firstResponder.inputAccessoryView;
|
|
69
90
|
}
|
|
91
|
+
return NO;
|
|
92
|
+
}
|
|
70
93
|
|
|
94
|
+
- (void)keyboardWillChangeFrame:(NSNotification *)notification {
|
|
71
95
|
NSDictionary *userInfo = notification.userInfo;
|
|
72
96
|
CGRect keyboardFrame = [userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
|
73
97
|
NSTimeInterval duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] doubleValue];
|
|
@@ -79,13 +103,63 @@
|
|
|
79
103
|
}
|
|
80
104
|
|
|
81
105
|
CGRect keyboardFrameInWindow = [window convertRect:keyboardFrame fromWindow:nil];
|
|
82
|
-
|
|
106
|
+
// A floating iPad keyboard is narrower than the window: it covers no edge and
|
|
107
|
+
// UIKit doesn't grow the sheet for it, so it counts as hidden. Width only — a
|
|
108
|
+
// windowed app gets docked frames offset by the window's inset.
|
|
109
|
+
BOOL floating = CGRectGetWidth(keyboardFrameInWindow) < CGRectGetWidth(window.bounds) - 1;
|
|
110
|
+
CGFloat keyboardHeight = floating ? 0 : MAX(0, window.bounds.size.height - keyboardFrameInWindow.origin.y);
|
|
111
|
+
CGFloat minimizedKeyboardOverlap = 0;
|
|
112
|
+
|
|
113
|
+
// Not gated on being topmost: UIKit restores (and an app may blur) a sheet's
|
|
114
|
+
// input while a stacked child is still dismissing over it. A hide reaches
|
|
115
|
+
// every sheet that rose, even one that no longer holds the first responder.
|
|
116
|
+
if (keyboardHeight > 0) {
|
|
117
|
+
if (![self isFirstResponderWithinSheet]) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// The minimized keyboard floats in a corner and covers nothing — treat it
|
|
121
|
+
// as hidden, and offset the growth UIKit still applies for it.
|
|
122
|
+
if ([self isMinimizedKeyboardFrame:keyboardFrameInWindow]) {
|
|
123
|
+
UIView *sheetView = _viewController.view;
|
|
124
|
+
CGFloat sheetBottom = CGRectGetMaxY([sheetView convertRect:sheetView.bounds toView:nil]);
|
|
125
|
+
minimizedKeyboardOverlap =
|
|
126
|
+
MAX(0, MIN(CGRectGetMaxY(keyboardFrameInWindow), sheetBottom) - CGRectGetMinY(keyboardFrameInWindow));
|
|
127
|
+
keyboardHeight = 0;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Hiding a full keyboard ends either minimized or fully hidden, and UIKit
|
|
132
|
+
// only tells which (show or hide) after it has laid out the sheet for it.
|
|
133
|
+
_pendingMinimizedKeyboardOverlap = 0;
|
|
134
|
+
if (minimizedKeyboardOverlap > 0 && _currentHeight > 0) {
|
|
135
|
+
_pendingMinimizedKeyboardOverlap = minimizedKeyboardOverlap;
|
|
136
|
+
minimizedKeyboardOverlap = 0;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
CGFloat previousOverlap = _viewController.minimizedKeyboardOverlap;
|
|
140
|
+
_viewController.minimizedKeyboardOverlap = minimizedKeyboardOverlap;
|
|
141
|
+
|
|
142
|
+
if (keyboardHeight == 0 && _currentHeight == 0 && minimizedKeyboardOverlap == previousOverlap) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Docking or undocking the floating keyboard comes unanimated — animate the
|
|
147
|
+
// footer and scrollable insets ourselves instead of snapping them.
|
|
148
|
+
if (duration == 0 && (keyboardHeight > 0) != (_currentHeight > 0)) {
|
|
149
|
+
duration = kUnanimatedKeyboardDuration;
|
|
150
|
+
}
|
|
83
151
|
|
|
84
152
|
_currentHeight = keyboardHeight;
|
|
153
|
+
_animationEndTime = CACurrentMediaTime() + duration;
|
|
154
|
+
_animationCurve = curve;
|
|
85
155
|
|
|
86
|
-
|
|
156
|
+
// UIKit also moves the sheet as the minimized keyboard comes and goes, by
|
|
157
|
+
// whatever the detents couldn't absorb — settle again once it's done.
|
|
158
|
+
if (keyboardHeight > 0 || minimizedKeyboardOverlap != previousOverlap) {
|
|
87
159
|
_viewController.keyboardSheetGrown = YES;
|
|
88
|
-
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (keyboardHeight == 0) {
|
|
89
163
|
// The sheet stays grown until UIKit finishes the shrink-back animation —
|
|
90
164
|
// clear after it completes, unless the keyboard came back in the meantime.
|
|
91
165
|
__weak __typeof(self) weakSelf = self;
|
|
@@ -98,6 +172,8 @@
|
|
|
98
172
|
});
|
|
99
173
|
}
|
|
100
174
|
|
|
175
|
+
_viewController.keyboardHeight = keyboardHeight;
|
|
176
|
+
|
|
101
177
|
for (id<TrueSheetKeyboardObserverDelegate> delegate in _delegates) {
|
|
102
178
|
if (keyboardHeight > 0) {
|
|
103
179
|
[delegate keyboardWillShow:keyboardHeight duration:duration curve:curve];
|
|
@@ -107,6 +183,22 @@
|
|
|
107
183
|
}
|
|
108
184
|
}
|
|
109
185
|
|
|
186
|
+
// Shown minimized: UIKit has grown the sheet for it already, take it back.
|
|
187
|
+
- (void)keyboardWillShow:(NSNotification *)notification {
|
|
188
|
+
if (_pendingMinimizedKeyboardOverlap == 0) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
_viewController.minimizedKeyboardOverlap = _pendingMinimizedKeyboardOverlap;
|
|
193
|
+
_pendingMinimizedKeyboardOverlap = 0;
|
|
194
|
+
_viewController.keyboardSheetGrown = YES;
|
|
195
|
+
[_viewController invalidateDetents];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
- (void)keyboardWillHide:(NSNotification *)notification {
|
|
199
|
+
_pendingMinimizedKeyboardOverlap = 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
110
202
|
@end
|
|
111
203
|
|
|
112
204
|
#endif
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
|
|
13
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
14
|
+
|
|
15
|
+
@protocol TrueSheetOverlayContainerViewDelegate <NSObject>
|
|
16
|
+
- (void)overlayContainerViewDidLayout:(CGSize)size;
|
|
17
|
+
@end
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Window-level view hosting an overlay's children.
|
|
21
|
+
* Touches that miss every child fall through to the views beneath.
|
|
22
|
+
*/
|
|
23
|
+
@interface TrueSheetOverlayContainerView : UIView
|
|
24
|
+
|
|
25
|
+
@property (nonatomic, weak, nullable) id<TrueSheetOverlayContainerViewDelegate> delegate;
|
|
26
|
+
|
|
27
|
+
@end
|
|
28
|
+
|
|
29
|
+
NS_ASSUME_NONNULL_END
|
|
30
|
+
|
|
31
|
+
#endif
|
|
@@ -0,0 +1,35 @@
|
|
|
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 "TrueSheetOverlayContainerView.h"
|
|
12
|
+
|
|
13
|
+
@implementation TrueSheetOverlayContainerView
|
|
14
|
+
|
|
15
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
16
|
+
if (self = [super initWithFrame:frame]) {
|
|
17
|
+
self.backgroundColor = UIColor.clearColor;
|
|
18
|
+
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
19
|
+
}
|
|
20
|
+
return self;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
- (nullable UIView *)hitTest:(CGPoint)point withEvent:(nullable UIEvent *)event {
|
|
24
|
+
UIView *hit = [super hitTest:point withEvent:event];
|
|
25
|
+
return hit == self ? nil : hit;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (void)layoutSubviews {
|
|
29
|
+
[super layoutSubviews];
|
|
30
|
+
[self.delegate overlayContainerViewDidLayout:self.bounds.size];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@end
|
|
34
|
+
|
|
35
|
+
#endif
|