@apexnative/navigation 1.0.0-alpha.6 → 1.0.0-alpha.7
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/ApexNavigation.podspec +2 -1
- package/CHANGELOG.md +7 -0
- package/android/CLAUDE.md +19 -10
- package/android/CORE.md +3 -2
- package/android/consumer-rules.pro +6 -0
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/ButtonOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LargeTitleOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/LayoutAnimation.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/OptionKey.kt +18 -0
- package/android/src/main/java/dev/apexnative/navigation/options/OptionPaths.kt +91 -0
- package/android/src/main/java/dev/apexnative/navigation/options/SheetOptions.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/options/TitleOptions.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarAppearanceOptions.kt +1 -1
- package/android/src/main/java/dev/apexnative/navigation/options/TopBarOptions.kt +16 -7
- package/android/src/main/java/dev/apexnative/navigation/react/ContentHeights.kt +95 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ContentMounts.kt +66 -0
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/react/OptionsCommands.kt +41 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReactView.kt +16 -1
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotScreenView.kt +27 -7
- package/android/src/main/java/dev/apexnative/navigation/slots/SlotView.kt +12 -1
- package/android/src/main/java/dev/apexnative/navigation/utils/MaterialTheme.kt +5 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/SystemAnimations.kt +36 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +329 -55
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +26 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetStops.kt +60 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/sidemenu/SideMenuMotion.kt +8 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/BackPreview.kt +32 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +46 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackQueue.kt +15 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +7 -4
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabMotion.kt +142 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/fab/FabPresenter.kt +215 -43
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/search/StackSearchPresenter.kt +25 -26
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/BarRowTransition.kt +3 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/ButtonPlaces.kt +11 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/topbar/TopBarPresenter.kt +36 -38
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +124 -181
- package/android/src/main/java/dev/apexnative/navigation/views/element/TargetsWait.kt +8 -1
- package/android/src/main/java/dev/apexnative/navigation/views/element/TransitionLayer.kt +37 -112
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +56 -3
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +170 -153
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMorph.kt +199 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +55 -45
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/{TextChangeAnimator.kt → TextMorph.kt} +79 -129
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewMorph.kt +108 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabCopy.kt +85 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/fab/FabMenu.kt +13 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TitleBar.kt +5 -24
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/TopBar.kt +51 -34
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonComponentCell.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarButtonView.kt +113 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/BarMenu.kt +219 -0
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonBar.kt +121 -175
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonMenus.kt +34 -64
- package/android/src/main/java/dev/apexnative/navigation/views/stack/topbar/titlebar/ButtonRow.kt +216 -0
- package/android/src/main/jni/ANNContentHeights.cpp +82 -0
- package/android/src/main/res/drawable/apex_navigation_ic_arrow_right.xml +14 -0
- package/android/src/main/res/layout/apex_navigation_menu_header.xml +19 -0
- package/android/src/main/res/layout/apex_navigation_menu_item.xml +68 -0
- package/android/src/main/res/values/styles.xml +1 -1
- package/android/src/main/res/values-night/styles.xml +1 -1
- package/cli/doctor/checks/app.js +16 -1
- package/cli/project.js +4 -0
- package/cli/setup/index.js +5 -0
- package/cli/setup/steps/metro.js +46 -0
- package/docs-md/architecture/native-screens.md +4 -0
- package/docs-md/architecture/theme-and-context.md +131 -12
- package/docs-md/architecture/transitions.md +43 -4
- package/docs-md/flows/bottom-sheets.md +4 -2
- package/docs-md/fundamentals/screens-and-props.md +9 -1
- package/docs-md/guides/cli.md +3 -1
- package/docs-md/help/troubleshooting.md +1 -1
- package/docs-md/index.json +29 -1
- package/docs-md/screen-ui/back-and-gestures.md +3 -1
- package/docs-md/screen-ui/bar-buttons.md +1 -1
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -0
- package/docs-md/start/installation.md +16 -1
- package/ios/Animations/ANNFrameDriver.h +55 -15
- package/ios/Animations/ANNFrameDriver.mm +214 -60
- package/ios/Animations/ANNTransitionAnimator.h +26 -5
- package/ios/Animations/ANNTransitionAnimator.mm +49 -3
- package/ios/Animations/ANNTransitionPlan.h +2 -2
- package/ios/Animations/ANNTransitionPlan.mm +10 -0
- package/ios/Animations/Elements/ANNElementFinder.h +2 -2
- package/ios/Animations/Elements/ANNElementFinder.mm +2 -2
- package/ios/Animations/Elements/ANNElementTransition.h +3 -3
- package/ios/Animations/Elements/ANNElementTransition.mm +2 -2
- package/ios/Animations/Elements/ANNSharedElementFlight.h +3 -3
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +181 -43
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -5
- package/ios/Animations/Interpolators/ANNViewTracks.mm +51 -21
- package/ios/CLAUDE.md +15 -7
- package/ios/CORE.md +1 -1
- package/ios/Core/ANNAppDelegate.mm +0 -2
- package/ios/Core/ANNDeepLinkQueue.h +6 -5
- package/ios/Core/ANNDeepLinkQueue.mm +22 -10
- package/ios/Core/ANNSceneDelegate.mm +2 -2
- package/ios/Core/ApexNavigation.mm +6 -2
- package/ios/Layouts/Root/ANNRootCoordinator.mm +45 -15
- package/ios/Layouts/Screen/ANNComponentViewController.mm +4 -1
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.h +11 -2
- package/ios/Layouts/Stack/ANNStackAnimatingDelegate.mm +14 -7
- package/ios/Layouts/Stack/ANNStackController.h +8 -0
- package/ios/Layouts/Stack/ANNStackController.mm +153 -11
- package/ios/Layouts/Stack/ANNSwipePopInteraction.h +26 -0
- package/ios/Layouts/Stack/ANNSwipePopInteraction.mm +185 -0
- package/ios/Layouts/Stack/InteractivePopGestureDelegate.mm +28 -4
- package/ios/Options/ANNAnimationOptions.h +2 -1
- package/ios/Options/ANNAnimationOptions.mm +1 -1
- package/ios/Options/ANNOptions.h +7 -0
- package/ios/Options/ANNOptions.mm +45 -0
- package/ios/Options/ANNOptionsReader.mm +2 -4
- package/ios/Options/ANNStackPopAnimationOptions.h +13 -0
- package/ios/Options/ANNStackPopAnimationOptions.mm +11 -0
- package/ios/Options/Params/ColorParser.h +4 -3
- package/ios/Options/Params/ColorParser.mm +22 -4
- package/ios/React/ANNSurfaceHostingView.h +8 -0
- package/ios/React/ANNSurfaceHostingView.mm +28 -1
- package/ios/React/Slots/ANNSlotScreenView.mm +8 -2
- package/ios/TurboModules/ANNTurboModule.mm +46 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.cpp +20 -0
- package/ios/cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h +5 -14
- package/lib/module/ARCHITECTURE.md +9 -3
- package/lib/module/adapters/NativeANNTurboModule.js.map +1 -1
- package/lib/module/api/app/AppRegistry.js +14 -2
- package/lib/module/api/app/AppRegistry.js.map +1 -1
- package/lib/module/api/app/createNavigation.js +188 -66
- package/lib/module/api/app/createNavigation.js.map +1 -1
- package/lib/module/api/events/navigationEvents.js +6 -2
- package/lib/module/api/events/navigationEvents.js.map +1 -1
- package/lib/module/api/linking/linking.js +11 -0
- package/lib/module/api/linking/linking.js.map +1 -1
- package/lib/module/api/screens/defineScreen.js +45 -3
- package/lib/module/api/screens/defineScreen.js.map +1 -1
- package/lib/module/api/screens/nativeScreens.js +31 -6
- package/lib/module/api/screens/nativeScreens.js.map +1 -1
- package/lib/module/api/screens/registerScreens.js +172 -35
- package/lib/module/api/screens/registerScreens.js.map +1 -1
- package/lib/module/api/theme/theme.js +5 -0
- package/lib/module/api/theme/theme.js.map +1 -1
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/layout.js +7 -2
- package/lib/module/engine/layout.js.map +1 -1
- package/lib/module/engine/nativeScreens.js +9 -0
- package/lib/module/engine/nativeScreens.js.map +1 -1
- package/lib/module/engine/options.js +5 -4
- package/lib/module/engine/options.js.map +1 -1
- package/lib/module/engine/optionsChange.js +102 -0
- package/lib/module/engine/optionsChange.js.map +1 -0
- package/lib/module/engine/refresh.js +85 -0
- package/lib/module/engine/refresh.js.map +1 -0
- package/lib/module/engine/rememberedOptions.js +60 -0
- package/lib/module/engine/rememberedOptions.js.map +1 -1
- package/lib/module/engine/screens.js +26 -0
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts +7 -0
- package/lib/typescript/adapters/NativeANNTurboModule.d.ts.map +1 -1
- package/lib/typescript/api/app/AppRegistry.d.ts.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +5 -0
- package/lib/typescript/api/app/createNavigation.d.ts.map +1 -1
- package/lib/typescript/api/events/navigationEvents.d.ts +2 -2
- package/lib/typescript/api/events/navigationEvents.d.ts.map +1 -1
- package/lib/typescript/api/index.d.ts +1 -1
- package/lib/typescript/api/index.d.ts.map +1 -1
- package/lib/typescript/api/linking/linking.d.ts +8 -0
- package/lib/typescript/api/linking/linking.d.ts.map +1 -1
- package/lib/typescript/api/screens/defineScreen.d.ts +23 -4
- package/lib/typescript/api/screens/defineScreen.d.ts.map +1 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts +8 -1
- package/lib/typescript/api/screens/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/api/screens/registerScreens.d.ts.map +1 -1
- package/lib/typescript/api/theme/theme.d.ts +2 -0
- package/lib/typescript/api/theme/theme.d.ts.map +1 -1
- package/lib/typescript/engine/commands.d.ts +5 -0
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/layout.d.ts.map +1 -1
- package/lib/typescript/engine/nativeScreens.d.ts +5 -0
- package/lib/typescript/engine/nativeScreens.d.ts.map +1 -1
- package/lib/typescript/engine/optionsChange.d.ts +28 -0
- package/lib/typescript/engine/optionsChange.d.ts.map +1 -0
- package/lib/typescript/engine/refresh.d.ts +35 -0
- package/lib/typescript/engine/refresh.d.ts.map +1 -0
- package/lib/typescript/engine/rememberedOptions.d.ts +13 -0
- package/lib/typescript/engine/rememberedOptions.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +7 -0
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/ComponentEvents.d.ts +2 -2
- package/lib/typescript/interfaces/options/animations.d.ts +40 -15
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +2 -2
- package/lib/typescript/interfaces/options/buttons.d.ts +2 -2
- package/lib/typescript/interfaces/options/modals.d.ts +6 -7
- package/lib/typescript/interfaces/options/modals.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/stack.d.ts +8 -3
- package/lib/typescript/interfaces/options/stack.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/topBar.d.ts +14 -0
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/metro/acceptPlugin.js +57 -0
- package/metro/babelTransformer.js +45 -0
- package/metro/index.js +36 -0
- package/package.json +3 -1
- package/src/ARCHITECTURE.md +9 -3
- package/src/adapters/NativeANNTurboModule.ts +7 -0
- package/src/api/app/AppRegistry.ts +16 -2
- package/src/api/app/createNavigation.ts +198 -66
- package/src/api/events/navigationEvents.ts +9 -3
- package/src/api/index.ts +1 -0
- package/src/api/linking/linking.ts +22 -1
- package/src/api/screens/defineScreen.ts +49 -5
- package/src/api/screens/nativeScreens.ts +40 -4
- package/src/api/screens/registerScreens.tsx +170 -42
- package/src/api/theme/theme.ts +5 -0
- package/src/engine/commands.ts +21 -0
- package/src/engine/layout.ts +11 -2
- package/src/engine/nativeScreens.ts +10 -1
- package/src/engine/options.ts +6 -8
- package/src/engine/optionsChange.ts +107 -0
- package/src/engine/refresh.ts +97 -0
- package/src/engine/rememberedOptions.ts +70 -0
- package/src/engine/screens.tsx +24 -0
- package/src/interfaces/ComponentEvents.ts +2 -2
- package/src/interfaces/options/animations.ts +40 -15
- package/src/interfaces/options/bottomTabs.ts +2 -2
- package/src/interfaces/options/buttons.ts +2 -2
- package/src/interfaces/options/modals.ts +6 -7
- package/src/interfaces/options/stack.ts +8 -3
- package/src/interfaces/options/topBar.ts +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementStub.kt +0 -31
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +0 -134
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +0 -78
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PropertyAnimatorCreator.kt +0 -33
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +0 -57
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +0 -146
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#import "ANNSwipePopInteraction.h"
|
|
2
|
+
#import "ANNAssert.h"
|
|
3
|
+
#import "ANNTransitionAnimator.h"
|
|
4
|
+
|
|
5
|
+
// A flick faster than this, in points a second, ends the swipe by its direction alone.
|
|
6
|
+
static const CGFloat ANNSwipeFlickSpeed = 500;
|
|
7
|
+
|
|
8
|
+
// The rest of the pop plays at most this many times as fast as its own animation.
|
|
9
|
+
static const CGFloat ANNSwipeMostSpeedUp = 3;
|
|
10
|
+
|
|
11
|
+
@implementation ANNSwipePopInteraction {
|
|
12
|
+
ANNTransitionAnimator *_strongAnimator;
|
|
13
|
+
__weak UIPanGestureRecognizer *_recognizer;
|
|
14
|
+
__weak UIView *_view;
|
|
15
|
+
id<UIViewControllerContextTransitioning> _context;
|
|
16
|
+
// The interaction keeps itself while it follows the gesture: UIKit may let it go before the finger does.
|
|
17
|
+
ANNSwipePopInteraction *_keep;
|
|
18
|
+
// The gesture ended before UIKit started the transition: the end is chosen at the start.
|
|
19
|
+
BOOL _endedEarly;
|
|
20
|
+
BOOL _gaveUpEarly;
|
|
21
|
+
CGFloat _completionSpeed;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
- (instancetype)initWithAnimator:(ANNTransitionAnimator *)animator
|
|
25
|
+
recognizer:(UIPanGestureRecognizer *)recognizer
|
|
26
|
+
view:(UIView *)view {
|
|
27
|
+
if (self = [super init]) {
|
|
28
|
+
_animator = animator;
|
|
29
|
+
_strongAnimator = animator;
|
|
30
|
+
_recognizer = recognizer;
|
|
31
|
+
_view = view;
|
|
32
|
+
_completionSpeed = 1;
|
|
33
|
+
_keep = self;
|
|
34
|
+
[recognizer addTarget:self action:@selector(swipe:)];
|
|
35
|
+
}
|
|
36
|
+
return self;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#pragma mark - UIViewControllerInteractiveTransitioning
|
|
40
|
+
|
|
41
|
+
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
|
|
42
|
+
ANNAssertMainQueue();
|
|
43
|
+
ANNTransitionAnimator *animator = _strongAnimator;
|
|
44
|
+
if (!animator || _context) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
_context = transitionContext;
|
|
48
|
+
[animator animateFollowingGesture:transitionContext];
|
|
49
|
+
UIGestureRecognizerState state = _recognizer.state;
|
|
50
|
+
BOOL moving = state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged;
|
|
51
|
+
if (_gaveUpEarly) {
|
|
52
|
+
[self endFinishing:NO speed:1];
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (_endedEarly || !moving) {
|
|
56
|
+
[self letGo];
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
[self follow];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
- (CGFloat)completionSpeed {
|
|
63
|
+
return _completionSpeed;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The frames play at an even pace to their end, so UIKit's part (the bar) does too.
|
|
67
|
+
- (UIViewAnimationCurve)completionCurve {
|
|
68
|
+
return UIViewAnimationCurveLinear;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
- (BOOL)wantsInteractiveStart {
|
|
72
|
+
return YES;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#pragma mark - The gesture
|
|
76
|
+
|
|
77
|
+
- (void)swipe:(UIPanGestureRecognizer *)recognizer {
|
|
78
|
+
switch (recognizer.state) {
|
|
79
|
+
case UIGestureRecognizerStateBegan:
|
|
80
|
+
case UIGestureRecognizerStateChanged:
|
|
81
|
+
[self follow];
|
|
82
|
+
break;
|
|
83
|
+
case UIGestureRecognizerStateEnded:
|
|
84
|
+
[self letGo];
|
|
85
|
+
break;
|
|
86
|
+
case UIGestureRecognizerStateCancelled:
|
|
87
|
+
case UIGestureRecognizerStateFailed:
|
|
88
|
+
[self giveUp];
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (void)follow {
|
|
96
|
+
if (!_context) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (_strongAnimator.isCompleted) {
|
|
100
|
+
[self stop];
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
CGFloat progress = [self progress];
|
|
104
|
+
[_strongAnimator setProgress:progress];
|
|
105
|
+
[_context updateInteractiveTransition:progress];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// The finger left: a flick decides by its direction, a slow release by how far the swipe went.
|
|
109
|
+
- (void)letGo {
|
|
110
|
+
if (!_context) {
|
|
111
|
+
[self endedEarlyGivingUp:NO];
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
CGFloat velocity = [self alongBack:[_recognizer velocityInView:_view].x];
|
|
115
|
+
BOOL finish = velocity > ANNSwipeFlickSpeed ||
|
|
116
|
+
(velocity >= -ANNSwipeFlickSpeed && [self progress] > 0.5);
|
|
117
|
+
// The rest takes the finger's pace when that is quicker than the animation's: the animation would cover the width
|
|
118
|
+
// in its length, the finger in width / velocity.
|
|
119
|
+
CGFloat width = MAX(_view.bounds.size.width, 1);
|
|
120
|
+
BOOL withFinger = finish ? velocity > 0 : velocity < 0;
|
|
121
|
+
CGFloat speed = withFinger ? fabs(velocity) * _strongAnimator.length / width : 1;
|
|
122
|
+
[self endFinishing:finish speed:MIN(MAX(speed, 1.0), ANNSwipeMostSpeedUp)];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// The system took the touch (a call, a second gesture): the screen goes back.
|
|
126
|
+
- (void)giveUp {
|
|
127
|
+
if (!_context) {
|
|
128
|
+
[self endedEarlyGivingUp:YES];
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
[self endFinishing:NO speed:1];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// UIKit holds an interaction it starts; one it never starts goes once the gesture ended.
|
|
135
|
+
- (void)endedEarlyGivingUp:(BOOL)gaveUp {
|
|
136
|
+
_endedEarly = YES;
|
|
137
|
+
_gaveUpEarly = gaveUp;
|
|
138
|
+
[_recognizer removeTarget:self action:@selector(swipe:)];
|
|
139
|
+
_keep = nil;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
- (void)endFinishing:(BOOL)finish speed:(CGFloat)speed {
|
|
143
|
+
id<UIViewControllerContextTransitioning> context = _context;
|
|
144
|
+
ANNTransitionAnimator *animator = _strongAnimator;
|
|
145
|
+
[self stop];
|
|
146
|
+
if (!context || !animator || animator.isCompleted) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
// UIKit reads the speed as it ends its own part of the transition.
|
|
150
|
+
_completionSpeed = speed;
|
|
151
|
+
if (finish) {
|
|
152
|
+
[context finishInteractiveTransition];
|
|
153
|
+
[animator finishWithSpeed:speed];
|
|
154
|
+
} else {
|
|
155
|
+
[context cancelInteractiveTransition];
|
|
156
|
+
[animator rewindWithSpeed:speed];
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// The part of the width the finger went in the direction of the swipe back.
|
|
161
|
+
- (CGFloat)progress {
|
|
162
|
+
UIView *view = _view;
|
|
163
|
+
CGFloat width = view.bounds.size.width;
|
|
164
|
+
if (width <= 0) {
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
CGFloat along = [self alongBack:[_recognizer translationInView:view].x];
|
|
168
|
+
return MIN(MAX(along / width, 0.0), 1.0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// A move across the view, positive in the direction of the swipe back: rightward, leftward in a right-to-left layout.
|
|
172
|
+
- (CGFloat)alongBack:(CGFloat)x {
|
|
173
|
+
BOOL rightToLeft =
|
|
174
|
+
_view.effectiveUserInterfaceLayoutDirection == UIUserInterfaceLayoutDirectionRightToLeft;
|
|
175
|
+
return rightToLeft ? -x : x;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
- (void)stop {
|
|
179
|
+
[_recognizer removeTarget:self action:@selector(swipe:)];
|
|
180
|
+
_context = nil;
|
|
181
|
+
_strongAnimator = nil;
|
|
182
|
+
_keep = nil;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
@end
|
|
@@ -86,10 +86,11 @@ static BOOL ANNIsOwnQuestionOfUIKit(SEL selector) {
|
|
|
86
86
|
|
|
87
87
|
#pragma mark - UIGestureRecognizerDelegate
|
|
88
88
|
|
|
89
|
-
// The order:
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
//
|
|
89
|
+
// The order: which of the stack's swipes plays first (`swipeBackTakes:`: the stack's own while the top's pop follows
|
|
90
|
+
// the finger, UIKit's otherwise), then `popGesture` (with `false` there is neither a swipe nor the hold's event), then
|
|
91
|
+
// one screen or a stack not at rest, then the hold (its event instead of the swipe), then the edge swipe under a hidden
|
|
92
|
+
// bar begins without UIKit's delegate, and otherwise UIKit's delegate decides (its direction, a vertical scroll of the
|
|
93
|
+
// content). A top that isn't the stack's own (a column UIKit's split view moved in) is UIKit's.
|
|
93
94
|
//
|
|
94
95
|
// Every pan in the content is offered to the content swipe, so it is first sorted: a pan that isn't a swipe back
|
|
95
96
|
// (vertical, the other way) is no attempt, and neither is one on a list that scrolls sideways, which takes the pan
|
|
@@ -97,6 +98,10 @@ static BOOL ANNIsOwnQuestionOfUIKit(SEL selector) {
|
|
|
97
98
|
// hidden bar it refuses them all, and the swipe sorts them itself, as the edge swipe begins there without it.
|
|
98
99
|
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
|
|
99
100
|
ANNStackController *stack = _stack;
|
|
101
|
+
// One swipe plays the pop: the stack's own while the top's pop follows the finger, UIKit's otherwise.
|
|
102
|
+
if (stack && ![stack swipeBackTakes:gestureRecognizer]) {
|
|
103
|
+
return NO;
|
|
104
|
+
}
|
|
100
105
|
if (stack && ![stack swipeBackIsUIKits:gestureRecognizer]) {
|
|
101
106
|
if (!_enabled || ![stack swipeBackMayBegin]) {
|
|
102
107
|
return NO;
|
|
@@ -191,6 +196,25 @@ static BOOL ANNIsOwnQuestionOfUIKit(SEL selector) {
|
|
|
191
196
|
return YES;
|
|
192
197
|
}
|
|
193
198
|
|
|
199
|
+
// A list under the stack's own swipe waits for it, as for UIKit's edge swipe: a swipe from the edge goes back, also on
|
|
200
|
+
// a list that scrolls sideways. UIKit's swipes keep UIKit's answer.
|
|
201
|
+
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer
|
|
202
|
+
shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
|
203
|
+
ANNStackController *stack = _stack;
|
|
204
|
+
if (stack && [stack swipeBackIsStacks:gestureRecognizer]) {
|
|
205
|
+
UIView *view = otherGestureRecognizer.view;
|
|
206
|
+
return [view isKindOfClass:UIScrollView.class] &&
|
|
207
|
+
otherGestureRecognizer == ((UIScrollView *)view).panGestureRecognizer;
|
|
208
|
+
}
|
|
209
|
+
id<UIGestureRecognizerDelegate> systemDelegate = _systemDelegate;
|
|
210
|
+
SEL question = @selector(gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:);
|
|
211
|
+
if ([systemDelegate respondsToSelector:question]) {
|
|
212
|
+
return [systemDelegate gestureRecognizer:gestureRecognizer
|
|
213
|
+
shouldBeRequiredToFailByGestureRecognizer:otherGestureRecognizer];
|
|
214
|
+
}
|
|
215
|
+
return NO;
|
|
216
|
+
}
|
|
217
|
+
|
|
194
218
|
#pragma mark - The rest goes to UIKit's delegate
|
|
195
219
|
|
|
196
220
|
- (void)setBarHidden:(BOOL)barHidden {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#import "ANNOptions.h"
|
|
4
4
|
#import "ANNSetStackRootAnimationOptions.h"
|
|
5
5
|
#import "ANNStackAnimationOptions.h"
|
|
6
|
+
#import "ANNStackPopAnimationOptions.h"
|
|
6
7
|
#import "ANNStackPushAnimationOptions.h"
|
|
7
8
|
|
|
8
9
|
NS_ASSUME_NONNULL_BEGIN
|
|
@@ -11,7 +12,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
11
12
|
@interface ANNAnimationOptions : ANNOptions
|
|
12
13
|
|
|
13
14
|
@property(nonatomic, strong) ANNStackPushAnimationOptions *push;
|
|
14
|
-
@property(nonatomic, strong)
|
|
15
|
+
@property(nonatomic, strong) ANNStackPopAnimationOptions *pop;
|
|
15
16
|
@property(nonatomic, strong) ANNSetStackRootAnimationOptions *setStackRoot;
|
|
16
17
|
@property(nonatomic, strong) ANNEnterExitScreenAnimationOptions *setRoot;
|
|
17
18
|
@property(nonatomic, strong) ANNModalAnimationOptions *showModal;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
+ (NSArray<ANNOptionField *> *)optionFields {
|
|
6
6
|
return @[
|
|
7
7
|
[ANNOptionField groupField:@"push" class:ANNStackPushAnimationOptions.class],
|
|
8
|
-
[ANNOptionField groupField:@"pop" class:
|
|
8
|
+
[ANNOptionField groupField:@"pop" class:ANNStackPopAnimationOptions.class],
|
|
9
9
|
[ANNOptionField groupField:@"setStackRoot" class:ANNSetStackRootAnimationOptions.class],
|
|
10
10
|
[ANNOptionField groupField:@"setRoot" class:ANNEnterExitScreenAnimationOptions.class],
|
|
11
11
|
[ANNOptionField groupField:@"showModal" class:ANNModalAnimationOptions.class],
|
package/ios/Options/ANNOptions.h
CHANGED
|
@@ -58,6 +58,13 @@ void ANNCheckOptionClassesFrom(Class root);
|
|
|
58
58
|
/// The group has any value.
|
|
59
59
|
- (BOOL)hasValue;
|
|
60
60
|
|
|
61
|
+
/// Takes the value at `path` out, in place: the JSON keys from this group, the last one a value, an array or a whole
|
|
62
|
+
/// group, which gets what a freshly made group has there (unset). A key of a text's font goes through the text. A
|
|
63
|
+
/// module's group (an empty field table) is walked by its properties named as the keys. NO, and nothing changes, when
|
|
64
|
+
/// a key of the path is none of the group's or a key before the last is no group. Only on a copy of own options
|
|
65
|
+
/// (`changeOwnOptions:`), never on options in use.
|
|
66
|
+
- (BOOL)clearValueAtPath:(NSArray<NSString *> *)path;
|
|
67
|
+
|
|
61
68
|
/// A deep copy: a new group of the same class with these merged into it. The copy shares values, never groups.
|
|
62
69
|
- (instancetype)copy;
|
|
63
70
|
|
|
@@ -398,6 +398,51 @@ static NSArray *ANNCopyItems(NSArray *items) {
|
|
|
398
398
|
return NO;
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
+
// Generic over the whole tree, so that any option an edit takes out (`clearOptions`) goes without a list of options:
|
|
402
|
+
// the field table names each key's property; a module's group, which reads its JSON itself, names its properties as
|
|
403
|
+
// the keys.
|
|
404
|
+
- (BOOL)clearValueAtPath:(NSArray<NSString *> *)path {
|
|
405
|
+
NSString *key = path.firstObject;
|
|
406
|
+
if (key.length == 0) {
|
|
407
|
+
return NO;
|
|
408
|
+
}
|
|
409
|
+
NSArray<ANNOptionField *> *fields = ANNFieldsOf(self.class);
|
|
410
|
+
NSString *property = nil;
|
|
411
|
+
if (fields.count) {
|
|
412
|
+
for (ANNOptionField *field in fields) {
|
|
413
|
+
if ([field.key isEqualToString:key]) {
|
|
414
|
+
property = field.property;
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (!property) {
|
|
419
|
+
// A group without a key (the font of a text) holds keys of its parent's object.
|
|
420
|
+
for (ANNOptionField *field in fields) {
|
|
421
|
+
if (field.kind == ANNOptionFieldKindGroup && !field.key &&
|
|
422
|
+
[[self valueForKey:field.property] clearValueAtPath:path]) {
|
|
423
|
+
return YES;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
return NO;
|
|
427
|
+
}
|
|
428
|
+
} else if ([ANNValuePropertiesOf(self.class) containsObject:key]) {
|
|
429
|
+
property = key;
|
|
430
|
+
} else {
|
|
431
|
+
return NO;
|
|
432
|
+
}
|
|
433
|
+
if (path.count == 1) {
|
|
434
|
+
ANNOptions *fresh = [[self.class alloc] initWithDict:nil];
|
|
435
|
+
[self setValue:[fresh valueForKey:property] forKey:property];
|
|
436
|
+
return YES;
|
|
437
|
+
}
|
|
438
|
+
id value = [self valueForKey:property];
|
|
439
|
+
if (![value isKindOfClass:ANNOptions.class]) {
|
|
440
|
+
return NO;
|
|
441
|
+
}
|
|
442
|
+
NSArray<NSString *> *rest = [path subarrayWithRange:NSMakeRange(1, path.count - 1)];
|
|
443
|
+
return [(ANNOptions *)value clearValueAtPath:rest];
|
|
444
|
+
}
|
|
445
|
+
|
|
401
446
|
- (id)copyWithZone:(NSZone *)zone {
|
|
402
447
|
ANNOptions *copy = [[self.class alloc] initWithDict:nil];
|
|
403
448
|
[copy mergeOptions:self];
|
|
@@ -52,8 +52,7 @@ static void ANNCheckSameValues(void) {
|
|
|
52
52
|
NSDictionary *json = @{
|
|
53
53
|
@"topBar" : @{
|
|
54
54
|
@"title" : @{@"text" : @"Home", @"color" : @(0xFF112233)},
|
|
55
|
-
@"rightButtonColor" :
|
|
56
|
-
@{@"dynamic" : @{@"light" : @(0xFF000000), @"dark" : @(0xFFFFFFFF)}},
|
|
55
|
+
@"rightButtonColor" : @{@"light" : @(0xFF000000), @"dark" : @(0xFFFFFFFF)},
|
|
57
56
|
@"leftButtonColor" : NSNull.null,
|
|
58
57
|
@"rightButtons" : @[ button ],
|
|
59
58
|
@"leftButtons" : @[],
|
|
@@ -75,8 +74,7 @@ static void ANNCheckSameValues(void) {
|
|
|
75
74
|
NSDictionary<NSString *, NSArray *> *changes = @{
|
|
76
75
|
@"a text" : @[ @[ @"topBar", @"title", @"text" ], @"Profile" ],
|
|
77
76
|
@"a color" : @[ @[ @"topBar", @"title", @"color" ], @(0xFF112234) ],
|
|
78
|
-
@"a side of a color pair" :
|
|
79
|
-
@[ @[ @"topBar", @"rightButtonColor", @"dynamic", @"dark" ], @(0xFFEEEEEE) ],
|
|
77
|
+
@"a side of a color pair" : @[ @[ @"topBar", @"rightButtonColor", @"dark" ], @(0xFFEEEEEE) ],
|
|
80
78
|
@"a number" : @[ @[ @"topBar", @"height" ], @45 ],
|
|
81
79
|
@"an image" : @[ @[ @"topBar", @"rightButtons", @0, @"icon", @"system" ], @"heart" ],
|
|
82
80
|
@"a boolean" : @[ @[ @"topBar", @"rightButtons", @0, @"enabled" ], @NO ],
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#import "ANNStackAnimationOptions.h"
|
|
2
|
+
|
|
3
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
+
|
|
5
|
+
/// `animations.pop` (`StackPopAnimationOptions`).
|
|
6
|
+
@interface ANNStackPopAnimationOptions : ANNStackAnimationOptions
|
|
7
|
+
|
|
8
|
+
/// The swipe back plays the screen's own pop under the finger.
|
|
9
|
+
@property(nonatomic, strong) Bool *interactive;
|
|
10
|
+
|
|
11
|
+
@end
|
|
12
|
+
|
|
13
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import "ANNStackPopAnimationOptions.h"
|
|
2
|
+
|
|
3
|
+
@implementation ANNStackPopAnimationOptions
|
|
4
|
+
|
|
5
|
+
+ (NSArray<ANNOptionField *> *)optionFields {
|
|
6
|
+
return [[super optionFields] arrayByAddingObjectsFromArray:@[
|
|
7
|
+
[ANNOptionField boolField:@"interactive"],
|
|
8
|
+
]];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@end
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
NS_ASSUME_NONNULL_BEGIN
|
|
4
4
|
|
|
5
|
-
/// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{
|
|
6
|
-
/// } }` with a number or a platform color on each side,
|
|
7
|
-
/// color". A value it can't read is no value and a warning
|
|
5
|
+
/// Reads `key` of the options' JSON as a color, in the forms JS sends on iOS: an ARGB number, `{ light, dark }` (or an
|
|
6
|
+
/// app's `DynamicColorIOS`, `{ dynamic: { light, dark } }`) with a number or a platform color on each side,
|
|
7
|
+
/// `{ semantic: [...] }`, and `'NoColor'` (or `null`) for "no color". A value it can't read is no value and a warning
|
|
8
|
+
/// in development.
|
|
8
9
|
@interface ColorParser : NSObject
|
|
9
10
|
|
|
10
11
|
+ (Color *)parse:(nullable NSDictionary *)json key:(NSString *)key;
|
|
@@ -49,6 +49,24 @@ static UIColor *ANNOneColor(id value) {
|
|
|
49
49
|
return nil;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// A pair comes as `{ light, dark }`, either side may be left out; an app's own `DynamicColorIOS` as
|
|
53
|
+
// `{ dynamic: { light, dark } }`. Anything else is not a pair.
|
|
54
|
+
static NSDictionary *ANNColorPair(id value) {
|
|
55
|
+
if (![value isKindOfClass:NSDictionary.class]) {
|
|
56
|
+
return nil;
|
|
57
|
+
}
|
|
58
|
+
id dynamic = value[@"dynamic"];
|
|
59
|
+
if (dynamic) {
|
|
60
|
+
return [dynamic isKindOfClass:NSDictionary.class] ? dynamic : nil;
|
|
61
|
+
}
|
|
62
|
+
for (id side in value) {
|
|
63
|
+
if (![side isEqual:@"light"] && ![side isEqual:@"dark"]) {
|
|
64
|
+
return nil;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return value;
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
@implementation ColorParser
|
|
53
71
|
|
|
54
72
|
+ (Color *)parse:(NSDictionary *)json key:(NSString *)key {
|
|
@@ -64,8 +82,8 @@ static UIColor *ANNOneColor(id value) {
|
|
|
64
82
|
return [[Color alloc] initWithColor:color];
|
|
65
83
|
}
|
|
66
84
|
NSString *path = [ANNOptionsReader pathOfKey:key];
|
|
67
|
-
NSDictionary *pair =
|
|
68
|
-
if (
|
|
85
|
+
NSDictionary *pair = ANNColorPair(value);
|
|
86
|
+
if (pair) {
|
|
69
87
|
UIColor *light = [self side:pair[@"light"] name:@"light" path:path];
|
|
70
88
|
UIColor *dark = [self side:pair[@"dark"] name:@"dark" path:path];
|
|
71
89
|
if (light || dark) {
|
|
@@ -77,8 +95,8 @@ static UIColor *ANNOneColor(id value) {
|
|
|
77
95
|
return [[Color alloc] initWithValue:nil];
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
// A side of `{
|
|
81
|
-
//
|
|
98
|
+
// A side of `{ light, dark }`. A side that is not there is a partial pair and is silent (the sides are optional); a
|
|
99
|
+
// side that is there but can't be read is missing too, with a warning.
|
|
82
100
|
+ (UIColor *)side:(id)value name:(NSString *)name path:(NSString *)path {
|
|
83
101
|
if (!value || value == NSNull.null) {
|
|
84
102
|
return nil;
|
|
@@ -8,6 +8,14 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
8
8
|
/// every change on.
|
|
9
9
|
@interface ANNSurfaceHostingView : RCTSurfaceHostingView
|
|
10
10
|
|
|
11
|
+
/// A size transition of the window (a rotation, a new window size) runs until its coordinator completes. Meanwhile React
|
|
12
|
+
/// content takes its new size without animation (`layOutContent:`). Each call ends with its own transition; main queue.
|
|
13
|
+
+ (void)followSizeTransition:(id<UIViewControllerTransitionCoordinator>)coordinator;
|
|
14
|
+
|
|
15
|
+
/// Runs a layout that gives React content a new size: without animation while a size transition runs, as it is
|
|
16
|
+
/// otherwise. Main queue.
|
|
17
|
+
+ (void)layOutContent:(void (^)(void))layout;
|
|
18
|
+
|
|
11
19
|
/// Called at each mount of the hosted surface: after a change of the surface's size, the first one lays the surface
|
|
12
20
|
/// out again, so a render that was on its way during the change doesn't keep the old size's layout.
|
|
13
21
|
- (void)surfaceDidMount;
|
|
@@ -11,6 +11,9 @@ static const void *ANNSurfaceStopperKey = &ANNSurfaceStopperKey;
|
|
|
11
11
|
// The hosting views whose surface stops at its start; each keeps its surface. Main queue.
|
|
12
12
|
static NSMutableSet<ANNSurfaceHostingView *> *ANNHostingViewsStoppingAtStart;
|
|
13
13
|
|
|
14
|
+
// The size transitions of the window that run now. Main queue.
|
|
15
|
+
static NSInteger ANNSizeTransitionCount;
|
|
16
|
+
|
|
14
17
|
// The delegate of a surface its hosting view let go at the limit: the surface keeps it, and it keeps nothing, so
|
|
15
18
|
// neither holds the other alive. A surface that something else still starts (a start already on its way holds its
|
|
16
19
|
// surface; React Native restarts every surface it knows after a reload, holding it) stops at once, instead of running
|
|
@@ -60,6 +63,28 @@ static NSMutableSet<ANNSurfaceHostingView *> *ANNHostingViewsStoppingAtStart;
|
|
|
60
63
|
|
|
61
64
|
#pragma mark - The surface's size
|
|
62
65
|
|
|
66
|
+
// UIKit lays the screens out inside the animation of a rotation or a new window size, and React Native commits and
|
|
67
|
+
// mounts the new layout synchronously inside the layout that gives it the size: every frame of its views animated
|
|
68
|
+
// from the old size to the new one, and what a view draws itself (an SVG, a glass view, text) was drawn once at the new
|
|
69
|
+
// size and stretched along that animation. During such a transition the content takes its new layout at once; the
|
|
70
|
+
// hosting view itself still follows the animation. Other animated size changes (a sheet's detent, a split view's
|
|
71
|
+
// column) keep the content's frames animated, so content pinned to the moving edge rides along with it.
|
|
72
|
+
+ (void)followSizeTransition:(id<UIViewControllerTransitionCoordinator>)coordinator {
|
|
73
|
+
ANNSizeTransitionCount++;
|
|
74
|
+
[coordinator animateAlongsideTransition:nil
|
|
75
|
+
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
|
76
|
+
ANNSizeTransitionCount--;
|
|
77
|
+
}];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
+ (void)layOutContent:(void (^)(void))layout {
|
|
81
|
+
if (ANNSizeTransitionCount > 0) {
|
|
82
|
+
[UIView performWithoutAnimation:layout];
|
|
83
|
+
} else {
|
|
84
|
+
layout();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
63
88
|
// React Native gives the surface its size here. A size change is noted for the next mount (see below). The note is
|
|
64
89
|
// put aside during the call: the change's own commit mounts inside it and must not take an earlier note, or a size
|
|
65
90
|
// that changes every frame (a sheet dragged between detents, a window resized) would lay out three times a frame. A
|
|
@@ -70,7 +95,9 @@ static NSMutableSet<ANNSurfaceHostingView *> *ANNHostingViewsStoppingAtStart;
|
|
|
70
95
|
CGSize maximumSize = surface.maximumSize;
|
|
71
96
|
BOOL sizeChangedBefore = _surfaceSizeChanged;
|
|
72
97
|
_surfaceSizeChanged = NO;
|
|
73
|
-
[
|
|
98
|
+
[ANNSurfaceHostingView layOutContent:^{
|
|
99
|
+
[super layoutSubviews];
|
|
100
|
+
}];
|
|
74
101
|
BOOL sizeChanged = surface && (!CGSizeEqualToSize(minimumSize, surface.minimumSize) ||
|
|
75
102
|
!CGSizeEqualToSize(maximumSize, surface.maximumSize));
|
|
76
103
|
_surfaceSizeChanged = sizeChanged || sizeChangedBefore;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#import "ANNScreenAppearance.h"
|
|
4
4
|
#import "ANNScreenReadiness.h"
|
|
5
5
|
#import "ANNSlotHost.h"
|
|
6
|
+
#import "ANNSurfaceHostingView.h"
|
|
6
7
|
#import "../../cpp/react/renderer/components/ApexNavigationSpec/ANNSlotState.h"
|
|
7
8
|
#import <React/RCTConstants.h>
|
|
8
9
|
#import <React/RCTSurfacePresenter.h>
|
|
@@ -62,12 +63,17 @@
|
|
|
62
63
|
return nil;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
// The slot lays its content out at this size: every change goes to it (rotation, a sheet detent, the bars).
|
|
66
|
+
// The slot lays its content out at this size: every change goes to it (rotation, a sheet detent, the bars). Its state
|
|
67
|
+
// update mounts synchronously, here: during a rotation the content takes its new layout at once, as a screen's surface
|
|
68
|
+
// does (`+[ANNSurfaceHostingView layOutContent:]`).
|
|
66
69
|
- (void)layoutSubviews {
|
|
67
70
|
[super layoutSubviews];
|
|
68
71
|
if (!CGSizeEqualToSize(self.bounds.size, _lastSize)) {
|
|
69
72
|
_lastSize = self.bounds.size;
|
|
70
|
-
|
|
73
|
+
NSString *componentId = _componentId;
|
|
74
|
+
[ANNSurfaceHostingView layOutContent:^{
|
|
75
|
+
[ANNSlotContainers containerDidChange:componentId];
|
|
76
|
+
}];
|
|
71
77
|
}
|
|
72
78
|
}
|
|
73
79
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import "ANNTurboModule.h"
|
|
2
2
|
#import "ANNAppDirection.h"
|
|
3
3
|
#import "ANNArrangementState.h"
|
|
4
|
+
#import "ANNArrangementViewController.h"
|
|
4
5
|
#import "ANNCommandRunner.h"
|
|
5
6
|
#import "ANNComponentViewController.h"
|
|
6
7
|
#import "ANNDeepLinkQueue.h"
|
|
@@ -245,6 +246,51 @@ RCT_EXPORT_MODULE(ANNTurboModule)
|
|
|
245
246
|
}];
|
|
246
247
|
}
|
|
247
248
|
|
|
249
|
+
// `clearOptions` (development: Fast Refresh took options out of a screen that shows): the values at the paths leave
|
|
250
|
+
// the node's own options, so it resolves to what its parents and the defaults give there, and what shows applies the
|
|
251
|
+
// new resolution as after `setDefaultOptions`. The window and the node are found as for `mergeOptions`; an unknown
|
|
252
|
+
// id, or no path that names an option, does nothing, without a log. The user layer stays.
|
|
253
|
+
- (void)clearOptions:(NSString *)componentId paths:(NSArray *)paths {
|
|
254
|
+
NSString *what = ANNWhat(@"clear the options of the screen '%@'", componentId);
|
|
255
|
+
[ANNCommandRunner runVoid:@"clearOptions"
|
|
256
|
+
what:what
|
|
257
|
+
body:^{
|
|
258
|
+
if (!ApexNavigation.mainWindow) {
|
|
259
|
+
[ANNCommandRunner logFailure:@"clearOptions"
|
|
260
|
+
text:ANNErrorTexts.nowhereToShow];
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
UIViewController *node = ANNCommandNode(componentId);
|
|
264
|
+
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 270100
|
|
265
|
+
// The options set with a pair's id are its primary's, as for `mergeOptions`.
|
|
266
|
+
if (@available(iOS 27.1, *)) {
|
|
267
|
+
if ([node isKindOfClass:ANNArrangementViewController.class]) {
|
|
268
|
+
node = ((ANNArrangementViewController *)node).primary;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
#endif
|
|
272
|
+
NSMutableArray<NSArray<NSString *> *> *keyPaths = [NSMutableArray new];
|
|
273
|
+
for (id path in [paths isKindOfClass:NSArray.class] ? paths : @[]) {
|
|
274
|
+
if ([path isKindOfClass:NSString.class] && [path length] > 0) {
|
|
275
|
+
[keyPaths addObject:[path componentsSeparatedByString:@"."]];
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!node || keyPaths.count == 0) {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
__block BOOL cleared = NO;
|
|
282
|
+
[node changeOwnOptions:^(ANNNavigationOptions *options) {
|
|
283
|
+
for (NSArray<NSString *> *keyPath in keyPaths) {
|
|
284
|
+
cleared = [options clearValueAtPath:keyPath] || cleared;
|
|
285
|
+
}
|
|
286
|
+
}];
|
|
287
|
+
// Paths that name no option change nothing that shows.
|
|
288
|
+
if (cleared) {
|
|
289
|
+
[ANNOptionsRepaint applyNewDefaultOptions:ANNDefaultOptions.current];
|
|
290
|
+
}
|
|
291
|
+
}];
|
|
292
|
+
}
|
|
293
|
+
|
|
248
294
|
#pragma mark - Stack
|
|
249
295
|
|
|
250
296
|
- (void)push:(NSString *)commandId
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#include "ANNSlotState.h"
|
|
2
|
+
|
|
3
|
+
namespace facebook::react {
|
|
4
|
+
|
|
5
|
+
std::mutex &ANNSlotContentHeights::mutex() {
|
|
6
|
+
static std::mutex value;
|
|
7
|
+
return value;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
std::unordered_map<std::string, Float> &ANNSlotContentHeights::heights() {
|
|
11
|
+
static std::unordered_map<std::string, Float> value;
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
std::atomic<int> &ANNSlotContentHeights::requested() {
|
|
16
|
+
static std::atomic<int> value{0};
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
} // namespace facebook::react
|
|
@@ -82,20 +82,11 @@ class ANNSlotContentHeights final {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
private:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
static std::unordered_map<std::string, Float> &heights() {
|
|
91
|
-
static std::unordered_map<std::string, Float> value;
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
static std::atomic<int> &requested() {
|
|
96
|
-
static std::atomic<int> value{0};
|
|
97
|
-
return value;
|
|
98
|
-
}
|
|
85
|
+
// One store for the whole app, in ANNSlotState.cpp: on Android the slot's layout runs in the app's library and the
|
|
86
|
+
// native side reads the heights from the library's own, and an inline static would give each its own copy.
|
|
87
|
+
static std::mutex &mutex();
|
|
88
|
+
static std::unordered_map<std::string, Float> &heights();
|
|
89
|
+
static std::atomic<int> &requested();
|
|
99
90
|
};
|
|
100
91
|
|
|
101
92
|
} // namespace facebook::react
|