@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.
Files changed (244) hide show
  1. package/README.md +28 -5
  2. package/android/build.gradle +23 -1
  3. package/android/proguard-rules.pro +3 -0
  4. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContainerView.kt +50 -16
  5. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentView.kt +303 -184
  6. package/android/src/main/java/com/lodev09/truesheet/TrueSheetContentViewManager.kt +5 -1
  7. package/android/src/main/java/com/lodev09/truesheet/TrueSheetFooterView.kt +38 -109
  8. package/android/src/main/java/com/lodev09/truesheet/TrueSheetModule.kt +13 -0
  9. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayView.kt +127 -0
  10. package/android/src/main/java/com/lodev09/truesheet/TrueSheetOverlayViewManager.kt +45 -0
  11. package/android/src/main/java/com/lodev09/truesheet/TrueSheetPackage.kt +2 -1
  12. package/android/src/main/java/com/lodev09/truesheet/TrueSheetStateUpdater.kt +29 -3
  13. package/android/src/main/java/com/lodev09/truesheet/TrueSheetView.kt +234 -66
  14. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewController.kt +491 -186
  15. package/android/src/main/java/com/lodev09/truesheet/TrueSheetViewManager.kt +64 -10
  16. package/android/src/main/java/com/lodev09/truesheet/core/RNScreensEventObserver.kt +31 -14
  17. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetBehavior.kt +135 -0
  18. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetBottomSheetView.kt +23 -23
  19. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetCoordinatorLayout.kt +130 -6
  20. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDetentCalculator.kt +104 -39
  21. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetDimView.kt +14 -17
  22. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetGrabberView.kt +2 -4
  23. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetOverlayRootView.kt +148 -0
  24. package/android/src/main/java/com/lodev09/truesheet/core/TrueSheetStackManager.kt +33 -40
  25. package/android/src/main/java/com/lodev09/truesheet/events/TrueSheetLifecycleEvents.kt +15 -0
  26. package/android/src/main/java/com/lodev09/truesheet/utils/ColorUtils.kt +19 -0
  27. package/android/src/main/java/com/lodev09/truesheet/utils/KeyboardUtils.kt +15 -2
  28. package/android/src/main/java/com/lodev09/truesheet/utils/ScreenUtils.kt +0 -12
  29. package/android/src/main/java/com/lodev09/truesheet/utils/ViewUtils.kt +19 -7
  30. package/android/src/main/jni/TrueSheetSpec.h +1 -0
  31. package/android/src/main/jni/TrueSheetStateUpdater.cpp +37 -6
  32. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewComponentDescriptor.h +1 -9
  33. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.cpp +114 -30
  34. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h +30 -7
  35. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.cpp +2 -2
  36. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetContentViewState.h +9 -8
  37. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewComponentDescriptor.h +3 -5
  38. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.cpp +14 -3
  39. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewShadowNode.h +5 -6
  40. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.cpp +1 -1
  41. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetFooterViewState.h +10 -6
  42. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.cpp +1 -1
  43. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetInsets.h +1 -1
  44. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetLayoutUtils.h +43 -0
  45. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewComponentDescriptor.h +21 -0
  46. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.cpp +17 -0
  47. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetOverlayViewShadowNode.h +33 -0
  48. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewComponentDescriptor.h +3 -5
  49. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.cpp +7 -40
  50. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewShadowNode.h +3 -7
  51. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.cpp +3 -5
  52. package/common/cpp/react/renderer/components/TrueSheetSpec/TrueSheetViewState.h +4 -5
  53. package/ios/TrueSheetContainerView.h +23 -7
  54. package/ios/TrueSheetContainerView.mm +45 -25
  55. package/ios/TrueSheetContentView.h +44 -15
  56. package/ios/TrueSheetContentView.mm +221 -190
  57. package/ios/TrueSheetFooterView.mm +133 -28
  58. package/ios/TrueSheetModule.h +2 -7
  59. package/ios/TrueSheetModule.mm +18 -18
  60. package/ios/TrueSheetOverlayView.h +42 -0
  61. package/ios/TrueSheetOverlayView.mm +175 -0
  62. package/ios/TrueSheetView.h +2 -0
  63. package/ios/TrueSheetView.mm +268 -52
  64. package/ios/TrueSheetViewController.h +36 -3
  65. package/ios/TrueSheetViewController.mm +797 -102
  66. package/ios/core/RNScreensEventObserver.mm +31 -28
  67. package/ios/core/TrueSheetBlurView.h +0 -2
  68. package/ios/core/TrueSheetBlurView.mm +3 -36
  69. package/ios/core/TrueSheetDetentCalculator.h +18 -1
  70. package/ios/core/TrueSheetDetentCalculator.mm +44 -9
  71. package/ios/core/TrueSheetDimView.h +27 -0
  72. package/ios/core/TrueSheetDimView.mm +41 -0
  73. package/ios/core/TrueSheetGrabberView.mm +13 -3
  74. package/ios/core/TrueSheetKeyboardObserver.h +7 -0
  75. package/ios/core/TrueSheetKeyboardObserver.mm +107 -15
  76. package/ios/core/TrueSheetOverlayContainerView.h +31 -0
  77. package/ios/core/TrueSheetOverlayContainerView.mm +35 -0
  78. package/ios/events/TrueSheetLifecycleEvents.h +2 -0
  79. package/ios/events/TrueSheetLifecycleEvents.mm +8 -0
  80. package/ios/tvos/TrueSheetStubs.mm +12 -0
  81. package/ios/utils/UIView+ScrollEdgeInteraction.h +1 -0
  82. package/ios/utils/UIView+ScrollEdgeInteraction.mm +24 -2
  83. package/lib/module/TrueSheet.js +222 -54
  84. package/lib/module/TrueSheet.js.map +1 -1
  85. package/lib/module/TrueSheet.web.js +364 -275
  86. package/lib/module/TrueSheet.web.js.map +1 -1
  87. package/lib/module/TrueSheetBackground.js +15 -0
  88. package/lib/module/TrueSheetBackground.js.map +1 -0
  89. package/lib/module/TrueSheetOverlay.js +30 -0
  90. package/lib/module/TrueSheetOverlay.js.map +1 -0
  91. package/lib/module/TrueSheetOverlay.web.js +35 -0
  92. package/lib/module/TrueSheetOverlay.web.js.map +1 -0
  93. package/lib/module/TrueSheetPeek.web.js +20 -15
  94. package/lib/module/TrueSheetPeek.web.js.map +1 -1
  95. package/lib/module/TrueSheetProvider.web.js +13 -9
  96. package/lib/module/TrueSheetProvider.web.js.map +1 -1
  97. package/lib/module/detentBackgrounds.js +21 -0
  98. package/lib/module/detentBackgrounds.js.map +1 -0
  99. package/lib/module/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  100. package/lib/module/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  101. package/lib/module/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  102. package/lib/module/fabric/TrueSheetViewNativeComponent.ts +89 -73
  103. package/lib/module/index.js +1 -0
  104. package/lib/module/index.js.map +1 -1
  105. package/lib/module/mocks/index.js +18 -2
  106. package/lib/module/mocks/index.js.map +1 -1
  107. package/lib/module/mocks/navigationExpoRouter.js.map +1 -1
  108. package/lib/module/navigation/base-types.js +2 -0
  109. package/lib/module/navigation/base-types.js.map +1 -0
  110. package/lib/module/navigation/createTrueSheetRouter.js +158 -100
  111. package/lib/module/navigation/createTrueSheetRouter.js.map +1 -1
  112. package/lib/module/navigation/expo-router/base-types.js +2 -0
  113. package/lib/module/navigation/expo-router/base-types.js.map +1 -0
  114. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js +71 -0
  115. package/lib/module/navigation/expo-router/extendTrueSheetRouter.js.map +1 -0
  116. package/lib/module/navigation/expo-router/index.js +18 -7
  117. package/lib/module/navigation/expo-router/index.js.map +1 -1
  118. package/lib/module/navigation/expo-router/types.js +4 -0
  119. package/lib/module/navigation/expo-router/types.js.map +1 -0
  120. package/lib/module/navigation/index.js.map +1 -1
  121. package/lib/module/navigation/screen/useSheetScreenState.js +1 -0
  122. package/lib/module/navigation/screen/useSheetScreenState.js.map +1 -1
  123. package/lib/module/scrollable.js +28 -0
  124. package/lib/module/scrollable.js.map +1 -0
  125. package/lib/module/web/constants.js +1 -1
  126. package/lib/module/web/constants.js.map +1 -1
  127. package/lib/module/web/keyboard.js +69 -0
  128. package/lib/module/web/keyboard.js.map +1 -0
  129. package/lib/module/web/layout.js +168 -0
  130. package/lib/module/web/layout.js.map +1 -0
  131. package/lib/module/web/vaul/constants.js +9 -0
  132. package/lib/module/web/vaul/constants.js.map +1 -1
  133. package/lib/module/web/vaul/context.js +4 -0
  134. package/lib/module/web/vaul/context.js.map +1 -1
  135. package/lib/module/web/vaul/helpers.js +1 -0
  136. package/lib/module/web/vaul/helpers.js.map +1 -1
  137. package/lib/module/web/vaul/index.js +83 -21
  138. package/lib/module/web/vaul/index.js.map +1 -1
  139. package/lib/module/web/vaul/use-snap-points.js +57 -28
  140. package/lib/module/web/vaul/use-snap-points.js.map +1 -1
  141. package/lib/typescript/src/TrueSheet.d.ts +18 -1
  142. package/lib/typescript/src/TrueSheet.d.ts.map +1 -1
  143. package/lib/typescript/src/TrueSheet.types.d.ts +209 -35
  144. package/lib/typescript/src/TrueSheet.types.d.ts.map +1 -1
  145. package/lib/typescript/src/TrueSheet.web.d.ts.map +1 -1
  146. package/lib/typescript/src/TrueSheetBackground.d.ts +3 -0
  147. package/lib/typescript/src/TrueSheetBackground.d.ts.map +1 -0
  148. package/lib/typescript/src/TrueSheetOverlay.d.ts +8 -0
  149. package/lib/typescript/src/TrueSheetOverlay.d.ts.map +1 -0
  150. package/lib/typescript/src/TrueSheetOverlay.web.d.ts +8 -0
  151. package/lib/typescript/src/TrueSheetOverlay.web.d.ts.map +1 -0
  152. package/lib/typescript/src/TrueSheetPeek.web.d.ts +8 -4
  153. package/lib/typescript/src/TrueSheetPeek.web.d.ts.map +1 -1
  154. package/lib/typescript/src/TrueSheetProvider.web.d.ts +5 -1
  155. package/lib/typescript/src/TrueSheetProvider.web.d.ts.map +1 -1
  156. package/lib/typescript/src/detentBackgrounds.d.ts +10 -0
  157. package/lib/typescript/src/detentBackgrounds.d.ts.map +1 -0
  158. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts +2 -3
  159. package/lib/typescript/src/fabric/TrueSheetFooterViewNativeComponent.d.ts.map +1 -1
  160. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts +6 -0
  161. package/lib/typescript/src/fabric/TrueSheetOverlayViewNativeComponent.d.ts.map +1 -0
  162. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts +76 -65
  163. package/lib/typescript/src/fabric/TrueSheetViewNativeComponent.d.ts.map +1 -1
  164. package/lib/typescript/src/index.d.ts +1 -0
  165. package/lib/typescript/src/index.d.ts.map +1 -1
  166. package/lib/typescript/src/mocks/index.d.ts +4 -0
  167. package/lib/typescript/src/mocks/index.d.ts.map +1 -1
  168. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts +3 -3
  169. package/lib/typescript/src/mocks/navigationExpoRouter.d.ts.map +1 -1
  170. package/lib/typescript/src/navigation/base-types.d.ts +9 -0
  171. package/lib/typescript/src/navigation/base-types.d.ts.map +1 -0
  172. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts +36 -10
  173. package/lib/typescript/src/navigation/createTrueSheetRouter.d.ts.map +1 -1
  174. package/lib/typescript/src/navigation/expo-router/base-types.d.ts +8 -0
  175. package/lib/typescript/src/navigation/expo-router/base-types.d.ts.map +1 -0
  176. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts +33 -0
  177. package/lib/typescript/src/navigation/expo-router/extendTrueSheetRouter.d.ts.map +1 -0
  178. package/lib/typescript/src/navigation/expo-router/index.d.ts +9 -6
  179. package/lib/typescript/src/navigation/expo-router/index.d.ts.map +1 -1
  180. package/lib/typescript/src/navigation/expo-router/types.d.ts +44 -0
  181. package/lib/typescript/src/navigation/expo-router/types.d.ts.map +1 -0
  182. package/lib/typescript/src/navigation/index.d.ts +3 -4
  183. package/lib/typescript/src/navigation/index.d.ts.map +1 -1
  184. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts +2 -1
  185. package/lib/typescript/src/navigation/screen/useSheetScreenState.d.ts.map +1 -1
  186. package/lib/typescript/src/navigation/types.d.ts +43 -6
  187. package/lib/typescript/src/navigation/types.d.ts.map +1 -1
  188. package/lib/typescript/src/scrollable.d.ts +2 -0
  189. package/lib/typescript/src/scrollable.d.ts.map +1 -0
  190. package/lib/typescript/src/web/constants.d.ts +1 -1
  191. package/lib/typescript/src/web/constants.d.ts.map +1 -1
  192. package/lib/typescript/src/web/keyboard.d.ts +15 -0
  193. package/lib/typescript/src/web/keyboard.d.ts.map +1 -0
  194. package/lib/typescript/src/web/layout.d.ts +20 -0
  195. package/lib/typescript/src/web/layout.d.ts.map +1 -0
  196. package/lib/typescript/src/web/vaul/constants.d.ts +3 -0
  197. package/lib/typescript/src/web/vaul/constants.d.ts.map +1 -1
  198. package/lib/typescript/src/web/vaul/context.d.ts +5 -1
  199. package/lib/typescript/src/web/vaul/context.d.ts.map +1 -1
  200. package/lib/typescript/src/web/vaul/helpers.d.ts +1 -0
  201. package/lib/typescript/src/web/vaul/helpers.d.ts.map +1 -1
  202. package/lib/typescript/src/web/vaul/index.d.ts +20 -1
  203. package/lib/typescript/src/web/vaul/index.d.ts.map +1 -1
  204. package/lib/typescript/src/web/vaul/types.d.ts +9 -0
  205. package/lib/typescript/src/web/vaul/types.d.ts.map +1 -1
  206. package/lib/typescript/src/web/vaul/use-snap-points.d.ts +6 -4
  207. package/lib/typescript/src/web/vaul/use-snap-points.d.ts.map +1 -1
  208. package/package.json +18 -8
  209. package/react-native.config.js +1 -0
  210. package/src/TrueSheet.tsx +257 -62
  211. package/src/TrueSheet.types.ts +225 -35
  212. package/src/TrueSheet.web.tsx +445 -333
  213. package/src/TrueSheetBackground.tsx +17 -0
  214. package/src/TrueSheetOverlay.tsx +24 -0
  215. package/src/TrueSheetOverlay.web.tsx +33 -0
  216. package/src/TrueSheetPeek.web.tsx +25 -23
  217. package/src/TrueSheetProvider.web.tsx +22 -13
  218. package/src/detentBackgrounds.ts +24 -0
  219. package/src/fabric/TrueSheetContentViewNativeComponent.ts +1 -1
  220. package/src/fabric/TrueSheetFooterViewNativeComponent.ts +2 -3
  221. package/src/fabric/TrueSheetOverlayViewNativeComponent.ts +12 -0
  222. package/src/fabric/TrueSheetViewNativeComponent.ts +89 -73
  223. package/src/index.ts +1 -0
  224. package/src/mocks/index.ts +17 -2
  225. package/src/mocks/navigationExpoRouter.ts +6 -5
  226. package/src/navigation/base-types.ts +20 -0
  227. package/src/navigation/createTrueSheetRouter.ts +220 -144
  228. package/src/navigation/expo-router/base-types.ts +18 -0
  229. package/src/navigation/expo-router/extendTrueSheetRouter.ts +99 -0
  230. package/src/navigation/expo-router/index.ts +41 -30
  231. package/src/navigation/expo-router/types.ts +116 -0
  232. package/src/navigation/index.ts +6 -7
  233. package/src/navigation/screen/useSheetScreenState.ts +2 -0
  234. package/src/navigation/types.ts +81 -23
  235. package/src/scrollable.ts +25 -0
  236. package/src/web/constants.ts +1 -1
  237. package/src/web/keyboard.ts +79 -0
  238. package/src/web/layout.ts +196 -0
  239. package/src/web/vaul/constants.ts +9 -0
  240. package/src/web/vaul/context.ts +7 -1
  241. package/src/web/vaul/helpers.ts +5 -0
  242. package/src/web/vaul/index.tsx +113 -16
  243. package/src/web/vaul/types.ts +10 -0
  244. 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
- __weak UIViewController *_parentScreenController;
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
- _parentScreenController = nil;
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
- _parentScreenController = nil;
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 (!_parentScreenController && screenVC) {
169
- _parentScreenController = screenVC;
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 (not immediate presenter), check if the presenter screen is being removed
243
- // This handles nested stack removal case
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
- UINavigationController *parentNav = _parentScreenController.navigationController;
246
-
247
- // Modal case: parent's nav is presented -> let sheet dismiss naturally
248
- // Nested stack case: parent's nav is not presented (embedded) -> need to dismiss
249
- if (parentNav.presentingViewController != nil) {
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
- return navController.topViewController != _presenterScreenController;
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
- _blurInteraction = YES;
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
- UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:style];
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)autoHeight;
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 autoHeight] / self.delegate.screenHeight;
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)autoHeight {
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
- CGFloat footerHeight = self.delegate.absoluteFooter ? 0 : [self.delegate.footerHeight floatValue];
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)offsetForIndex:(NSInteger)index {
79
- // Use this detent's own offset if available
83
+ - (CGFloat)learnedOffsetForIndex:(NSInteger)index {
80
84
  if (index >= 0 && index < (NSInteger)_resolvedDetentOffsets.count) {
81
- CGFloat offset = [_resolvedDetentOffsets[index] doubleValue];
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 frameHeight = pillHeight + kHitPaddingVertical * 2;
149
- CGFloat frameY = topMargin - kHitPaddingVertical;
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, kHitPaddingVertical, pillWidth, pillHeight);
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
- [[NSNotificationCenter defaultCenter] addObserver:self
41
- selector:@selector(keyboardWillChangeFrame:)
42
- name:UIKeyboardWillChangeFrameNotification
43
- object:nil];
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
- [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillChangeFrameNotification object:nil];
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
- - (void)keyboardWillChangeFrame:(NSNotification *)notification {
63
- if (_viewController && !_viewController.isTopmostPresentedController) {
64
- return;
65
- }
66
-
67
- if (![self isFirstResponderWithinSheet]) {
68
- return;
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
- CGFloat keyboardHeight = MAX(0, window.bounds.size.height - keyboardFrameInWindow.origin.y);
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
- if (keyboardHeight > 0) {
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
- } else {
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