@apexnative/navigation 1.0.0-alpha.5 → 1.0.0-alpha.6
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/CHANGELOG.md +25 -0
- package/README.md +1 -1
- package/android/CLAUDE.md +14 -12
- package/android/CORE.md +9 -6
- package/android/src/main/java/dev/apexnative/navigation/NavigationActivity.kt +36 -5
- package/android/src/main/java/dev/apexnative/navigation/options/BottomTabsOptions.kt +13 -8
- package/android/src/main/java/dev/apexnative/navigation/react/NavigationTurboModule.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/react/ReloadCoordinator.kt +0 -2
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenEnvironment.kt +177 -0
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenRegions.kt +12 -49
- package/android/src/main/java/dev/apexnative/navigation/react/ScrollEvents.kt +49 -128
- package/android/src/main/java/dev/apexnative/navigation/safearea/SafeAreaView.kt +6 -6
- package/android/src/main/java/dev/apexnative/navigation/utils/ViewExtensions.kt +0 -13
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/arrangement/ArrangementController.kt +9 -7
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsCollapseBehavior.kt +5 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/BottomTabsController.kt +13 -10
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowGeometry.kt +22 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/TabsRoom.kt +0 -5
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ComponentViewController.kt +7 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/component/ScreenSafeArea.kt +29 -11
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/externalcomponent/ExternalComponentViewController.kt +2 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalCoordinator.kt +20 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalSheet.kt +119 -8
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/ModalViews.kt +7 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/modal/SheetBack.kt +42 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/navigator/Navigator.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/parent/ParentController.kt +3 -6
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/PreparedFrame.kt +3 -3
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackCommands.kt +2 -2
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackController.kt +4 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/stack/StackViews.kt +3 -0
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/viewcontroller/ScrollEventListener.kt +6 -9
- package/android/src/main/java/dev/apexnative/navigation/views/animations/AnimationLayer.kt +73 -0
- package/android/src/main/java/dev/apexnative/navigation/views/animations/ValueInterpolation.kt +14 -0
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/BottomTabsLayout.kt +61 -2
- package/android/src/main/java/dev/apexnative/navigation/views/bottomtabs/CustomTabRow.kt +72 -10
- package/android/src/main/java/dev/apexnative/navigation/views/element/ElementTransitions.kt +9 -2
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ElementPictures.kt +11 -0
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/FlightGeometry.kt +318 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ImageMatrixAnimator.kt +18 -20
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/PlainViewAnimator.kt +17 -25
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ReactImageAnimator.kt +15 -22
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/SharedAnimatorFactory.kt +12 -9
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/TextChangeAnimator.kt +84 -35
- package/android/src/main/java/dev/apexnative/navigation/views/element/animators/ViewChangeAnimator.kt +49 -40
- package/docs-md/architecture/transitions.md +4 -2
- package/docs-md/containers/modals.md +2 -0
- package/docs-md/containers/overlays.md +1 -1
- package/docs-md/containers/side-menu.md +1 -1
- package/docs-md/containers/tabs.md +2 -0
- package/docs-md/flows/bottom-sheets.md +1 -1
- package/docs-md/fundamentals/starting-the-app.md +1 -1
- package/docs-md/help/glossary.md +2 -2
- package/docs-md/help/troubleshooting.md +21 -0
- package/docs-md/index.json +12 -0
- package/docs-md/migration/from-expo-router.md +1 -1
- package/docs-md/migration/from-react-native-navigation.md +1 -1
- package/docs-md/migration/from-react-navigation.md +2 -2
- package/docs-md/screen-ui/back-and-gestures.md +14 -0
- package/docs-md/screen-ui/bar-buttons.md +4 -4
- package/docs-md/screen-ui/search.md +1 -1
- package/docs-md/screen-ui/system-ui.md +16 -11
- package/docs-md/screen-ui/toolbar-and-fab.md +2 -2
- package/docs-md/screen-ui/top-bar.md +8 -4
- package/ios/Animations/ANNFrameDriver.mm +2 -0
- package/ios/Animations/Elements/ANNSharedElementFlight.mm +70 -4
- package/ios/Animations/Interpolators/ANNInterpolate.h +2 -30
- package/ios/Animations/Interpolators/ANNTransformParts.h +48 -0
- package/ios/Animations/Interpolators/ANNTransformParts.mm +507 -0
- package/ios/Animations/Interpolators/ANNViewTracks.h +6 -4
- package/ios/Animations/Interpolators/ANNViewTracks.mm +49 -9
- package/ios/CLAUDE.md +17 -15
- package/ios/CORE.md +24 -20
- package/ios/Contract/ANNCommandRunner.h +8 -7
- package/ios/Contract/ANNCommandRunner.mm +21 -27
- package/ios/Contract/ANNLayoutNode.h +3 -3
- package/ios/Contract/ANNLayoutReader.h +3 -3
- package/ios/Core/ANNJavaScriptCalls.h +11 -12
- package/ios/Core/ANNJavaScriptCalls.mm +7 -16
- package/ios/Core/ANNReloadCoordinator.mm +4 -8
- package/ios/Core/ApexNavigation+Internal.h +1 -1
- package/ios/Layouts/Arrangement/ANNArrangementState.h +2 -17
- package/ios/Layouts/Arrangement/ANNArrangementState.mm +0 -38
- package/ios/Layouts/Arrangement/ANNArrangementViewController.mm +4 -4
- package/ios/Layouts/Screen/ANNComponentViewController.h +2 -2
- package/ios/Layouts/Screen/ANNComponentViewController.mm +9 -22
- package/ios/Layouts/Screen/ANNPreviewInteraction.h +7 -4
- package/ios/Layouts/Screen/ANNPreviewInteraction.mm +72 -10
- package/ios/Layouts/Screen/ANNScreenEnvironment.h +63 -0
- package/ios/Layouts/Screen/ANNScreenEnvironment.mm +181 -0
- package/ios/Layouts/Screen/ANNScreenRegions.h +3 -18
- package/ios/Layouts/Screen/ANNScreenRegions.mm +0 -38
- package/ios/Layouts/Screen/ANNScreenSafeArea.h +4 -9
- package/ios/Layouts/Screen/ANNScreenSafeArea.mm +14 -16
- package/ios/Layouts/Stack/ANNPreviewPush.h +7 -6
- package/ios/Layouts/Stack/ANNPreviewPush.mm +57 -5
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.h +12 -1
- package/ios/Layouts/Tabs/ANNBottomTabsAccessoryView.mm +23 -0
- package/ios/Layouts/Tabs/ANNBottomTabsController.mm +2 -1
- package/ios/Layouts/Tabs/ANNBottomTabsCustomRow.mm +8 -3
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.h +4 -0
- package/ios/Layouts/Tabs/ANNBottomTabsPresenter.mm +123 -36
- package/ios/Layouts/Tabs/ANNTabsDesign.h +6 -5
- package/ios/Layouts/Tabs/ANNTabsDesign.mm +9 -1
- package/ios/Layouts/Tabs/UITabBar+utils.h +5 -5
- package/ios/Layouts/Tabs/UITabBar+utils.mm +32 -13
- package/ios/Options/ANNBottomTabsOptions.h +2 -0
- package/ios/Options/ANNBottomTabsOptions.mm +1 -0
- package/ios/Options/ANNOptions.h +18 -0
- package/ios/Options/ANNOptions.mm +112 -1
- package/ios/Options/ANNOptionsReader.mm +110 -0
- package/ios/Options/Params/ANNParam.h +15 -0
- package/ios/Options/Params/ANNParam.mm +61 -0
- package/ios/Options/Params/Color.h +3 -0
- package/ios/Options/Params/Color.mm +21 -0
- package/ios/Options/Params/Image.h +4 -0
- package/ios/Options/Params/Image.mm +20 -0
- package/ios/React/ANNReactView.mm +6 -25
- package/ios/React/ANNSafeAreaViewComponentView.mm +26 -15
- package/ios/React/ANNSurfaceHostingView.h +6 -0
- package/ios/React/ANNSurfaceHostingView.mm +116 -0
- package/ios/TopBar/ANNBackButtonPresenter.mm +6 -5
- package/ios/TopBar/ANNBarAppearance.h +2 -1
- package/ios/TopBar/ANNBarAppearance.mm +111 -30
- package/ios/TopBar/ANNBarBackgroundComponent.mm +16 -0
- package/ios/TopBar/ANNBarButtonComponentView.h +4 -4
- package/ios/TopBar/ANNBarButtonComponentView.mm +1 -1
- package/ios/TopBar/ANNBarButtons.h +5 -3
- package/ios/TopBar/ANNBarButtons.mm +186 -17
- package/ios/TopBar/ANNLargeTitlePresenter.mm +24 -4
- package/ios/TopBar/ANNMainList.h +3 -0
- package/ios/TopBar/ANNMainList.mm +4 -0
- package/ios/TopBar/ANNScrollEdgeEffects.h +22 -6
- package/ios/TopBar/ANNScrollEdgeEffects.mm +123 -16
- package/ios/TopBar/ANNSearchPresenter.mm +2 -2
- package/ios/TopBar/ANNSearchResultsViewController.mm +5 -5
- package/ios/TopBar/ANNTopBarPresenter.h +3 -2
- package/ios/TopBar/ANNTopBarPresenter.mm +95 -22
- package/ios/TurboModules/ANNTurboModule.mm +20 -16
- package/lib/module/ARCHITECTURE.md +8 -4
- package/lib/module/api/hooks/useSafeAreaInsets.js +4 -3
- package/lib/module/api/hooks/useSafeAreaInsets.js.map +1 -1
- package/lib/module/api/options/OptionsChain.js +2 -2
- package/lib/module/engine/commands.js +21 -1
- package/lib/module/engine/commands.js.map +1 -1
- package/lib/module/engine/screens.js +29 -4
- package/lib/module/engine/screens.js.map +1 -1
- package/lib/typescript/api/app/createNavigation.d.ts +3 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts +4 -3
- package/lib/typescript/api/hooks/useSafeAreaInsets.d.ts.map +1 -1
- package/lib/typescript/api/options/OptionsChain.d.ts +2 -2
- package/lib/typescript/engine/commands.d.ts.map +1 -1
- package/lib/typescript/engine/screens.d.ts +20 -6
- package/lib/typescript/engine/screens.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/animations.d.ts +12 -7
- package/lib/typescript/interfaces/options/animations.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/bottomTabs.d.ts +18 -5
- package/lib/typescript/interfaces/options/bottomTabs.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/buttons.d.ts +8 -8
- package/lib/typescript/interfaces/options/system.d.ts +8 -5
- package/lib/typescript/interfaces/options/system.d.ts.map +1 -1
- package/lib/typescript/interfaces/options/toolbar.d.ts +2 -2
- package/lib/typescript/interfaces/options/topBar.d.ts +32 -29
- package/lib/typescript/interfaces/options/topBar.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/ARCHITECTURE.md +8 -4
- package/src/api/app/createNavigation.ts +3 -3
- package/src/api/hooks/useSafeAreaInsets.ts +4 -3
- package/src/api/options/OptionsChain.ts +2 -2
- package/src/engine/commands.ts +30 -6
- package/src/engine/screens.tsx +29 -8
- package/src/interfaces/options/animations.ts +12 -7
- package/src/interfaces/options/bottomTabs.ts +18 -5
- package/src/interfaces/options/buttons.ts +8 -8
- package/src/interfaces/options/system.ts +8 -5
- package/src/interfaces/options/toolbar.ts +2 -2
- package/src/interfaces/options/topBar.ts +32 -29
- package/android/src/main/java/dev/apexnative/navigation/react/ArrangementStates.kt +0 -42
- package/android/src/main/java/dev/apexnative/navigation/react/ScreenInsets.kt +0 -70
- package/android/src/main/java/dev/apexnative/navigation/viewcontrollers/bottomtabs/CustomTabRowPlacer.kt +0 -223
- package/ios/Layouts/Screen/ANNScreenInsets.h +0 -30
- package/ios/Layouts/Screen/ANNScreenInsets.mm +0 -70
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import "ANNBarButtonComponentView.h"
|
|
3
3
|
#import "ANNIconButtonImage.h"
|
|
4
4
|
#import "ANNShowTextButton.h"
|
|
5
|
+
#import "ANNTabsDesign.h"
|
|
5
6
|
#import "ANNUIBarButtonItem.h"
|
|
6
7
|
#import "ANNUserChoice.h"
|
|
7
8
|
#import "UIViewController+LayoutProtocol.h"
|
|
@@ -301,6 +302,27 @@ static BOOL ANNHasComponent(ANNButtonOptions *button) {
|
|
|
301
302
|
[button.component.componentId withDefault:nil].length > 0;
|
|
302
303
|
}
|
|
303
304
|
|
|
305
|
+
// The image an icon loads, for telling builds apart (NSNull without one): a source of a file, data or Metro's server
|
|
306
|
+
// loads through the shared cache, which gives the same image until the file at its path changes or a failed load
|
|
307
|
+
// succeeds. An SF Symbol is the system's: its name in the options says it all.
|
|
308
|
+
static id ANNLoadedImageOf(Image *icon) {
|
|
309
|
+
ANNImageSource *source = icon.source;
|
|
310
|
+
if (!icon.hasValue || (source && source.kind == ANNImageSourceKindSymbol)) {
|
|
311
|
+
return NSNull.null;
|
|
312
|
+
}
|
|
313
|
+
return [icon withDefault:nil] ?: NSNull.null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// The images the items of a menu load (an item with `sfSymbol` shows the symbol), at any depth.
|
|
317
|
+
static void ANNAddMenuImages(NSArray<ANNMenuItemOptions *> *items, NSMutableArray *loaded) {
|
|
318
|
+
for (ANNMenuItemOptions *item in items) {
|
|
319
|
+
if (!item.sfSymbol.hasValue) {
|
|
320
|
+
[loaded addObject:ANNLoadedImageOf(item.icon)];
|
|
321
|
+
}
|
|
322
|
+
ANNAddMenuImages(item.items, loaded);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
304
326
|
// The image button's background: its own `iconBackground.color`, else (only for an image icon) the side's.
|
|
305
327
|
static UIColor *ANNIconBackgroundOf(ANNButtonOptions *button, Color *sideBackground) {
|
|
306
328
|
UIColor *own = ANNColorOf(button.iconBackground.color);
|
|
@@ -394,6 +416,60 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
394
416
|
@implementation ANNButtonPress
|
|
395
417
|
@end
|
|
396
418
|
|
|
419
|
+
// The same objects, one by one: what a build read by identity (a loaded image, a React button's slot) and what it put
|
|
420
|
+
// on the navigation item. A string (the item's customization id) by its value.
|
|
421
|
+
static BOOL ANNSameObjects(NSArray *a, NSArray *b) {
|
|
422
|
+
if (a.count != b.count) {
|
|
423
|
+
return NO;
|
|
424
|
+
}
|
|
425
|
+
for (NSUInteger index = 0; index < a.count; index++) {
|
|
426
|
+
id mine = a[index];
|
|
427
|
+
id theirs = b[index];
|
|
428
|
+
if (mine != theirs && !([mine isKindOfClass:NSString.class] && [mine isEqual:theirs])) {
|
|
429
|
+
return NO;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return YES;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Everything the build of one side reads: the side's buttons, its colors and the customization id of the middle by
|
|
436
|
+
// value; the images it loads and the slots of its React buttons by identity (a file written again at its path loads as
|
|
437
|
+
// another image, a reload gives other roots); the traits it builds under (a text button's font follows the text size,
|
|
438
|
+
// a drawn image the scale and the style).
|
|
439
|
+
@interface ANNSideInputs : NSObject
|
|
440
|
+
@property(nonatomic, strong, nullable) NSArray<ANNButtonOptions *> *buttons;
|
|
441
|
+
@property(nonatomic, strong, nullable) Color *color;
|
|
442
|
+
@property(nonatomic, strong, nullable) Color *disabledColor;
|
|
443
|
+
@property(nonatomic, strong, nullable) Color *background;
|
|
444
|
+
@property(nonatomic, strong, nullable) Text *customizationId;
|
|
445
|
+
@property(nonatomic, copy) NSArray *loaded;
|
|
446
|
+
@property(nonatomic, copy) NSArray *traits;
|
|
447
|
+
@end
|
|
448
|
+
|
|
449
|
+
@implementation ANNSideInputs
|
|
450
|
+
|
|
451
|
+
- (BOOL)isSameAs:(ANNSideInputs *)other {
|
|
452
|
+
return ANNOptionValuesSame(_buttons, other.buttons) &&
|
|
453
|
+
ANNOptionValuesSame(_color, other.color) &&
|
|
454
|
+
ANNOptionValuesSame(_disabledColor, other.disabledColor) &&
|
|
455
|
+
ANNOptionValuesSame(_background, other.background) &&
|
|
456
|
+
ANNOptionValuesSame(_customizationId, other.customizationId) &&
|
|
457
|
+
ANNSameObjects(_loaded, other.loaded) && [_traits isEqualToArray:other.traits];
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@end
|
|
461
|
+
|
|
462
|
+
// The last build of one side: what it read and what the navigation item held right after it.
|
|
463
|
+
@interface ANNSideBuild : NSObject
|
|
464
|
+
@property(nonatomic, strong) ANNSideInputs *inputs;
|
|
465
|
+
// The side went in as groups (`leadingItemGroups`, …), not as a list of items.
|
|
466
|
+
@property(nonatomic) BOOL grouped;
|
|
467
|
+
@property(nonatomic, copy) NSArray *placed;
|
|
468
|
+
@end
|
|
469
|
+
|
|
470
|
+
@implementation ANNSideBuild
|
|
471
|
+
@end
|
|
472
|
+
|
|
397
473
|
@implementation ANNBarButtons {
|
|
398
474
|
__weak UIViewController *_screen;
|
|
399
475
|
ANNReactComponentRegistry *_registry;
|
|
@@ -402,6 +478,8 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
402
478
|
NSMutableDictionary<NSString *, NSNumber *> *_selected;
|
|
403
479
|
// What a press of each sender (an item, a button of an item) means.
|
|
404
480
|
NSMapTable<id, ANNButtonPress *> *_presses;
|
|
481
|
+
// The last build of each side, by the side.
|
|
482
|
+
NSMutableDictionary<NSNumber *, ANNSideBuild *> *_builds;
|
|
405
483
|
}
|
|
406
484
|
|
|
407
485
|
- (instancetype)initWithScreen:(UIViewController *)screen
|
|
@@ -412,12 +490,16 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
412
490
|
_slots = [NSMutableDictionary new];
|
|
413
491
|
_selected = [NSMutableDictionary new];
|
|
414
492
|
_presses = [NSMapTable weakToStrongObjectsMapTable];
|
|
493
|
+
_builds = [NSMutableDictionary new];
|
|
415
494
|
}
|
|
416
495
|
return self;
|
|
417
496
|
}
|
|
418
497
|
|
|
419
498
|
#pragma mark - The sides
|
|
420
499
|
|
|
500
|
+
// From iOS 26 UIKit animates any change of the bar's items, also to equal ones and with `animated:NO`: new items on
|
|
501
|
+
// each appearance would morph, close an open menu and drop a pressed state. So a side is built again only when what
|
|
502
|
+
// its build reads changed, or when the item no longer holds what the last build put there.
|
|
421
503
|
- (void)applySide:(ANNBarButtonsSide)side
|
|
422
504
|
options:(ANNNavigationOptions *)options
|
|
423
505
|
animated:(BOOL)animated {
|
|
@@ -426,12 +508,92 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
426
508
|
return;
|
|
427
509
|
}
|
|
428
510
|
UINavigationItem *navigationItem = screen.navigationItem;
|
|
511
|
+
ANNSideInputs *inputs = [self inputsOfSide:side options:options];
|
|
512
|
+
ANNSideBuild *last = _builds[@(side)];
|
|
513
|
+
if (last && [last.inputs isSameAs:inputs]) {
|
|
514
|
+
NSArray *placed = [self placedOf:side grouped:last.grouped item:navigationItem];
|
|
515
|
+
if (ANNSameObjects(last.placed, placed)) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
ANNSideBuild *build = [ANNSideBuild new];
|
|
520
|
+
build.inputs = inputs;
|
|
521
|
+
build.grouped = [self placeSide:side options:options item:navigationItem animated:animated];
|
|
522
|
+
build.placed = [self placedOf:side grouped:build.grouped item:navigationItem];
|
|
523
|
+
_builds[@(side)] = build;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
- (ANNSideInputs *)inputsOfSide:(ANNBarButtonsSide)side options:(ANNNavigationOptions *)options {
|
|
527
|
+
ANNTopBarOptions *topBar = options.topBar;
|
|
528
|
+
ANNSideInputs *inputs = [ANNSideInputs new];
|
|
529
|
+
inputs.buttons = ANNButtonsOfSide(options, side);
|
|
530
|
+
inputs.color = ANNSideColor(topBar, side);
|
|
531
|
+
inputs.disabledColor = ANNSideDisabledColor(topBar, side);
|
|
532
|
+
inputs.background = ANNSideBackgroundColor(topBar, side);
|
|
533
|
+
inputs.customizationId = side == ANNBarButtonsSideCenter ? topBar.customizationId : nil;
|
|
534
|
+
NSMutableArray *loaded = [NSMutableArray new];
|
|
535
|
+
for (ANNButtonOptions *button in inputs.buttons ?: @[]) {
|
|
536
|
+
if (ANNHasComponent(button)) {
|
|
537
|
+
[loaded addObject:[self slotFor:button] ?: NSNull.null];
|
|
538
|
+
}
|
|
539
|
+
[loaded addObject:ANNLoadedImageOf(button.icon)];
|
|
540
|
+
ANNAddMenuImages(button.menu.items, loaded);
|
|
541
|
+
}
|
|
542
|
+
inputs.loaded = loaded;
|
|
543
|
+
UITraitCollection *traits = UITraitCollection.currentTraitCollection;
|
|
544
|
+
inputs.traits = @[
|
|
545
|
+
@(traits.userInterfaceStyle), @(traits.displayScale),
|
|
546
|
+
traits.preferredContentSizeCategory ?: @"", @(traits.legibilityWeight)
|
|
547
|
+
];
|
|
548
|
+
return inputs;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// What the navigation item holds of `side` now, as the side's build put it there.
|
|
552
|
+
- (NSArray *)placedOf:(ANNBarButtonsSide)side
|
|
553
|
+
grouped:(BOOL)grouped
|
|
554
|
+
item:(UINavigationItem *)navigationItem {
|
|
555
|
+
NSMutableArray *placed = [NSMutableArray new];
|
|
556
|
+
BOOL left = side == ANNBarButtonsSideLeft;
|
|
557
|
+
switch (side) {
|
|
558
|
+
case ANNBarButtonsSideLeft:
|
|
559
|
+
case ANNBarButtonsSideRight: {
|
|
560
|
+
NSArray *shown =
|
|
561
|
+
left ? navigationItem.leftBarButtonItems : navigationItem.rightBarButtonItems;
|
|
562
|
+
if (@available(iOS 16.0, *)) {
|
|
563
|
+
if (grouped) {
|
|
564
|
+
shown = left ? navigationItem.leadingItemGroups
|
|
565
|
+
: navigationItem.trailingItemGroups;
|
|
566
|
+
}
|
|
567
|
+
if (!left) {
|
|
568
|
+
[placed addObject:navigationItem.pinnedTrailingGroup ?: NSNull.null];
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
[placed addObjectsFromArray:shown ?: @[]];
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case ANNBarButtonsSideCenter:
|
|
575
|
+
if (@available(iOS 16.0, *)) {
|
|
576
|
+
[placed addObjectsFromArray:navigationItem.centerItemGroups ?: @[]];
|
|
577
|
+
[placed addObject:navigationItem.customizationIdentifier ?: NSNull.null];
|
|
578
|
+
}
|
|
579
|
+
break;
|
|
580
|
+
case ANNBarButtonsSideToolbar:
|
|
581
|
+
break;
|
|
582
|
+
}
|
|
583
|
+
return placed;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Builds `side` whole and puts it on the item; YES when it went in as groups.
|
|
587
|
+
- (BOOL)placeSide:(ANNBarButtonsSide)side
|
|
588
|
+
options:(ANNNavigationOptions *)options
|
|
589
|
+
item:(UINavigationItem *)navigationItem
|
|
590
|
+
animated:(BOOL)animated {
|
|
429
591
|
NSArray<ANNBuiltButton *> *built = [self buildSide:side options:options];
|
|
430
592
|
if (side == ANNBarButtonsSideCenter) {
|
|
431
593
|
if (@available(iOS 16.0, *)) {
|
|
432
594
|
[self setCenter:built options:options item:navigationItem];
|
|
433
595
|
}
|
|
434
|
-
return;
|
|
596
|
+
return YES;
|
|
435
597
|
}
|
|
436
598
|
NSMutableArray<ANNBuiltButton *> *placed = [built mutableCopy];
|
|
437
599
|
if (side == ANNBarButtonsSideRight) {
|
|
@@ -440,26 +602,28 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
440
602
|
}
|
|
441
603
|
}
|
|
442
604
|
if (@available(iOS 26.0, *)) {
|
|
443
|
-
if ([self hasGroupOfSeveral:placed]) {
|
|
605
|
+
if (ANNTabsDesign.isGlass && [self hasGroupOfSeveral:placed]) {
|
|
444
606
|
NSArray<UIBarButtonItemGroup *> *groups = [self groupsOf:placed];
|
|
445
607
|
if (side == ANNBarButtonsSideLeft) {
|
|
446
608
|
navigationItem.leadingItemGroups = groups;
|
|
447
609
|
} else {
|
|
448
610
|
navigationItem.trailingItemGroups = groups.reverseObjectEnumerator.allObjects;
|
|
449
611
|
}
|
|
450
|
-
return;
|
|
612
|
+
return YES;
|
|
451
613
|
}
|
|
452
614
|
}
|
|
453
615
|
NSMutableArray<UIBarButtonItem *> *items = [NSMutableArray new];
|
|
454
616
|
for (ANNBuiltButton *button in placed) {
|
|
455
617
|
if (@available(iOS 26.0, *)) {
|
|
456
618
|
// Each button its own capsule: a zero-width space between neighbours, and the right ones never share the
|
|
457
|
-
// capsule of "•••".
|
|
458
|
-
if (
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
619
|
+
// capsule of "•••". The compatibility mode has no capsules.
|
|
620
|
+
if (ANNTabsDesign.isGlass) {
|
|
621
|
+
if (items.count > 0) {
|
|
622
|
+
[items addObject:[UIBarButtonItem fixedSpaceItemOfWidth:0]];
|
|
623
|
+
}
|
|
624
|
+
if (side == ANNBarButtonsSideRight) {
|
|
625
|
+
button.item.sharesBackground = NO;
|
|
626
|
+
}
|
|
463
627
|
}
|
|
464
628
|
}
|
|
465
629
|
[items addObject:button.item];
|
|
@@ -469,6 +633,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
469
633
|
} else {
|
|
470
634
|
[navigationItem setRightBarButtonItems:items animated:animated];
|
|
471
635
|
}
|
|
636
|
+
return NO;
|
|
472
637
|
}
|
|
473
638
|
|
|
474
639
|
- (NSArray<ANNBuiltButton *> *)buildSide:(ANNBarButtonsSide)side
|
|
@@ -559,7 +724,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
559
724
|
NSMutableArray<UIBarButtonItem *> *items = [NSMutableArray new];
|
|
560
725
|
for (ANNBuiltButton *button in run) {
|
|
561
726
|
if (@available(iOS 26.0, *)) {
|
|
562
|
-
if (run.count == 1) {
|
|
727
|
+
if (run.count == 1 && ANNTabsDesign.isGlass) {
|
|
563
728
|
button.item.sharesBackground = NO;
|
|
564
729
|
}
|
|
565
730
|
}
|
|
@@ -667,7 +832,7 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
667
832
|
BOOL icon = kind == ANNBarButtonKindSymbol || kind == ANNBarButtonKindImage ||
|
|
668
833
|
kind == ANNBarButtonKindImageButton || kind == ANNBarButtonKindShowText;
|
|
669
834
|
NSString *group = icon ? [button.group withDefault:nil] : nil;
|
|
670
|
-
if (
|
|
835
|
+
if (ANNTabsDesign.isGlass) {
|
|
671
836
|
// Only icons of one group share a capsule (the toolbar has no groups): any other neighbours get a
|
|
672
837
|
// zero-width space, unless a space of the options is there already.
|
|
673
838
|
BOOL sameGroup = group.length > 0 && [group isEqualToString:previousIconGroup];
|
|
@@ -835,10 +1000,12 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
835
1000
|
}
|
|
836
1001
|
BOOL prominent = [[button.style withDefault:nil] isEqualToString:@"prominent"];
|
|
837
1002
|
if (prominent) {
|
|
1003
|
+
// The capsule of the glass; the design before it (and its compatibility mode) has the bold `done` style.
|
|
1004
|
+
item.style = UIBarButtonItemStyleDone;
|
|
838
1005
|
if (@available(iOS 26.0, *)) {
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1006
|
+
if (ANNTabsDesign.isGlass) {
|
|
1007
|
+
item.style = UIBarButtonItemStyleProminent;
|
|
1008
|
+
}
|
|
842
1009
|
}
|
|
843
1010
|
}
|
|
844
1011
|
// The color: the tint, and for a plain button also the text's; `prominent` takes it only into the tint, and a font
|
|
@@ -1021,9 +1188,11 @@ static UIBarButtonSystemItem ANNSystemItemOf(NSString *name) {
|
|
|
1021
1188
|
kind:(ANNBarButtonKind)kind
|
|
1022
1189
|
to:(UIBarButtonItem *)item {
|
|
1023
1190
|
if (@available(iOS 26.0, *)) {
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1191
|
+
if (ANNTabsDesign.isGlass) {
|
|
1192
|
+
BOOL ownBackground =
|
|
1193
|
+
kind == ANNBarButtonKindComponent || kind == ANNBarButtonKindImageButton;
|
|
1194
|
+
item.hidesSharedBackground = [button.hideSharedBackground withDefault:ownBackground];
|
|
1195
|
+
}
|
|
1027
1196
|
}
|
|
1028
1197
|
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 270000
|
|
1029
1198
|
if (@available(iOS 27.0, *)) {
|
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
#import "ANNBarAppearance.h"
|
|
4
4
|
#import "ANNMainList.h"
|
|
5
5
|
#import "ANNScreenBars.h"
|
|
6
|
+
#import "ANNTabsDesign.h"
|
|
7
|
+
|
|
8
|
+
// The glass design of iOS 26 draws the large title inside the list the bar follows: the places, fills and bands of
|
|
9
|
+
// this file are for it. The compatibility mode keeps the title in the bar, as before iOS 26 (`ANNTabsDesign`).
|
|
10
|
+
static BOOL ANNTitleInList(void) {
|
|
11
|
+
return ANNTabsDesign.isGlass;
|
|
12
|
+
}
|
|
6
13
|
|
|
7
14
|
// How far the color reaches above the list's content: as far as a pull can show.
|
|
8
15
|
static const CGFloat ANNListFillHeight = 2000;
|
|
@@ -58,10 +65,8 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
58
65
|
![options.topBar.largeTitle.collapseOnScroll withDefault:YES]) {
|
|
59
66
|
return NO;
|
|
60
67
|
}
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
return NO;
|
|
64
|
-
}
|
|
68
|
+
if (ANNTitleInList() && ANNAppDirection.reactNativeIsRightToLeft) {
|
|
69
|
+
return NO;
|
|
65
70
|
}
|
|
66
71
|
return YES;
|
|
67
72
|
}
|
|
@@ -132,6 +137,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
132
137
|
- (void)screenInPop:(ANNNavigationOptions *)options
|
|
133
138
|
list:(UIScrollView *)list
|
|
134
139
|
inlineInRow:(BOOL)inlineInRow {
|
|
140
|
+
if (!ANNTitleInList()) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
135
143
|
if (@available(iOS 26.0, *)) {
|
|
136
144
|
UIViewController *screen = _screen;
|
|
137
145
|
UIView *screenView = screen.viewIfLoaded;
|
|
@@ -168,6 +176,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
168
176
|
inlineInRow:(BOOL)inlineInRow
|
|
169
177
|
top:(CGFloat)top
|
|
170
178
|
stackTop:(CGFloat)stackTop {
|
|
179
|
+
if (!ANNTitleInList()) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
171
182
|
if (@available(iOS 26.0, *)) {
|
|
172
183
|
UIView *screenView = _screen.viewIfLoaded;
|
|
173
184
|
if (!screenView || inlineInRow || !ANNLargeTitleCanShow(options) || top - stackTop <= 0) {
|
|
@@ -282,6 +293,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
282
293
|
list:(UIScrollView *)list
|
|
283
294
|
inlineInRow:(BOOL)inlineInRow
|
|
284
295
|
backgroundComponent:(BOOL)backgroundComponent {
|
|
296
|
+
if (!ANNTitleInList()) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
285
299
|
if (@available(iOS 26.0, *)) {
|
|
286
300
|
UIViewController *screen = _screen;
|
|
287
301
|
UIColor *fill = [ANNBarAppearance solidColorOf:options.topBar.background.color
|
|
@@ -320,6 +334,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
320
334
|
- (void)fillTitlePlaceFrom:(CGFloat)oldBarBottom
|
|
321
335
|
list:(UIScrollView *)list
|
|
322
336
|
options:(ANNNavigationOptions *)options {
|
|
337
|
+
if (!ANNTitleInList()) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
323
340
|
if (@available(iOS 26.0, *)) {
|
|
324
341
|
[self removeTitlePlaceFill];
|
|
325
342
|
if (!ANNLargeTitleCanShow(options) || [ANNLargeTitlePresenter collapsesIntoList:options]) {
|
|
@@ -334,6 +351,9 @@ static UINavigationItemLargeTitleDisplayMode ANNItemModeOf(NSString *mode) {
|
|
|
334
351
|
// The pushed screen's content may render only during the push, so its list mounts later than the push starts: the
|
|
335
352
|
// fill is placed again at each mount and layout until the screen appeared.
|
|
336
353
|
- (void)updateTitlePlaceFill:(ANNNavigationOptions *)options list:(UIScrollView *)list {
|
|
354
|
+
if (!ANNTitleInList()) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
337
357
|
if (@available(iOS 26.0, *)) {
|
|
338
358
|
UIViewController *screen = _screen;
|
|
339
359
|
UIView *screenView = screen.viewIfLoaded;
|
package/ios/TopBar/ANNMainList.h
CHANGED
|
@@ -23,6 +23,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
23
23
|
/// search takes it, or what is in it, for the screen's list.
|
|
24
24
|
+ (void)excludeView:(UIView *)view;
|
|
25
25
|
|
|
26
|
+
/// The view is one of those (`excludeView:`).
|
|
27
|
+
+ (BOOL)isExcludedView:(UIView *)view;
|
|
28
|
+
|
|
26
29
|
@end
|
|
27
30
|
|
|
28
31
|
NS_ASSUME_NONNULL_END
|
|
@@ -22,6 +22,10 @@ static const CGFloat ANNMainListTolerance = 1;
|
|
|
22
22
|
objc_setAssociatedObject(view, ANNMainListExcludedKey, @YES, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
+ (BOOL)isExcludedView:(UIView *)view {
|
|
26
|
+
return objc_getAssociatedObject(view, ANNMainListExcludedKey) != nil;
|
|
27
|
+
}
|
|
28
|
+
|
|
25
29
|
- (UIScrollView *)keptList {
|
|
26
30
|
UIScrollView *list = _list;
|
|
27
31
|
UIView *screenView = _screenView;
|
|
@@ -5,9 +5,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
5
5
|
|
|
6
6
|
/// The scroll edge effect of a screen's lists (iOS 26; `scrollEdgeEffect.*`): each edge's own `hidden` / `style` beat
|
|
7
7
|
/// the general ones, through the public API. No top effect under a solid `background.color` (not a clear one) or
|
|
8
|
-
/// `background.component` unless the screen set its own for the top or for all edges. The
|
|
9
|
-
///
|
|
10
|
-
///
|
|
8
|
+
/// `background.component` unless the screen set its own for the top or for all edges. The lists are the scroll views
|
|
9
|
+
/// of the screen's view down to 16 levels, outside hidden views (`display: none`), not inside a list that scrolls
|
|
10
|
+
/// vertically (its rows) or a text view; a scroll view that doesn't (a pager, a carousel) is looked into for its
|
|
11
|
+
/// lists. The screen remembers the lists it set and gives them the system values back when it leaves, at its destroy
|
|
12
|
+
/// and when they leave its view for no screen (Fabric's pool; in another screen they may be that screen's already).
|
|
13
|
+
/// Before iOS 26 nothing. Main queue.
|
|
11
14
|
@interface ANNScrollEdgeEffects : NSObject
|
|
12
15
|
|
|
13
16
|
- (instancetype)initWithScreenView:(UIView *)screenView;
|
|
@@ -15,13 +18,26 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
15
18
|
- (instancetype)init NS_UNAVAILABLE;
|
|
16
19
|
+ (instancetype)new NS_UNAVAILABLE;
|
|
17
20
|
|
|
18
|
-
/// At the appearance and at a merge:
|
|
21
|
+
/// At the appearance and at a merge: the lists of the screen's view, set again whole. Without a walk (only the known
|
|
22
|
+
/// lists still in the view set again) when the options it reads and the traits are the same as at the last walk and
|
|
23
|
+
/// no content mounted since, while the screen's content tells all its mounts (`mountsReported`) and that walk found
|
|
24
|
+
/// no pager.
|
|
19
25
|
- (void)applyToAllWithOptions:(ANNNavigationOptions *)options;
|
|
20
26
|
|
|
21
|
-
///
|
|
27
|
+
/// The screen's content tells each of its mounts, also out of the window (a React root): without it every call walks
|
|
28
|
+
/// the view.
|
|
29
|
+
@property(nonatomic) BOOL mountsReported;
|
|
30
|
+
|
|
31
|
+
/// After a mount of the content: the main list only; lists React took out of the screen get the system values, and
|
|
32
|
+
/// the next `applyToAllWithOptions:` walks the view again.
|
|
22
33
|
- (void)applyToList:(nullable UIScrollView *)list options:(ANNNavigationOptions *)options;
|
|
23
34
|
|
|
24
|
-
/// The lists
|
|
35
|
+
/// The screen left: its lists get the system values back (a list Fabric recycles from it comes clean), and stay
|
|
36
|
+
/// known for its next appearance.
|
|
37
|
+
- (void)screenDidDisappear;
|
|
38
|
+
|
|
39
|
+
/// The lists set get the system values back and are forgotten (the screen's content goes, its destroy); the next
|
|
40
|
+
/// `applyToAllWithOptions:` walks the view again.
|
|
25
41
|
- (void)reset;
|
|
26
42
|
|
|
27
43
|
@end
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#import "ANNScrollEdgeEffects.h"
|
|
2
2
|
#import "ANNBarAppearance.h"
|
|
3
|
+
#import "ANNMainList.h"
|
|
4
|
+
|
|
5
|
+
// How deep the walk looks for lists, as the main list's search.
|
|
6
|
+
static const NSUInteger ANNEdgeWalkMaxDepth = 16;
|
|
7
|
+
static const CGFloat ANNEdgeWalkTolerance = 1;
|
|
8
|
+
|
|
9
|
+
// A list that scrolls (or bounces) vertically: what is in it is its rows, which never meet a bar themselves.
|
|
10
|
+
static BOOL ANNScrollsVertically(UIScrollView *list) {
|
|
11
|
+
return list.alwaysBounceVertical ||
|
|
12
|
+
list.contentSize.height > CGRectGetHeight(list.bounds) + ANNEdgeWalkTolerance;
|
|
13
|
+
}
|
|
3
14
|
|
|
4
15
|
// What one edge gets: whether it is hidden and its style; `nil` style is automatic.
|
|
5
16
|
typedef struct {
|
|
@@ -18,6 +29,11 @@ static ANNEdgeEffect ANNEdgeEffectOf(ANNScrollEdgeEffectOptions *effect,
|
|
|
18
29
|
@implementation ANNScrollEdgeEffects {
|
|
19
30
|
__weak UIView *_screenView;
|
|
20
31
|
NSHashTable<UIScrollView *> *_lists;
|
|
32
|
+
// What the last walk read (nil: walk at the next call), and whether content mounted since or the walk found a
|
|
33
|
+
// scroll view whose lists come and go without a mount (a pager's pages).
|
|
34
|
+
NSArray *_walkInputs;
|
|
35
|
+
BOOL _mountedSinceWalk;
|
|
36
|
+
BOOL _walkFoundPager;
|
|
21
37
|
}
|
|
22
38
|
|
|
23
39
|
- (instancetype)initWithScreenView:(UIView *)screenView {
|
|
@@ -30,46 +46,137 @@ static ANNEdgeEffect ANNEdgeEffectOf(ANNScrollEdgeEffectOptions *effect,
|
|
|
30
46
|
|
|
31
47
|
- (void)applyToAllWithOptions:(ANNNavigationOptions *)options {
|
|
32
48
|
if (@available(iOS 26.0, *)) {
|
|
33
|
-
[self reset];
|
|
34
49
|
UIView *screenView = _screenView;
|
|
35
|
-
if (screenView) {
|
|
36
|
-
[self
|
|
50
|
+
if (!screenView) {
|
|
51
|
+
[self reset];
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
NSArray *inputs = [self inputsOf:options traits:screenView.traitCollection];
|
|
55
|
+
[self dropListsOutside:screenView];
|
|
56
|
+
// The lists are the same as at the last walk: they take the values again (the screen gave them the system
|
|
57
|
+
// ones as it went), without a walk.
|
|
58
|
+
if (_walkInputs && _mountsReported && !_mountedSinceWalk && !_walkFoundPager &&
|
|
59
|
+
ANNOptionValuesSame(_walkInputs, inputs)) {
|
|
60
|
+
for (UIScrollView *list in _lists.allObjects) {
|
|
61
|
+
[self apply:options to:list];
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
NSMutableArray<UIScrollView *> *found = [NSMutableArray new];
|
|
66
|
+
BOOL pager = NO;
|
|
67
|
+
[self collectIn:screenView depth:0 into:found pager:&pager];
|
|
68
|
+
// The screen's lists that the walk no longer finds get the system values; the others are set again.
|
|
69
|
+
for (UIScrollView *kept in _lists.allObjects) {
|
|
70
|
+
if ([found indexOfObjectIdenticalTo:kept] == NSNotFound) {
|
|
71
|
+
[ANNScrollEdgeEffects resetList:kept];
|
|
72
|
+
[_lists removeObject:kept];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (UIScrollView *list in found) {
|
|
76
|
+
[self apply:options to:list];
|
|
37
77
|
}
|
|
78
|
+
_walkInputs = inputs;
|
|
79
|
+
_mountedSinceWalk = NO;
|
|
80
|
+
_walkFoundPager = pager;
|
|
38
81
|
}
|
|
39
82
|
}
|
|
40
83
|
|
|
41
|
-
|
|
84
|
+
// What the effects of a list read: the effect's options, the bar's background (a solid one hides the top), the traits
|
|
85
|
+
// a clear color is told by.
|
|
86
|
+
- (NSArray *)inputsOf:(ANNNavigationOptions *)options traits:(UITraitCollection *)traits {
|
|
87
|
+
ANNTopBarBackgroundOptions *background = options.topBar.background;
|
|
88
|
+
return @[
|
|
89
|
+
options.scrollEdgeEffect, background.color, background.component.name,
|
|
90
|
+
@(traits.userInterfaceStyle), @(traits.accessibilityContrast)
|
|
91
|
+
];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// The lists of `view`, bounded as the main list's search: hidden views (`display: none`) and the core's own views are
|
|
95
|
+
// left out with what is in them. A view at alpha 0 is not: an animation (a fade in, a transition) changes it without a
|
|
96
|
+
// mount. A list that scrolls vertically and a text view are set, not looked into; a scroll view that doesn't is looked
|
|
97
|
+
// into for the lists of its pages, and such a walk is never skipped.
|
|
98
|
+
- (void)collectIn:(UIView *)view
|
|
99
|
+
depth:(NSUInteger)depth
|
|
100
|
+
into:(NSMutableArray<UIScrollView *> *)found
|
|
101
|
+
pager:(BOOL *)pager {
|
|
102
|
+
if (depth > ANNEdgeWalkMaxDepth) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
42
105
|
for (UIView *subview in view.subviews) {
|
|
106
|
+
if (subview.hidden || [ANNMainList isExcludedView:subview]) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
43
109
|
if ([subview isKindOfClass:UIScrollView.class]) {
|
|
44
|
-
|
|
110
|
+
UIScrollView *list = (UIScrollView *)subview;
|
|
111
|
+
[found addObject:list];
|
|
112
|
+
if ([list isKindOfClass:UITextView.class] || ANNScrollsVertically(list)) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
*pager = YES;
|
|
45
116
|
}
|
|
46
|
-
[self
|
|
117
|
+
[self collectIn:subview depth:depth + 1 into:found pager:pager];
|
|
47
118
|
}
|
|
48
119
|
}
|
|
49
120
|
|
|
50
121
|
- (void)applyToList:(UIScrollView *)list options:(ANNNavigationOptions *)options {
|
|
122
|
+
_mountedSinceWalk = YES;
|
|
51
123
|
if (@available(iOS 26.0, *)) {
|
|
52
|
-
|
|
53
|
-
UIView *screenView = _screenView;
|
|
54
|
-
for (UIScrollView *kept in _lists.allObjects) {
|
|
55
|
-
if (!screenView || ![kept isDescendantOfView:screenView]) {
|
|
56
|
-
[ANNScrollEdgeEffects resetList:kept];
|
|
57
|
-
[_lists removeObject:kept];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
124
|
+
[self dropListsOutside:_screenView];
|
|
60
125
|
if (list) {
|
|
61
126
|
[self apply:options to:list];
|
|
62
127
|
}
|
|
63
128
|
}
|
|
64
129
|
}
|
|
65
130
|
|
|
66
|
-
|
|
131
|
+
// Whether `view` is in some screen: one of its views is the view of a view controller, in a window or not. A list in
|
|
132
|
+
// Fabric's pool is not: its component view (the list's own superview) has no superview there.
|
|
133
|
+
static BOOL ANNIsInAScreen(UIView *view) {
|
|
134
|
+
for (UIView *ancestor = view; ancestor; ancestor = ancestor.superview) {
|
|
135
|
+
if ([ancestor.nextResponder isKindOfClass:UIViewController.class]) {
|
|
136
|
+
return YES;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return NO;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// A list React took out of the screen is no longer the screen's: outside every screen (in the pool Fabric recycles
|
|
143
|
+
// its views from, shared by every surface) it gets the system values, so it comes clean into the screen that takes it;
|
|
144
|
+
// in another screen it may be that screen's already, which set its values: it is only forgotten.
|
|
145
|
+
- (void)dropListsOutside:(nullable UIView *)screenView {
|
|
146
|
+
for (UIScrollView *kept in _lists.allObjects) {
|
|
147
|
+
if (screenView && [kept isDescendantOfView:screenView]) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (@available(iOS 26.0, *)) {
|
|
151
|
+
if (!ANNIsInAScreen(kept)) {
|
|
152
|
+
[ANNScrollEdgeEffects resetList:kept];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
[_lists removeObject:kept];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// The screen's lists get the system values; with `keepingLists` they stay known, with what the last walk read, for
|
|
160
|
+
// the next appearance to set them again without a walk.
|
|
161
|
+
- (void)giveBackKeepingLists:(BOOL)keepingLists {
|
|
162
|
+
[self dropListsOutside:_screenView];
|
|
67
163
|
if (@available(iOS 26.0, *)) {
|
|
68
164
|
for (UIScrollView *list in _lists.allObjects) {
|
|
69
165
|
[ANNScrollEdgeEffects resetList:list];
|
|
70
166
|
}
|
|
71
167
|
}
|
|
72
|
-
|
|
168
|
+
if (!keepingLists) {
|
|
169
|
+
[_lists removeAllObjects];
|
|
170
|
+
_walkInputs = nil;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
- (void)screenDidDisappear {
|
|
175
|
+
[self giveBackKeepingLists:YES];
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
- (void)reset {
|
|
179
|
+
[self giveBackKeepingLists:NO];
|
|
73
180
|
}
|
|
74
181
|
|
|
75
182
|
+ (void)resetList:(UIScrollView *)list API_AVAILABLE(ios(26.0)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#import "ANNSearchPresenter.h"
|
|
2
2
|
#import "ANNEventEmitter.h"
|
|
3
|
-
#import "
|
|
3
|
+
#import "ANNScreenEnvironment.h"
|
|
4
4
|
#import "ANNSearchResultsViewController.h"
|
|
5
5
|
#import "ANNUserChoice.h"
|
|
6
6
|
|
|
@@ -298,7 +298,7 @@ static ANNUserChoice *ANNScopeChoice(NSString *scope) {
|
|
|
298
298
|
_appliedSuggestions = nil;
|
|
299
299
|
if (_resultsComponentId) {
|
|
300
300
|
[_componentRegistry removeChildComponent:_resultsComponentId];
|
|
301
|
-
[
|
|
301
|
+
[ANNScreenEnvironment removeScreenId:_resultsComponentId];
|
|
302
302
|
}
|
|
303
303
|
_resultsKey = nil;
|
|
304
304
|
_resultsComponentId = nil;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#import "ANNSearchResultsViewController.h"
|
|
2
|
-
#import "
|
|
2
|
+
#import "ANNScreenEnvironment.h"
|
|
3
3
|
|
|
4
4
|
@implementation ANNSearchResultsViewController {
|
|
5
5
|
NSString *_componentId;
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
[super viewDidLayoutSubviews];
|
|
31
31
|
_reactView.frame = self.view.bounds;
|
|
32
32
|
if (_componentId) {
|
|
33
|
-
[
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
[ANNScreenEnvironment setInsets:self.view.safeAreaInsets
|
|
34
|
+
padding:UIEdgeInsetsZero
|
|
35
|
+
forScreenId:_componentId
|
|
36
|
+
eventEmitter:_eventEmitter];
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|