@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
|
@@ -17,25 +17,20 @@
|
|
|
17
17
|
#import <react/renderer/components/TrueSheetSpec/TrueSheetContentViewShadowNode.h>
|
|
18
18
|
#import "TrueSheetContainerView.h"
|
|
19
19
|
#import "TrueSheetFooterView.h"
|
|
20
|
-
#import "TrueSheetView.h"
|
|
21
20
|
#import "TrueSheetViewController.h"
|
|
22
21
|
#import "utils/PlatformUtil.h"
|
|
23
22
|
#import "utils/UIView+FirstResponder.h"
|
|
24
23
|
|
|
25
24
|
using namespace facebook::react;
|
|
26
25
|
|
|
27
|
-
static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
28
|
-
|
|
29
26
|
@implementation TrueSheetContentView {
|
|
30
27
|
TrueSheetContentViewShadowNode::ConcreteState::Shared _state;
|
|
31
|
-
RCTScrollViewComponentView *
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
CGFloat _bottomInset;
|
|
35
|
-
CGFloat _originalIndicatorBottomInset;
|
|
28
|
+
RCTScrollViewComponentView *_detectedScrollView;
|
|
29
|
+
CGFloat _lastReportedNaturalHeight;
|
|
30
|
+
CGFloat _appliedKeyboardOffset;
|
|
36
31
|
BOOL _observingTextChanges;
|
|
37
|
-
|
|
38
|
-
BOOL
|
|
32
|
+
UIScrollViewContentInsetAdjustmentBehavior _originalInsetAdjustmentBehavior;
|
|
33
|
+
BOOL _appliedContentInsetAdjustment;
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
@@ -52,37 +47,51 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
52
47
|
|
|
53
48
|
- (void)dealloc {
|
|
54
49
|
[self stopObservingTextChanges];
|
|
55
|
-
[self unobserveScrollViewContentSize];
|
|
56
50
|
}
|
|
57
51
|
|
|
58
52
|
- (void)updateState:(const State::Shared &)state oldState:(const State::Shared &)oldState {
|
|
59
53
|
_state = std::static_pointer_cast<TrueSheetContentViewShadowNode::ConcreteState const>(state);
|
|
54
|
+
|
|
55
|
+
CGFloat naturalHeight = _state->getData().naturalHeight;
|
|
56
|
+
if (naturalHeight != _lastReportedNaturalHeight) {
|
|
57
|
+
_lastReportedNaturalHeight = naturalHeight;
|
|
58
|
+
[self.delegate contentViewDidChangeSize:CGSizeMake(self.frame.size.width, naturalHeight)];
|
|
59
|
+
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
// for auto detents — otherwise content lays out naturally like a regular view.
|
|
65
|
-
- (void)setScrollableBounded:(BOOL)bounded {
|
|
66
|
-
if (_scrollableBounded == bounded) {
|
|
62
|
+
- (void)setScrollableHandle:(NSInteger)scrollableHandle {
|
|
63
|
+
if (_scrollableHandle == scrollableHandle) {
|
|
67
64
|
return;
|
|
68
65
|
}
|
|
69
|
-
|
|
66
|
+
_scrollableHandle = scrollableHandle;
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
// Release the previously resolved ScrollView — setupScrollable re-resolves
|
|
69
|
+
[self clearScrollable];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
- (void)setContentInsetAdjustment:(BOOL)contentInsetAdjustment {
|
|
73
|
+
if (_contentInsetAdjustment == contentInsetAdjustment) {
|
|
74
|
+
return;
|
|
75
75
|
}
|
|
76
|
+
_contentInsetAdjustment = contentInsetAdjustment;
|
|
77
|
+
[self applyContentInsetAdjustment];
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
+
// Hands the bottom safe-area inset to UIKit — `automatic` only insets while
|
|
81
|
+
// the content can scroll, which the Android counterpart mirrors manually.
|
|
82
|
+
- (void)applyContentInsetAdjustment {
|
|
83
|
+
if (!_detectedScrollView) {
|
|
80
84
|
return;
|
|
81
85
|
}
|
|
82
|
-
_hasAutoDetent = hasAutoDetent;
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
88
|
+
if (_contentInsetAdjustment && !_appliedContentInsetAdjustment) {
|
|
89
|
+
_originalInsetAdjustmentBehavior = scrollView.contentInsetAdjustmentBehavior;
|
|
90
|
+
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
|
|
91
|
+
_appliedContentInsetAdjustment = YES;
|
|
92
|
+
} else if (!_contentInsetAdjustment && _appliedContentInsetAdjustment) {
|
|
93
|
+
scrollView.contentInsetAdjustmentBehavior = _originalInsetAdjustmentBehavior;
|
|
94
|
+
_appliedContentInsetAdjustment = NO;
|
|
86
95
|
}
|
|
87
96
|
}
|
|
88
97
|
|
|
@@ -111,58 +120,25 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
111
120
|
#pragma mark - Layout
|
|
112
121
|
|
|
113
122
|
- (CGFloat)naturalHeight {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
height
|
|
117
|
-
|
|
118
|
-
return MAX(0, height);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
- (void)reportSizeIfChanged {
|
|
122
|
-
// A deep ScrollView unmount doesn't pass through this view's mount hooks —
|
|
123
|
-
// detect the stale pin here so it (and its contentSize observation) is
|
|
124
|
-
// released instead of lingering until the next explicit setup or recycle.
|
|
125
|
-
if (_pinnedScrollView && ![_pinnedScrollView isDescendantOfView:self]) {
|
|
126
|
-
[self.delegate contentViewScrollViewDidChange];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// naturalHeight mixes frames from different views; mid-transaction they're
|
|
130
|
-
// momentarily inconsistent (parent updates before the ScrollView), which
|
|
131
|
-
// feeds back into the auto detent and oscillates. Coalesce to the next
|
|
132
|
-
// main-queue tick so frames are settled before measuring.
|
|
133
|
-
if (_pinnedScrollView) {
|
|
134
|
-
if (_isReportPending) {
|
|
135
|
-
return;
|
|
123
|
+
if (_state) {
|
|
124
|
+
CGFloat height = _state->getData().naturalHeight;
|
|
125
|
+
if (height > 0) {
|
|
126
|
+
return height;
|
|
136
127
|
}
|
|
137
|
-
_isReportPending = YES;
|
|
138
|
-
|
|
139
|
-
__weak __typeof(self) weakSelf = self;
|
|
140
|
-
dispatch_async(dispatch_get_main_queue(), ^{
|
|
141
|
-
__typeof(self) strongSelf = weakSelf;
|
|
142
|
-
if (!strongSelf) {
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
strongSelf->_isReportPending = NO;
|
|
146
|
-
[strongSelf reportSizeNow];
|
|
147
|
-
});
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
[self reportSizeNow];
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
- (void)reportSizeNow {
|
|
155
|
-
CGSize newSize = CGSizeMake(self.frame.size.width, self.naturalHeight);
|
|
156
|
-
if (!CGSizeEqualToSize(newSize, _lastSize)) {
|
|
157
|
-
_lastSize = newSize;
|
|
158
|
-
[self.delegate contentViewDidChangeSize:newSize];
|
|
159
128
|
}
|
|
129
|
+
return self.frame.size.height;
|
|
160
130
|
}
|
|
161
131
|
|
|
162
132
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
163
133
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
|
|
164
134
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
165
|
-
|
|
135
|
+
|
|
136
|
+
// A deep ScrollView unmount doesn't pass through this view's mount hooks —
|
|
137
|
+
// detect the stale reference here so it's released instead of lingering until the
|
|
138
|
+
// next explicit setup or recycle.
|
|
139
|
+
if (_detectedScrollView && ![_detectedScrollView isDescendantOfView:self]) {
|
|
140
|
+
[self.delegate contentViewScrollViewDidChange];
|
|
141
|
+
}
|
|
166
142
|
}
|
|
167
143
|
|
|
168
144
|
#pragma mark - Child Mounting
|
|
@@ -178,46 +154,52 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
178
154
|
}
|
|
179
155
|
|
|
180
156
|
- (void)checkScrollViewChanged {
|
|
181
|
-
if (!
|
|
157
|
+
if (!_detectedScrollView || ![_detectedScrollView isDescendantOfView:self]) {
|
|
158
|
+
[self.delegate contentViewScrollViewDidChange];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// A handle that commits in the same transaction as the ScrollView's insertion
|
|
163
|
+
// resolves to nothing — prop updates process before mount instructions, and a
|
|
164
|
+
// deep insertion doesn't pass through this view's mount hooks. Retry once the
|
|
165
|
+
// whole transaction has mounted.
|
|
166
|
+
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
167
|
+
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry {
|
|
168
|
+
if (_scrollableHandle > 0 && !_detectedScrollView) {
|
|
182
169
|
[self.delegate contentViewScrollViewDidChange];
|
|
183
170
|
}
|
|
184
171
|
}
|
|
185
172
|
|
|
186
173
|
#pragma mark - Scrollable
|
|
187
174
|
|
|
188
|
-
|
|
189
|
-
|
|
175
|
+
// The scroll indicator follows automatically — UIKit derives its insets from
|
|
176
|
+
// the content inset while `automaticallyAdjustsScrollIndicatorInsets` is set.
|
|
177
|
+
- (void)setKeyboardInset:(CGFloat)inset {
|
|
178
|
+
if (!_detectedScrollView)
|
|
190
179
|
return;
|
|
191
180
|
|
|
192
|
-
UIEdgeInsets contentInset =
|
|
193
|
-
contentInset.bottom =
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
UIEdgeInsets indicatorInsets = _pinnedScrollView.scrollView.verticalScrollIndicatorInsets;
|
|
197
|
-
indicatorInsets.bottom = indicatorBottom;
|
|
198
|
-
_pinnedScrollView.scrollView.verticalScrollIndicatorInsets = indicatorInsets;
|
|
181
|
+
UIEdgeInsets contentInset = _detectedScrollView.scrollView.contentInset;
|
|
182
|
+
contentInset.bottom = inset;
|
|
183
|
+
_detectedScrollView.scrollView.contentInset = contentInset;
|
|
199
184
|
}
|
|
200
185
|
|
|
201
186
|
- (void)clearScrollable {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
_originalIndicatorBottomInset = 0;
|
|
210
|
-
[self reportSizeIfChanged];
|
|
187
|
+
[self setKeyboardInset:0];
|
|
188
|
+
_appliedKeyboardOffset = 0;
|
|
189
|
+
if (_appliedContentInsetAdjustment) {
|
|
190
|
+
_detectedScrollView.scrollView.contentInsetAdjustmentBehavior = _originalInsetAdjustmentBehavior;
|
|
191
|
+
_appliedContentInsetAdjustment = NO;
|
|
192
|
+
}
|
|
193
|
+
_detectedScrollView = nil;
|
|
211
194
|
}
|
|
212
195
|
|
|
213
|
-
- (void)
|
|
214
|
-
// Check if
|
|
215
|
-
if (
|
|
196
|
+
- (void)setupScrollable {
|
|
197
|
+
// Check if the detected scroll view is still valid (still in view hierarchy)
|
|
198
|
+
if (_detectedScrollView && ![_detectedScrollView isDescendantOfView:self]) {
|
|
216
199
|
[self clearScrollable];
|
|
217
200
|
}
|
|
218
201
|
|
|
219
|
-
|
|
220
|
-
if (_pinnedScrollView && _bottomInset == bottomInset) {
|
|
202
|
+
if (_detectedScrollView) {
|
|
221
203
|
return;
|
|
222
204
|
}
|
|
223
205
|
|
|
@@ -226,104 +208,74 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
226
208
|
return;
|
|
227
209
|
}
|
|
228
210
|
|
|
229
|
-
|
|
230
|
-
if (!_pinnedScrollView) {
|
|
231
|
-
_originalIndicatorBottomInset = scrollView.scrollView.verticalScrollIndicatorInsets.bottom;
|
|
232
|
-
_pinnedScrollView = scrollView;
|
|
233
|
-
|
|
234
|
-
[self observeScrollViewContentSize];
|
|
235
|
-
[self setScrollableBounded:_hasAutoDetent];
|
|
236
|
-
[self reportSizeIfChanged];
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
_bottomInset = bottomInset;
|
|
211
|
+
_detectedScrollView = scrollView;
|
|
240
212
|
|
|
241
|
-
[self
|
|
213
|
+
[self applyContentInsetAdjustment];
|
|
242
214
|
|
|
243
215
|
// If keyboard is currently showing, re-apply the keyboard inset to the new ScrollView
|
|
244
216
|
CGFloat keyboardHeight = _keyboardObserver ? _keyboardObserver.currentHeight : 0;
|
|
245
217
|
if (keyboardHeight > 0) {
|
|
246
|
-
|
|
247
|
-
[self setScrollViewContentInset:inset indicatorInset:_originalIndicatorBottomInset + inset];
|
|
218
|
+
[self setKeyboardInset:[self keyboardInsetWithHeight:keyboardHeight]];
|
|
248
219
|
}
|
|
249
220
|
}
|
|
250
221
|
|
|
251
|
-
// An absolute footer rises above the keyboard and covers the content's bottom
|
|
252
|
-
// edge — include it so the caret clears the footer, not just the keyboard.
|
|
253
222
|
// A relative footer stays behind the keyboard below the content, so its
|
|
254
|
-
// height shields that much of the keyboard's overlap.
|
|
223
|
+
// height shields that much of the keyboard's overlap. An absolute footer
|
|
224
|
+
// floats within the viewport — its clearance is the content padding's job
|
|
225
|
+
// (the caret reveal accounts for it, see footerOcclusion).
|
|
255
226
|
- (CGFloat)keyboardInsetWithHeight:(CGFloat)height {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (_keyboardObserver.viewController.absoluteFooter) {
|
|
260
|
-
return height + [self.footerView keyboardOcclusionHeight];
|
|
227
|
+
CGFloat inset = height;
|
|
228
|
+
if (self.footerView && !_keyboardObserver.viewController.absoluteFooter) {
|
|
229
|
+
inset = MAX(0, height - self.footerView.frame.size.height);
|
|
261
230
|
}
|
|
262
|
-
return MAX(0, height - self.footerView.frame.size.height);
|
|
263
|
-
}
|
|
264
231
|
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (_observedScrollView == scrollView) {
|
|
270
|
-
return;
|
|
232
|
+
// UIKit stacks the safe area on top of contentInset while the adjustment
|
|
233
|
+
// behavior is automatic — take it out so the total lands on the keyboard edge.
|
|
234
|
+
if (_appliedContentInsetAdjustment) {
|
|
235
|
+
inset = MAX(0, inset - _detectedScrollView.scrollView.safeAreaInsets.bottom);
|
|
271
236
|
}
|
|
272
|
-
[self unobserveScrollViewContentSize];
|
|
273
|
-
_observedScrollView = scrollView;
|
|
274
|
-
[scrollView addObserver:self
|
|
275
|
-
forKeyPath:@"contentSize"
|
|
276
|
-
options:NSKeyValueObservingOptionNew
|
|
277
|
-
context:TrueSheetContentSizeContext];
|
|
278
|
-
}
|
|
279
237
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
238
|
+
// Track how much of keyboardOffset actually lands in the inset so the caret
|
|
239
|
+
// reveal can compensate — the offset shifts the inset, not the keyboard edge.
|
|
240
|
+
CGFloat adjustedInset = MAX(0, inset + self.keyboardOffset);
|
|
241
|
+
_appliedKeyboardOffset = adjustedInset - inset;
|
|
242
|
+
|
|
243
|
+
// Content that already fits above the keyboard has nothing to reveal — the
|
|
244
|
+
// inset would only open blank scroll range below it (huge gap at the bottom).
|
|
245
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
246
|
+
if (scrollView.contentSize.height <= scrollView.bounds.size.height - adjustedInset) {
|
|
247
|
+
_appliedKeyboardOffset = 0;
|
|
248
|
+
return 0;
|
|
284
249
|
}
|
|
250
|
+
|
|
251
|
+
return adjustedInset;
|
|
285
252
|
}
|
|
286
253
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
[self reportSizeIfChanged];
|
|
293
|
-
return;
|
|
254
|
+
// An absolute footer floats over the viewport's bottom edge — extend the
|
|
255
|
+
// caret target so it clears the footer, not just the keyboard.
|
|
256
|
+
- (CGFloat)footerOcclusion {
|
|
257
|
+
if (self.footerView && _keyboardObserver.viewController.absoluteFooter) {
|
|
258
|
+
return [self.footerView keyboardOcclusionHeight];
|
|
294
259
|
}
|
|
295
|
-
|
|
260
|
+
return 0;
|
|
296
261
|
}
|
|
297
262
|
|
|
263
|
+
// Resolves the user-provided `scrollableHandle` within the content subtree —
|
|
264
|
+
// Fabric component views carry their React tag as `UIView.tag`.
|
|
298
265
|
- (RCTScrollViewComponentView *)findScrollView {
|
|
299
|
-
if (
|
|
300
|
-
return
|
|
266
|
+
if (_detectedScrollView) {
|
|
267
|
+
return _detectedScrollView;
|
|
301
268
|
}
|
|
302
269
|
|
|
303
|
-
if (
|
|
270
|
+
if (_scrollableHandle <= 0) {
|
|
304
271
|
return nil;
|
|
305
272
|
}
|
|
306
273
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
for (UIView *subview in self.subviews) {
|
|
311
|
-
scrollView = [self findScrollViewInSubviews:subview.subviews];
|
|
312
|
-
if (scrollView) {
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
274
|
+
UIView *view = [self viewWithTag:_scrollableHandle];
|
|
275
|
+
if ([view isKindOfClass:RCTScrollViewComponentView.class]) {
|
|
276
|
+
return (RCTScrollViewComponentView *)view;
|
|
316
277
|
}
|
|
317
278
|
|
|
318
|
-
return scrollView;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
- (RCTScrollViewComponentView *)findScrollViewInSubviews:(NSArray<UIView *> *)subviews {
|
|
322
|
-
for (UIView *subview in subviews) {
|
|
323
|
-
if ([subview isKindOfClass:RCTScrollViewComponentView.class] && ![subview isKindOfClass:TrueSheetView.class]) {
|
|
324
|
-
return (RCTScrollViewComponentView *)subview;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
279
|
return nil;
|
|
328
280
|
}
|
|
329
281
|
|
|
@@ -331,11 +283,11 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
331
283
|
|
|
332
284
|
- (void)applyScrollEdgeEffects:(nullable ScrollableOptions *)options {
|
|
333
285
|
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_0)
|
|
334
|
-
if (!
|
|
286
|
+
if (!_detectedScrollView)
|
|
335
287
|
return;
|
|
336
288
|
|
|
337
289
|
if (@available(iOS 26.0, *)) {
|
|
338
|
-
UIScrollView *scrollView =
|
|
290
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
339
291
|
auto topEffect = options ? options.topScrollEdgeEffect : TrueSheetViewTopScrollEdgeEffect::Hidden;
|
|
340
292
|
auto bottomEffect = options ? options.bottomScrollEdgeEffect : TrueSheetViewBottomScrollEdgeEffect::Hidden;
|
|
341
293
|
|
|
@@ -371,7 +323,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
371
323
|
#pragma mark - TrueSheetKeyboardObserverDelegate
|
|
372
324
|
|
|
373
325
|
- (void)keyboardWillShow:(CGFloat)height duration:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
374
|
-
if (!
|
|
326
|
+
if (!_detectedScrollView) {
|
|
375
327
|
return;
|
|
376
328
|
}
|
|
377
329
|
|
|
@@ -385,7 +337,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
385
337
|
delay:0
|
|
386
338
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
387
339
|
animations:^{
|
|
388
|
-
[self
|
|
340
|
+
[self setKeyboardInset:inset];
|
|
389
341
|
}
|
|
390
342
|
completion:nil];
|
|
391
343
|
|
|
@@ -398,7 +350,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
398
350
|
}
|
|
399
351
|
|
|
400
352
|
- (void)focusedInputTextDidChange:(NSNotification *)notification {
|
|
401
|
-
if (!
|
|
353
|
+
if (!_detectedScrollView || !_keyboardObserver || _keyboardObserver.currentHeight <= 0) {
|
|
402
354
|
return;
|
|
403
355
|
}
|
|
404
356
|
|
|
@@ -409,12 +361,15 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
409
361
|
}
|
|
410
362
|
|
|
411
363
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
364
|
+
// Typing can grow the content (e.g. a multiline input) past the fits-above-
|
|
365
|
+
// the-keyboard threshold — keep the inset in sync before revealing the caret.
|
|
366
|
+
[self setKeyboardInset:[self keyboardInsetWithHeight:self->_keyboardObserver.currentHeight]];
|
|
412
367
|
[self scrollToFocusedCaretAnimated:YES];
|
|
413
368
|
});
|
|
414
369
|
}
|
|
415
370
|
|
|
416
371
|
- (void)scrollToFocusedCaretAnimated:(BOOL)animated {
|
|
417
|
-
if (!
|
|
372
|
+
if (!_detectedScrollView) {
|
|
418
373
|
return;
|
|
419
374
|
}
|
|
420
375
|
|
|
@@ -424,7 +379,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
424
379
|
return;
|
|
425
380
|
}
|
|
426
381
|
|
|
427
|
-
UIScrollView *scrollView =
|
|
382
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
428
383
|
CGRect targetRect = [firstResponder convertRect:firstResponder.bounds toView:scrollView];
|
|
429
384
|
|
|
430
385
|
if ([firstResponder conformsToProtocol:@protocol(UITextInput)]) {
|
|
@@ -442,14 +397,14 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
442
397
|
}
|
|
443
398
|
}
|
|
444
399
|
|
|
445
|
-
targetRect.size.height += self.keyboardScrollOffset;
|
|
400
|
+
targetRect.size.height += self.keyboardScrollOffset + [self footerOcclusion] - _appliedKeyboardOffset;
|
|
446
401
|
[scrollView scrollRectToVisible:targetRect animated:animated];
|
|
447
402
|
}
|
|
448
403
|
|
|
449
404
|
- (void)keyboardWillHide:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
450
405
|
[self stopObservingTextChanges];
|
|
451
406
|
|
|
452
|
-
if (!
|
|
407
|
+
if (!_detectedScrollView) {
|
|
453
408
|
return;
|
|
454
409
|
}
|
|
455
410
|
|
|
@@ -457,8 +412,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
457
412
|
delay:0
|
|
458
413
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
459
414
|
animations:^{
|
|
460
|
-
[self
|
|
461
|
-
indicatorInset:self->_originalIndicatorBottomInset];
|
|
415
|
+
[self setKeyboardInset:0];
|
|
462
416
|
}
|
|
463
417
|
completion:nil];
|
|
464
418
|
}
|
|
@@ -470,9 +424,9 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
470
424
|
[self stopObservingTextChanges];
|
|
471
425
|
[self clearScrollable];
|
|
472
426
|
_state.reset();
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
427
|
+
_scrollableHandle = 0;
|
|
428
|
+
_contentInsetAdjustment = NO;
|
|
429
|
+
_lastReportedNaturalHeight = 0;
|
|
476
430
|
}
|
|
477
431
|
|
|
478
432
|
@end
|
|
@@ -27,6 +27,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
27
27
|
@property (nonatomic, weak, nullable) TrueSheetKeyboardObserver *keyboardObserver;
|
|
28
28
|
@property (nonatomic, weak, nullable) id<TrueSheetFooterViewDelegate> delegate;
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* Bottom inset currently baked into the footer's layout height — pairs with
|
|
32
|
+
* the measured frame so detent math can subtract exactly what's baked in.
|
|
33
|
+
*/
|
|
34
|
+
@property (nonatomic, readonly) CGFloat appliedBottomInset;
|
|
35
|
+
|
|
30
36
|
/**
|
|
31
37
|
* Re-applies the footer's keyboard slide using the current keyboard height.
|
|
32
38
|
* No-op when the keyboard is hidden. Used to reflect live `keyboardOffset` changes.
|
|
@@ -25,6 +25,8 @@ using namespace facebook::react;
|
|
|
25
25
|
CGFloat _currentKeyboardOffset;
|
|
26
26
|
CGFloat _bottomInset;
|
|
27
27
|
BOOL _keyboardVisible;
|
|
28
|
+
BOOL _didPushBottomInset;
|
|
29
|
+
CGFloat _appliedBottomInset;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
@@ -43,6 +45,8 @@ using namespace facebook::react;
|
|
|
43
45
|
_currentKeyboardOffset = 0;
|
|
44
46
|
_bottomInset = 0;
|
|
45
47
|
_keyboardVisible = NO;
|
|
48
|
+
_didPushBottomInset = NO;
|
|
49
|
+
_appliedBottomInset = 0;
|
|
46
50
|
}
|
|
47
51
|
return self;
|
|
48
52
|
}
|
|
@@ -55,7 +59,11 @@ using namespace facebook::react;
|
|
|
55
59
|
// bottom safe-area inset — the footer owns the sheet's bottom edge, so it
|
|
56
60
|
// absorbs the inset and its background fills it.
|
|
57
61
|
- (void)setBottomInset:(CGFloat)bottomInset {
|
|
58
|
-
|
|
62
|
+
// The first push must go through even when the value matches the ivar
|
|
63
|
+
// default (0) — it marks the state initialized so the shadow node stops
|
|
64
|
+
// seeding from the app-wide precalculated inset, which may belong to
|
|
65
|
+
// another sheet (e.g. a floating sheet whose real inset is 0).
|
|
66
|
+
if (_didPushBottomInset && _bottomInset == bottomInset) {
|
|
59
67
|
return;
|
|
60
68
|
}
|
|
61
69
|
_bottomInset = bottomInset;
|
|
@@ -85,6 +93,14 @@ using namespace facebook::react;
|
|
|
85
93
|
// them an inset stale (same reason Android bridges through
|
|
86
94
|
// TrueSheetStateUpdater).
|
|
87
95
|
_state->updateState(std::move(newState), EventQueue::UpdateMode::unstable_Immediate);
|
|
96
|
+
_didPushBottomInset = YES;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The inset baked into the current layout by the shadow node (pushed or
|
|
100
|
+
// seeded — see TrueSheetFooterViewShadowNode). Refreshed in
|
|
101
|
+
// updateLayoutMetrics so it always pairs with the measured height.
|
|
102
|
+
- (CGFloat)appliedBottomInset {
|
|
103
|
+
return _appliedBottomInset;
|
|
88
104
|
}
|
|
89
105
|
|
|
90
106
|
// Height the footer occupies above the keyboard — its layout height minus the
|
|
@@ -123,8 +139,19 @@ using namespace facebook::react;
|
|
|
123
139
|
oldLayoutMetrics:(const facebook::react::LayoutMetrics &)oldLayoutMetrics {
|
|
124
140
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
125
141
|
|
|
142
|
+
// State lands before layout metrics within the same mount transaction, so
|
|
143
|
+
// this reflects the inset the shadow node baked into this frame — seeded
|
|
144
|
+
// values are recorded back into the state at layout time (see
|
|
145
|
+
// TrueSheetFooterViewShadowNode).
|
|
146
|
+
CGFloat previousAppliedBottomInset = _appliedBottomInset;
|
|
147
|
+
if (_state) {
|
|
148
|
+
_appliedBottomInset = _state->getData().bottomInset;
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
CGFloat height = layoutMetrics.frame.size.height;
|
|
127
|
-
|
|
152
|
+
// An inset change alone (fixed-height footer: padding moves, frame doesn't)
|
|
153
|
+
// still changes what's baked in — report so the controller re-pairs.
|
|
154
|
+
if (height != _lastHeight || _appliedBottomInset != previousAppliedBottomInset) {
|
|
128
155
|
_lastHeight = height;
|
|
129
156
|
[self.delegate footerViewDidChangeSize:CGSizeMake(layoutMetrics.frame.size.width, height)];
|
|
130
157
|
}
|
|
@@ -143,6 +170,8 @@ using namespace facebook::react;
|
|
|
143
170
|
_currentKeyboardOffset = 0;
|
|
144
171
|
_bottomInset = 0;
|
|
145
172
|
_keyboardVisible = NO;
|
|
173
|
+
_didPushBottomInset = NO;
|
|
174
|
+
_appliedBottomInset = 0;
|
|
146
175
|
}
|
|
147
176
|
|
|
148
177
|
#pragma mark - TrueSheetKeyboardObserverDelegate
|
|
@@ -167,6 +196,7 @@ using namespace facebook::react;
|
|
|
167
196
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
168
197
|
animations:^{
|
|
169
198
|
self.transform = CGAffineTransformMakeTranslation(0, -slide);
|
|
199
|
+
[self syncEdgeEffect];
|
|
170
200
|
}
|
|
171
201
|
completion:nil];
|
|
172
202
|
}
|
|
@@ -180,10 +210,26 @@ using namespace facebook::react;
|
|
|
180
210
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
181
211
|
animations:^{
|
|
182
212
|
self.transform = CGAffineTransformIdentity;
|
|
213
|
+
[self syncEdgeEffect];
|
|
183
214
|
}
|
|
184
215
|
completion:nil];
|
|
185
216
|
}
|
|
186
217
|
|
|
218
|
+
// UIKit derives the edge effect region from model geometry at commit time,
|
|
219
|
+
// outside our animation context — the blur snaps to the footer's final
|
|
220
|
+
// position while the footer is still animating. Re-evaluating the scroll
|
|
221
|
+
// view's layout inside the animation block makes the region change inherit
|
|
222
|
+
// the keyboard curve.
|
|
223
|
+
- (void)syncEdgeEffect {
|
|
224
|
+
if (@available(iOS 26.0, *)) {
|
|
225
|
+
UIScrollView *scrollView = [self edgeInteractionScrollView];
|
|
226
|
+
if (scrollView) {
|
|
227
|
+
[scrollView setNeedsLayout];
|
|
228
|
+
[scrollView layoutIfNeeded];
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
187
233
|
- (void)applyKeyboardOffset {
|
|
188
234
|
CGFloat height = self.keyboardObserver.currentHeight;
|
|
189
235
|
if (height <= 0 || !self.keyboardObserver.viewController.absoluteFooter) {
|
package/ios/TrueSheetModule.h
CHANGED
|
@@ -27,16 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Register a sheet component view with its React tag
|
|
30
|
-
* Called automatically by TrueSheetView
|
|
30
|
+
* Called automatically by TrueSheetView when it attaches to a window.
|
|
31
|
+
* The registry holds views weakly, so no unregistration is needed.
|
|
31
32
|
*/
|
|
32
33
|
+ (void)registerView:(TrueSheetView *)view withTag:(NSNumber *)tag;
|
|
33
34
|
|
|
34
|
-
/**
|
|
35
|
-
* Unregister a sheet component view
|
|
36
|
-
* Called automatically by TrueSheetView during dealloc
|
|
37
|
-
*/
|
|
38
|
-
+ (void)unregisterViewWithTag:(NSNumber *)tag;
|
|
39
|
-
|
|
40
35
|
@end
|
|
41
36
|
|
|
42
37
|
NS_ASSUME_NONNULL_END
|