@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
|
@@ -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 _appliedSafeAreaInsetAdjustment;
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
@@ -52,37 +47,60 @@ 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)setSafeAreaInsetAdjustment:(BOOL)safeAreaInsetAdjustment {
|
|
73
|
+
if (_safeAreaInsetAdjustment == safeAreaInsetAdjustment) {
|
|
74
|
+
return;
|
|
75
75
|
}
|
|
76
|
+
_safeAreaInsetAdjustment = safeAreaInsetAdjustment;
|
|
77
|
+
[self applySafeAreaInsetAdjustment];
|
|
78
|
+
[self updateBottomInset];
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
- (void)
|
|
79
|
-
if (
|
|
81
|
+
- (void)setFooterInsetAdjustment:(BOOL)footerInsetAdjustment {
|
|
82
|
+
if (_footerInsetAdjustment == footerInsetAdjustment) {
|
|
80
83
|
return;
|
|
81
84
|
}
|
|
82
|
-
|
|
85
|
+
_footerInsetAdjustment = footerInsetAdjustment;
|
|
86
|
+
[self updateBottomInset];
|
|
87
|
+
}
|
|
83
88
|
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
// Hands the bottom safe-area inset to UIKit — `automatic` only insets while
|
|
90
|
+
// the content can scroll, which the Android counterpart mirrors manually.
|
|
91
|
+
- (void)applySafeAreaInsetAdjustment {
|
|
92
|
+
if (!_detectedScrollView) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
97
|
+
if (_safeAreaInsetAdjustment && !_appliedSafeAreaInsetAdjustment) {
|
|
98
|
+
_originalInsetAdjustmentBehavior = scrollView.contentInsetAdjustmentBehavior;
|
|
99
|
+
scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
|
|
100
|
+
_appliedSafeAreaInsetAdjustment = YES;
|
|
101
|
+
} else if (!_safeAreaInsetAdjustment && _appliedSafeAreaInsetAdjustment) {
|
|
102
|
+
scrollView.contentInsetAdjustmentBehavior = _originalInsetAdjustmentBehavior;
|
|
103
|
+
_appliedSafeAreaInsetAdjustment = NO;
|
|
86
104
|
}
|
|
87
105
|
}
|
|
88
106
|
|
|
@@ -111,58 +129,25 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
111
129
|
#pragma mark - Layout
|
|
112
130
|
|
|
113
131
|
- (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;
|
|
132
|
+
if (_state) {
|
|
133
|
+
CGFloat height = _state->getData().naturalHeight;
|
|
134
|
+
if (height > 0) {
|
|
135
|
+
return height;
|
|
136
136
|
}
|
|
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
137
|
}
|
|
138
|
+
return self.frame.size.height;
|
|
160
139
|
}
|
|
161
140
|
|
|
162
141
|
- (void)updateLayoutMetrics:(const LayoutMetrics &)layoutMetrics
|
|
163
142
|
oldLayoutMetrics:(const LayoutMetrics &)oldLayoutMetrics {
|
|
164
143
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
165
|
-
|
|
144
|
+
|
|
145
|
+
// A deep ScrollView unmount doesn't pass through this view's mount hooks —
|
|
146
|
+
// detect the stale reference here so it's released instead of lingering until the
|
|
147
|
+
// next explicit setup or recycle.
|
|
148
|
+
if (_detectedScrollView && ![_detectedScrollView isDescendantOfView:self]) {
|
|
149
|
+
[self.delegate contentViewScrollViewDidChange];
|
|
150
|
+
}
|
|
166
151
|
}
|
|
167
152
|
|
|
168
153
|
#pragma mark - Child Mounting
|
|
@@ -178,46 +163,107 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
178
163
|
}
|
|
179
164
|
|
|
180
165
|
- (void)checkScrollViewChanged {
|
|
181
|
-
if (!
|
|
166
|
+
if (!_detectedScrollView || ![_detectedScrollView isDescendantOfView:self]) {
|
|
167
|
+
[self.delegate contentViewScrollViewDidChange];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// A handle that commits in the same transaction as the ScrollView's insertion
|
|
172
|
+
// resolves to nothing — prop updates process before mount instructions, and a
|
|
173
|
+
// deep insertion doesn't pass through this view's mount hooks. Retry once the
|
|
174
|
+
// whole transaction has mounted.
|
|
175
|
+
- (void)mountingTransactionDidMount:(const MountingTransaction &)transaction
|
|
176
|
+
withSurfaceTelemetry:(const SurfaceTelemetry &)surfaceTelemetry {
|
|
177
|
+
if (_scrollableHandle > 0 && !_detectedScrollView) {
|
|
182
178
|
[self.delegate contentViewScrollViewDidChange];
|
|
183
179
|
}
|
|
180
|
+
|
|
181
|
+
// Layout has settled for the whole tree — the footer overlap (sheet height,
|
|
182
|
+
// scroll view frame) and the keyboard inset are current here
|
|
183
|
+
[self updateBottomInset];
|
|
184
|
+
|
|
185
|
+
[self.delegate contentViewDidMountTransaction];
|
|
184
186
|
}
|
|
185
187
|
|
|
186
188
|
#pragma mark - Scrollable
|
|
187
189
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
// The scroll indicator follows automatically — UIKit derives its insets from
|
|
191
|
+
// the content inset while `automaticallyAdjustsScrollIndicatorInsets` is set.
|
|
192
|
+
- (void)setBottomInset:(CGFloat)inset {
|
|
193
|
+
if (!_detectedScrollView)
|
|
194
|
+
return;
|
|
195
|
+
|
|
196
|
+
UIEdgeInsets contentInset = _detectedScrollView.scrollView.contentInset;
|
|
197
|
+
if (fabs(contentInset.bottom - inset) < 0.001) {
|
|
190
198
|
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
contentInset.bottom = inset;
|
|
202
|
+
_detectedScrollView.scrollView.contentInset = contentInset;
|
|
203
|
+
}
|
|
191
204
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
205
|
+
// How much of the absolute footer covers the scroll view — measured from the
|
|
206
|
+
// footer's layout position (pinned to the container's bottom edge; its
|
|
207
|
+
// keyboard rise is a transform) so a short scroll view ending above the footer
|
|
208
|
+
// gets no inset. UIKit stacks the safe area on top of contentInset while the
|
|
209
|
+
// adjustment behavior is automatic — take out the inset baked into the
|
|
210
|
+
// footer's height so the total lands on the footer's top edge. Stays constant
|
|
211
|
+
// across keyboard transitions: the footer drops its inset as it rises, and
|
|
212
|
+
// the baked value drops with it. A pinned footer adds only the overlap not
|
|
213
|
+
// already covered by the keyboard inset.
|
|
214
|
+
- (CGFloat)footerInsetAbove:(CGFloat)keyboardInset {
|
|
215
|
+
if (!_footerInsetAdjustment || !self.footerView || !_detectedScrollView) {
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
UIView *container = self.footerView.superview;
|
|
220
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
221
|
+
CGRect scrollFrame = [scrollView convertRect:scrollView.bounds toView:container];
|
|
222
|
+
CGFloat footerTop = container.bounds.size.height - self.footerView.bounds.size.height;
|
|
195
223
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
224
|
+
CGFloat inset = CGRectGetMaxY(scrollFrame) - footerTop;
|
|
225
|
+
if (_appliedSafeAreaInsetAdjustment) {
|
|
226
|
+
inset -= self.footerView.appliedBottomInset;
|
|
227
|
+
}
|
|
228
|
+
if (!_keyboardObserver.viewController.footerAvoidsKeyboard) {
|
|
229
|
+
inset -= keyboardInset;
|
|
230
|
+
}
|
|
231
|
+
return MAX(0, inset);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
- (CGFloat)keyboardInset {
|
|
235
|
+
CGFloat keyboardHeight = _keyboardObserver.currentHeight;
|
|
236
|
+
return keyboardHeight > 0 ? [self keyboardInsetWithHeight:keyboardHeight] : 0;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Safe to call mid keyboard animation: the target matches the animation's
|
|
240
|
+
// model value (the footer inset doesn't change with the keyboard), so the
|
|
241
|
+
// early-out in setBottomInset: leaves the animation alone.
|
|
242
|
+
- (void)updateBottomInset {
|
|
243
|
+
if (!_detectedScrollView) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
CGFloat keyboardInset = [self keyboardInset];
|
|
247
|
+
[self setBottomInset:[self footerInsetAbove:keyboardInset] + keyboardInset];
|
|
199
248
|
}
|
|
200
249
|
|
|
201
250
|
- (void)clearScrollable {
|
|
202
|
-
|
|
203
|
-
|
|
251
|
+
[self setBottomInset:0];
|
|
252
|
+
_appliedKeyboardOffset = 0;
|
|
253
|
+
if (_appliedSafeAreaInsetAdjustment) {
|
|
254
|
+
_detectedScrollView.scrollView.contentInsetAdjustmentBehavior = _originalInsetAdjustmentBehavior;
|
|
255
|
+
_appliedSafeAreaInsetAdjustment = NO;
|
|
204
256
|
}
|
|
205
|
-
|
|
206
|
-
[self setScrollableBounded:NO];
|
|
207
|
-
_pinnedScrollView = nil;
|
|
208
|
-
_bottomInset = 0;
|
|
209
|
-
_originalIndicatorBottomInset = 0;
|
|
210
|
-
[self reportSizeIfChanged];
|
|
257
|
+
_detectedScrollView = nil;
|
|
211
258
|
}
|
|
212
259
|
|
|
213
|
-
- (void)
|
|
214
|
-
// Check if
|
|
215
|
-
if (
|
|
260
|
+
- (void)setupScrollable {
|
|
261
|
+
// Check if the detected scroll view is still valid (still in view hierarchy)
|
|
262
|
+
if (_detectedScrollView && ![_detectedScrollView isDescendantOfView:self]) {
|
|
216
263
|
[self clearScrollable];
|
|
217
264
|
}
|
|
218
265
|
|
|
219
|
-
|
|
220
|
-
if (_pinnedScrollView && _bottomInset == bottomInset) {
|
|
266
|
+
if (_detectedScrollView) {
|
|
221
267
|
return;
|
|
222
268
|
}
|
|
223
269
|
|
|
@@ -226,104 +272,76 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
226
272
|
return;
|
|
227
273
|
}
|
|
228
274
|
|
|
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;
|
|
275
|
+
_detectedScrollView = scrollView;
|
|
240
276
|
|
|
241
|
-
[self
|
|
277
|
+
[self applySafeAreaInsetAdjustment];
|
|
242
278
|
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
if (keyboardHeight > 0) {
|
|
246
|
-
CGFloat inset = [self keyboardInsetWithHeight:keyboardHeight];
|
|
247
|
-
[self setScrollViewContentInset:inset indicatorInset:_originalIndicatorBottomInset + inset];
|
|
248
|
-
}
|
|
279
|
+
// Apply the footer inset, and the keyboard inset if it's currently showing
|
|
280
|
+
[self updateBottomInset];
|
|
249
281
|
}
|
|
250
282
|
|
|
251
|
-
//
|
|
252
|
-
//
|
|
253
|
-
// A relative footer stays behind the keyboard below the content, so its
|
|
254
|
-
// height shields that much of the keyboard's overlap.
|
|
283
|
+
// Short or nested scroll views can end above the sheet's bottom edge.
|
|
284
|
+
// Their frame also accounts for space occupied by a relative footer.
|
|
255
285
|
- (CGFloat)keyboardInsetWithHeight:(CGFloat)height {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}
|
|
262
|
-
return MAX(0, height - self.footerView.frame.size.height);
|
|
263
|
-
}
|
|
286
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
287
|
+
UIWindow *window = scrollView.window;
|
|
288
|
+
CGRect scrollFrame = [scrollView convertRect:scrollView.bounds toView:window];
|
|
289
|
+
CGFloat keyboardTop = CGRectGetMaxY(window.bounds) - height;
|
|
290
|
+
CGFloat inset = MAX(0, CGRectGetMaxY(scrollFrame) - keyboardTop);
|
|
264
291
|
|
|
265
|
-
//
|
|
266
|
-
//
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
if (_observedScrollView == scrollView) {
|
|
270
|
-
return;
|
|
292
|
+
// UIKit stacks the safe area on top of contentInset while the adjustment
|
|
293
|
+
// behavior is automatic — take it out so the total lands on the keyboard edge.
|
|
294
|
+
if (_appliedSafeAreaInsetAdjustment) {
|
|
295
|
+
inset = MAX(0, inset - scrollView.safeAreaInsets.bottom);
|
|
271
296
|
}
|
|
272
|
-
[self unobserveScrollViewContentSize];
|
|
273
|
-
_observedScrollView = scrollView;
|
|
274
|
-
[scrollView addObserver:self
|
|
275
|
-
forKeyPath:@"contentSize"
|
|
276
|
-
options:NSKeyValueObservingOptionNew
|
|
277
|
-
context:TrueSheetContentSizeContext];
|
|
278
|
-
}
|
|
279
297
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
298
|
+
// Track how much of keyboardOffset actually lands in the inset so the caret
|
|
299
|
+
// reveal can compensate — the offset shifts the inset, not the keyboard edge.
|
|
300
|
+
CGFloat adjustedInset = MAX(0, inset + self.keyboardOffset);
|
|
301
|
+
_appliedKeyboardOffset =
|
|
302
|
+
adjustedInset + [self footerInsetAbove:adjustedInset] - (inset + [self footerInsetAbove:inset]);
|
|
303
|
+
|
|
304
|
+
// Content that already fits above the keyboard (and footer) has nothing to
|
|
305
|
+
// reveal — the inset would only open blank scroll range below it (huge gap
|
|
306
|
+
// at the bottom).
|
|
307
|
+
if (scrollView.contentSize.height + [self footerInsetAbove:adjustedInset] <=
|
|
308
|
+
scrollView.bounds.size.height - adjustedInset) {
|
|
309
|
+
_appliedKeyboardOffset = 0;
|
|
310
|
+
return 0;
|
|
284
311
|
}
|
|
312
|
+
|
|
313
|
+
return adjustedInset;
|
|
285
314
|
}
|
|
286
315
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
316
|
+
// An absolute footer floats over the viewport's bottom edge — extend the
|
|
317
|
+
// caret target so it clears the footer, not just the keyboard. Not needed
|
|
318
|
+
// while the footer inset is applied (the visible rect already ends above it)
|
|
319
|
+
// or when the footer stays behind the keyboard.
|
|
320
|
+
- (CGFloat)footerOcclusion {
|
|
321
|
+
TrueSheetViewController *controller = _keyboardObserver.viewController;
|
|
322
|
+
if (self.footerView && controller.absoluteFooter && controller.footerAvoidsKeyboard &&
|
|
323
|
+
[self footerInsetAbove:0] <= 0) {
|
|
324
|
+
return [self.footerView keyboardOcclusionHeight];
|
|
294
325
|
}
|
|
295
|
-
|
|
326
|
+
return 0;
|
|
296
327
|
}
|
|
297
328
|
|
|
329
|
+
// Resolves the user-provided `scrollableHandle` within the content subtree —
|
|
330
|
+
// Fabric component views carry their React tag as `UIView.tag`.
|
|
298
331
|
- (RCTScrollViewComponentView *)findScrollView {
|
|
299
|
-
if (
|
|
300
|
-
return
|
|
332
|
+
if (_detectedScrollView) {
|
|
333
|
+
return _detectedScrollView;
|
|
301
334
|
}
|
|
302
335
|
|
|
303
|
-
if (
|
|
336
|
+
if (_scrollableHandle <= 0) {
|
|
304
337
|
return nil;
|
|
305
338
|
}
|
|
306
339
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
for (UIView *subview in self.subviews) {
|
|
311
|
-
scrollView = [self findScrollViewInSubviews:subview.subviews];
|
|
312
|
-
if (scrollView) {
|
|
313
|
-
break;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
340
|
+
UIView *view = [self viewWithTag:_scrollableHandle];
|
|
341
|
+
if ([view isKindOfClass:RCTScrollViewComponentView.class]) {
|
|
342
|
+
return (RCTScrollViewComponentView *)view;
|
|
316
343
|
}
|
|
317
344
|
|
|
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
345
|
return nil;
|
|
328
346
|
}
|
|
329
347
|
|
|
@@ -331,11 +349,11 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
331
349
|
|
|
332
350
|
- (void)applyScrollEdgeEffects:(nullable ScrollableOptions *)options {
|
|
333
351
|
#if RNTS_IPHONE_OS_VERSION_AVAILABLE(26_0)
|
|
334
|
-
if (!
|
|
352
|
+
if (!_detectedScrollView)
|
|
335
353
|
return;
|
|
336
354
|
|
|
337
355
|
if (@available(iOS 26.0, *)) {
|
|
338
|
-
UIScrollView *scrollView =
|
|
356
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
339
357
|
auto topEffect = options ? options.topScrollEdgeEffect : TrueSheetViewTopScrollEdgeEffect::Hidden;
|
|
340
358
|
auto bottomEffect = options ? options.bottomScrollEdgeEffect : TrueSheetViewBottomScrollEdgeEffect::Hidden;
|
|
341
359
|
|
|
@@ -370,8 +388,14 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
370
388
|
|
|
371
389
|
#pragma mark - TrueSheetKeyboardObserverDelegate
|
|
372
390
|
|
|
391
|
+
- (void)setKeyboardObserver:(TrueSheetKeyboardObserver *)keyboardObserver {
|
|
392
|
+
[_keyboardObserver removeDelegate:self];
|
|
393
|
+
_keyboardObserver = keyboardObserver;
|
|
394
|
+
[keyboardObserver addDelegate:self];
|
|
395
|
+
}
|
|
396
|
+
|
|
373
397
|
- (void)keyboardWillShow:(CGFloat)height duration:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
374
|
-
if (!
|
|
398
|
+
if (!_detectedScrollView) {
|
|
375
399
|
return;
|
|
376
400
|
}
|
|
377
401
|
|
|
@@ -380,14 +404,17 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
380
404
|
TrueSheetViewController *sheetController = _keyboardObserver.viewController;
|
|
381
405
|
UIView *firstResponder = sheetController ? [sheetController.view findFirstResponder] : nil;
|
|
382
406
|
|
|
383
|
-
CGFloat
|
|
407
|
+
CGFloat keyboardInset = [self keyboardInsetWithHeight:height];
|
|
408
|
+
CGFloat inset = [self footerInsetAbove:keyboardInset] + keyboardInset;
|
|
384
409
|
[UIView animateWithDuration:duration
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
410
|
+
delay:0
|
|
411
|
+
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
412
|
+
animations:^{
|
|
413
|
+
[self setBottomInset:inset];
|
|
414
|
+
}
|
|
415
|
+
completion:^(BOOL finished) {
|
|
416
|
+
[self updateBottomInset];
|
|
417
|
+
}];
|
|
391
418
|
|
|
392
419
|
// Defer scroll until the next run loop so content insets are applied first
|
|
393
420
|
if (firstResponder) {
|
|
@@ -398,7 +425,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
398
425
|
}
|
|
399
426
|
|
|
400
427
|
- (void)focusedInputTextDidChange:(NSNotification *)notification {
|
|
401
|
-
if (!
|
|
428
|
+
if (!_detectedScrollView || !_keyboardObserver || _keyboardObserver.currentHeight <= 0) {
|
|
402
429
|
return;
|
|
403
430
|
}
|
|
404
431
|
|
|
@@ -409,22 +436,26 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
409
436
|
}
|
|
410
437
|
|
|
411
438
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
439
|
+
// Typing can grow the content (e.g. a multiline input) past the fits-above-
|
|
440
|
+
// the-keyboard threshold — keep the inset in sync before revealing the caret.
|
|
441
|
+
[self updateBottomInset];
|
|
412
442
|
[self scrollToFocusedCaretAnimated:YES];
|
|
413
443
|
});
|
|
414
444
|
}
|
|
415
445
|
|
|
416
446
|
- (void)scrollToFocusedCaretAnimated:(BOOL)animated {
|
|
417
|
-
if (!
|
|
447
|
+
if (!_detectedScrollView) {
|
|
418
448
|
return;
|
|
419
449
|
}
|
|
420
450
|
|
|
421
451
|
TrueSheetViewController *sheetController = _keyboardObserver.viewController;
|
|
422
452
|
UIView *firstResponder = sheetController ? [sheetController.view findFirstResponder] : nil;
|
|
423
|
-
|
|
453
|
+
UIScrollView *scrollView = _detectedScrollView.scrollView;
|
|
454
|
+
// An input outside the scrollable (e.g. in the footer) can't be revealed by scrolling it
|
|
455
|
+
if (!firstResponder || ![firstResponder isDescendantOfView:scrollView]) {
|
|
424
456
|
return;
|
|
425
457
|
}
|
|
426
458
|
|
|
427
|
-
UIScrollView *scrollView = _pinnedScrollView.scrollView;
|
|
428
459
|
CGRect targetRect = [firstResponder convertRect:firstResponder.bounds toView:scrollView];
|
|
429
460
|
|
|
430
461
|
if ([firstResponder conformsToProtocol:@protocol(UITextInput)]) {
|
|
@@ -442,14 +473,14 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
442
473
|
}
|
|
443
474
|
}
|
|
444
475
|
|
|
445
|
-
targetRect.size.height += self.keyboardScrollOffset;
|
|
476
|
+
targetRect.size.height += self.keyboardScrollOffset + [self footerOcclusion] - _appliedKeyboardOffset;
|
|
446
477
|
[scrollView scrollRectToVisible:targetRect animated:animated];
|
|
447
478
|
}
|
|
448
479
|
|
|
449
480
|
- (void)keyboardWillHide:(NSTimeInterval)duration curve:(UIViewAnimationOptions)curve {
|
|
450
481
|
[self stopObservingTextChanges];
|
|
451
482
|
|
|
452
|
-
if (!
|
|
483
|
+
if (!_detectedScrollView) {
|
|
453
484
|
return;
|
|
454
485
|
}
|
|
455
486
|
|
|
@@ -457,8 +488,7 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
457
488
|
delay:0
|
|
458
489
|
options:curve | UIViewAnimationOptionBeginFromCurrentState
|
|
459
490
|
animations:^{
|
|
460
|
-
[self
|
|
461
|
-
indicatorInset:self->_originalIndicatorBottomInset];
|
|
491
|
+
[self setBottomInset:[self footerInsetAbove:0]];
|
|
462
492
|
}
|
|
463
493
|
completion:nil];
|
|
464
494
|
}
|
|
@@ -470,9 +500,10 @@ static void *TrueSheetContentSizeContext = &TrueSheetContentSizeContext;
|
|
|
470
500
|
[self stopObservingTextChanges];
|
|
471
501
|
[self clearScrollable];
|
|
472
502
|
_state.reset();
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
503
|
+
_scrollableHandle = 0;
|
|
504
|
+
_safeAreaInsetAdjustment = NO;
|
|
505
|
+
_footerInsetAdjustment = NO;
|
|
506
|
+
_lastReportedNaturalHeight = 0;
|
|
476
507
|
}
|
|
477
508
|
|
|
478
509
|
@end
|