@nativescript/core 9.1.0-alpha.9 → 9.1.0-dev.0
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/animation-frame/animation-native.windows.d.ts +1 -0
- package/animation-frame/animation-native.windows.js +4 -0
- package/animation-frame/animation-native.windows.js.map +1 -0
- package/application/application-common.d.ts +2 -1
- package/application/application-common.js +4 -0
- package/application/application-common.js.map +1 -1
- package/application/application.android.d.ts +0 -16
- package/application/application.android.js +1 -6
- package/application/application.android.js.map +1 -1
- package/application/application.d.ts +9 -0
- package/application/application.ios.d.ts +1 -2
- package/application/application.ios.js +9 -88
- package/application/application.ios.js.map +1 -1
- package/application/application.windows.d.ts +27 -0
- package/application/application.windows.js +331 -0
- package/application/application.windows.js.map +1 -0
- package/application/helpers.windows.d.ts +8 -0
- package/application/helpers.windows.js +12 -0
- package/application/helpers.windows.js.map +1 -0
- package/application/index.windows.d.ts +2 -0
- package/application/index.windows.js +3 -0
- package/application/index.windows.js.map +1 -0
- package/application/window-helper.windows.d.ts +29 -0
- package/application/window-helper.windows.js +159 -0
- package/application/window-helper.windows.js.map +1 -0
- package/application-settings/index.windows.d.ts +11 -0
- package/application-settings/index.windows.js +93 -0
- package/application-settings/index.windows.js.map +1 -0
- package/color/index.d.ts +5 -0
- package/color/index.windows.d.ts +8 -0
- package/color/index.windows.js +19 -0
- package/color/index.windows.js.map +1 -0
- package/connectivity/index.windows.d.ts +11 -0
- package/connectivity/index.windows.js +50 -0
- package/connectivity/index.windows.js.map +1 -0
- package/debugger/webinspector-network.windows.d.ts +79 -0
- package/debugger/webinspector-network.windows.js +307 -0
- package/debugger/webinspector-network.windows.js.map +1 -0
- package/file-system/file-system-access.windows.d.ts +76 -0
- package/file-system/file-system-access.windows.js +476 -0
- package/file-system/file-system-access.windows.js.map +1 -0
- package/fps-meter/fps-native.windows.d.ts +8 -0
- package/fps-meter/fps-native.windows.js +40 -0
- package/fps-meter/fps-native.windows.js.map +1 -0
- package/global-types.d.ts +1 -0
- package/globals/index.js +1 -4
- package/globals/index.js.map +1 -1
- package/http/http-request/index.android.js.map +1 -1
- package/http/http-request/index.ios.js.map +1 -1
- package/http/http-request/index.windows.d.ts +2 -0
- package/http/http-request/index.windows.js +41 -0
- package/http/http-request/index.windows.js.map +1 -0
- package/http/http-request-internal/index.android.d.ts +1 -7
- package/http/http-request-internal/index.android.js.map +1 -1
- package/http/http-request-internal/index.ios.d.ts +1 -7
- package/http/http-request-internal/index.ios.js.map +1 -1
- package/http/http-request-internal/index.windows.d.ts +4 -0
- package/http/http-request-internal/index.windows.js +134 -0
- package/http/http-request-internal/index.windows.js.map +1 -0
- package/image-asset/index.windows.d.ts +9 -0
- package/image-asset/index.windows.js +34 -0
- package/image-asset/index.windows.js.map +1 -0
- package/image-source/index.android.d.ts +1 -0
- package/image-source/index.android.js.map +1 -1
- package/image-source/index.d.ts +5 -0
- package/image-source/index.ios.d.ts +1 -0
- package/image-source/index.ios.js.map +1 -1
- package/image-source/index.windows.d.ts +52 -0
- package/image-source/index.windows.js +498 -0
- package/image-source/index.windows.js.map +1 -0
- package/package.json +6 -4
- package/platform/common.d.ts +2 -0
- package/platform/common.js +2 -0
- package/platform/common.js.map +1 -1
- package/platform/device/index.windows.d.ts +18 -0
- package/platform/device/index.windows.js +94 -0
- package/platform/device/index.windows.js.map +1 -0
- package/platform/screen/index.windows.d.ts +13 -0
- package/platform/screen/index.windows.js +25 -0
- package/platform/screen/index.windows.js.map +1 -0
- package/platforms/windows/arm64/NativeScript.Widgets.dll +0 -0
- package/platforms/windows/arm64/NativeScript.Widgets.winmd +0 -0
- package/platforms/windows/x64/NativeScript.Widgets.dll +0 -0
- package/platforms/windows/x64/NativeScript.Widgets.winmd +0 -0
- package/plugin.props +12 -0
- package/plugin.targets +71 -0
- package/references.d.ts +4 -0
- package/text/index.windows.d.ts +9 -0
- package/text/index.windows.js +12 -0
- package/text/index.windows.js.map +1 -0
- package/timer/index.windows.d.ts +4 -0
- package/timer/index.windows.js +14 -0
- package/timer/index.windows.js.map +1 -0
- package/ui/action-bar/index.ios.js +1 -0
- package/ui/action-bar/index.ios.js.map +1 -1
- package/ui/action-bar/index.windows.d.ts +12 -0
- package/ui/action-bar/index.windows.js +36 -0
- package/ui/action-bar/index.windows.js.map +1 -0
- package/ui/activity-indicator/index.windows.d.ts +15 -0
- package/ui/activity-indicator/index.windows.js +65 -0
- package/ui/activity-indicator/index.windows.js.map +1 -0
- package/ui/animation/index.windows.d.ts +19 -0
- package/ui/animation/index.windows.js +603 -0
- package/ui/animation/index.windows.js.map +1 -0
- package/ui/builder/index.d.ts +1 -0
- package/ui/builder/index.js +2 -1
- package/ui/builder/index.js.map +1 -1
- package/ui/button/index.android.d.ts +9 -2
- package/ui/button/index.android.js +24 -7
- package/ui/button/index.android.js.map +1 -1
- package/ui/button/index.ios.d.ts +9 -2
- package/ui/button/index.ios.js +72 -17
- package/ui/button/index.ios.js.map +1 -1
- package/ui/button/index.windows.d.ts +13 -0
- package/ui/button/index.windows.js +73 -0
- package/ui/button/index.windows.js.map +1 -0
- package/ui/core/bindable/index.js +1 -2
- package/ui/core/bindable/index.js.map +1 -1
- package/ui/core/control-state-change/index.windows.d.ts +4 -0
- package/ui/core/control-state-change/index.windows.js +6 -0
- package/ui/core/control-state-change/index.windows.js.map +1 -0
- package/ui/core/view/index.android.d.ts +0 -1
- package/ui/core/view/index.android.js +8 -17
- package/ui/core/view/index.android.js.map +1 -1
- package/ui/core/view/index.ios.d.ts +0 -1
- package/ui/core/view/index.ios.js +4 -17
- package/ui/core/view/index.ios.js.map +1 -1
- package/ui/core/view/index.windows.d.ts +120 -0
- package/ui/core/view/index.windows.js +2178 -0
- package/ui/core/view/index.windows.js.map +1 -0
- package/ui/core/view/view-common.d.ts +2 -0
- package/ui/core/view/view-common.js +6 -1
- package/ui/core/view/view-common.js.map +1 -1
- package/ui/core/view/view-helper/index.d.ts +0 -1
- package/ui/core/view/view-helper/index.ios.d.ts +0 -24
- package/ui/core/view/view-helper/index.ios.js +5 -54
- package/ui/core/view/view-helper/index.ios.js.map +1 -1
- package/ui/core/view/view-helper/index.windows.d.ts +3 -0
- package/ui/core/view/view-helper/index.windows.js +4 -0
- package/ui/core/view/view-helper/index.windows.js.map +1 -0
- package/ui/core/view-base/index.d.ts +10 -24
- package/ui/core/view-base/index.js +32 -50
- package/ui/core/view-base/index.js.map +1 -1
- package/ui/date-picker/index.windows.d.ts +22 -0
- package/ui/date-picker/index.windows.js +97 -0
- package/ui/date-picker/index.windows.js.map +1 -0
- package/ui/dialogs/index.windows.d.ts +17 -0
- package/ui/dialogs/index.windows.js +471 -0
- package/ui/dialogs/index.windows.js.map +1 -0
- package/ui/editable-text-base/index.windows.d.ts +27 -0
- package/ui/editable-text-base/index.windows.js +165 -0
- package/ui/editable-text-base/index.windows.js.map +1 -0
- package/ui/embedding/index.windows.d.ts +1 -0
- package/ui/embedding/index.windows.js +4 -0
- package/ui/embedding/index.windows.js.map +1 -0
- package/ui/frame/fragment.transitions.android.d.ts +13 -13
- package/ui/frame/fragment.transitions.android.js.map +1 -1
- package/ui/frame/frame-common.d.ts +1 -2
- package/ui/frame/frame-common.js +0 -10
- package/ui/frame/frame-common.js.map +1 -1
- package/ui/frame/frame-helper-for-android.d.ts +4 -4
- package/ui/frame/frame-helper-for-android.js +8 -17
- package/ui/frame/frame-helper-for-android.js.map +1 -1
- package/ui/frame/index.android.d.ts +11 -6
- package/ui/frame/index.android.js +59 -18
- package/ui/frame/index.android.js.map +1 -1
- package/ui/frame/index.d.ts +21 -12
- package/ui/frame/index.ios.d.ts +2 -2
- package/ui/frame/index.ios.js.map +1 -1
- package/ui/frame/index.windows.d.ts +52 -0
- package/ui/frame/index.windows.js +778 -0
- package/ui/frame/index.windows.js.map +1 -0
- package/ui/gestures/index.windows.d.ts +35 -0
- package/ui/gestures/index.windows.js +483 -0
- package/ui/gestures/index.windows.js.map +1 -0
- package/ui/html-view/index.windows.d.ts +18 -0
- package/ui/html-view/index.windows.js +191 -0
- package/ui/html-view/index.windows.js.map +1 -0
- package/ui/image/index.windows.d.ts +17 -0
- package/ui/image/index.windows.js +132 -0
- package/ui/image/index.windows.js.map +1 -0
- package/ui/image/symbol-effects.windows.d.ts +5 -0
- package/ui/image/symbol-effects.windows.js +8 -0
- package/ui/image/symbol-effects.windows.js.map +1 -0
- package/ui/image-cache/index.windows.d.ts +11 -0
- package/ui/image-cache/index.windows.js +48 -0
- package/ui/image-cache/index.windows.js.map +1 -0
- package/ui/label/index.ios.d.ts +5 -2
- package/ui/label/index.ios.js +44 -12
- package/ui/label/index.ios.js.map +1 -1
- package/ui/label/index.windows.d.ts +15 -0
- package/ui/label/index.windows.js +66 -0
- package/ui/label/index.windows.js.map +1 -0
- package/ui/layouts/absolute-layout/index.ios.js +1 -5
- package/ui/layouts/absolute-layout/index.ios.js.map +1 -1
- package/ui/layouts/absolute-layout/index.windows.d.ts +8 -0
- package/ui/layouts/absolute-layout/index.windows.js +47 -0
- package/ui/layouts/absolute-layout/index.windows.js.map +1 -0
- package/ui/layouts/dock-layout/index.ios.js +1 -5
- package/ui/layouts/dock-layout/index.ios.js.map +1 -1
- package/ui/layouts/dock-layout/index.windows.d.ts +19 -0
- package/ui/layouts/dock-layout/index.windows.js +202 -0
- package/ui/layouts/dock-layout/index.windows.js.map +1 -0
- package/ui/layouts/flexbox-layout/flexbox-layout-common.d.ts +17 -2
- package/ui/layouts/flexbox-layout/flexbox-layout-common.js +73 -2
- package/ui/layouts/flexbox-layout/flexbox-layout-common.js.map +1 -1
- package/ui/layouts/flexbox-layout/index.ios.js +1 -5
- package/ui/layouts/flexbox-layout/index.ios.js.map +1 -1
- package/ui/layouts/flexbox-layout/index.windows.d.ts +27 -0
- package/ui/layouts/flexbox-layout/index.windows.js +188 -0
- package/ui/layouts/flexbox-layout/index.windows.js.map +1 -0
- package/ui/layouts/grid-layout/index.ios.js +1 -5
- package/ui/layouts/grid-layout/index.ios.js.map +1 -1
- package/ui/layouts/grid-layout/index.windows.d.ts +20 -0
- package/ui/layouts/grid-layout/index.windows.js +130 -0
- package/ui/layouts/grid-layout/index.windows.js.map +1 -0
- package/ui/layouts/layout-base.android.d.ts +10 -3
- package/ui/layouts/layout-base.android.js +24 -7
- package/ui/layouts/layout-base.android.js.map +1 -1
- package/ui/layouts/layout-base.ios.js.map +1 -1
- package/ui/layouts/layout-base.windows.d.ts +4 -0
- package/ui/layouts/layout-base.windows.js +5 -0
- package/ui/layouts/layout-base.windows.js.map +1 -0
- package/ui/layouts/liquid-glass/index.windows.d.ts +4 -0
- package/ui/layouts/liquid-glass/index.windows.js +5 -0
- package/ui/layouts/liquid-glass/index.windows.js.map +1 -0
- package/ui/layouts/liquid-glass-container/index.windows.d.ts +4 -0
- package/ui/layouts/liquid-glass-container/index.windows.js +5 -0
- package/ui/layouts/liquid-glass-container/index.windows.js.map +1 -0
- package/ui/layouts/root-layout/index.windows.d.ts +13 -0
- package/ui/layouts/root-layout/index.windows.js +160 -0
- package/ui/layouts/root-layout/index.windows.js.map +1 -0
- package/ui/layouts/stack-layout/index.ios.js +1 -5
- package/ui/layouts/stack-layout/index.ios.js.map +1 -1
- package/ui/layouts/stack-layout/index.windows.d.ts +13 -0
- package/ui/layouts/stack-layout/index.windows.js +63 -0
- package/ui/layouts/stack-layout/index.windows.js.map +1 -0
- package/ui/layouts/wrap-layout/index.ios.js +1 -5
- package/ui/layouts/wrap-layout/index.ios.js.map +1 -1
- package/ui/layouts/wrap-layout/index.windows.d.ts +20 -0
- package/ui/layouts/wrap-layout/index.windows.js +182 -0
- package/ui/layouts/wrap-layout/index.windows.js.map +1 -0
- package/ui/list-picker/index.windows.d.ts +19 -0
- package/ui/list-picker/index.windows.js +75 -0
- package/ui/list-picker/index.windows.js.map +1 -0
- package/ui/list-view/index.d.ts +1 -13
- package/ui/list-view/index.ios.d.ts +1 -3
- package/ui/list-view/index.ios.js +10 -48
- package/ui/list-view/index.ios.js.map +1 -1
- package/ui/list-view/index.windows.d.ts +60 -0
- package/ui/list-view/index.windows.js +842 -0
- package/ui/list-view/index.windows.js.map +1 -0
- package/ui/list-view/list-view-common.d.ts +0 -14
- package/ui/list-view/list-view-common.js +0 -4
- package/ui/list-view/list-view-common.js.map +1 -1
- package/ui/page/index.d.ts +1 -1
- package/ui/page/index.ios.d.ts +0 -1
- package/ui/page/index.ios.js +1 -15
- package/ui/page/index.ios.js.map +1 -1
- package/ui/page/index.windows.d.ts +12 -0
- package/ui/page/index.windows.js +42 -0
- package/ui/page/index.windows.js.map +1 -0
- package/ui/page/page-common.d.ts +2 -5
- package/ui/page/page-common.js.map +1 -1
- package/ui/progress/index.windows.d.ts +20 -0
- package/ui/progress/index.windows.js +65 -0
- package/ui/progress/index.windows.js.map +1 -0
- package/ui/repeater/index.d.ts +1 -2
- package/ui/repeater/index.js.map +1 -1
- package/ui/scroll-view/index.d.ts +0 -13
- package/ui/scroll-view/index.ios.js +7 -21
- package/ui/scroll-view/index.ios.js.map +1 -1
- package/ui/scroll-view/index.windows.d.ts +29 -0
- package/ui/scroll-view/index.windows.js +192 -0
- package/ui/scroll-view/index.windows.js.map +1 -0
- package/ui/scroll-view/scroll-view-common.d.ts +0 -6
- package/ui/scroll-view/scroll-view-common.js +0 -8
- package/ui/scroll-view/scroll-view-common.js.map +1 -1
- package/ui/search-bar/index.windows.d.ts +19 -0
- package/ui/search-bar/index.windows.js +91 -0
- package/ui/search-bar/index.windows.js.map +1 -0
- package/ui/segmented-bar/index.windows.d.ts +23 -0
- package/ui/segmented-bar/index.windows.js +117 -0
- package/ui/segmented-bar/index.windows.js.map +1 -0
- package/ui/segmented-bar/segmented-bar-common.d.ts +1 -5
- package/ui/segmented-bar/segmented-bar-common.js.map +1 -1
- package/ui/slider/index.windows.d.ts +25 -0
- package/ui/slider/index.windows.js +134 -0
- package/ui/slider/index.windows.js.map +1 -0
- package/ui/split-view/index.windows.d.ts +49 -0
- package/ui/split-view/index.windows.js +549 -0
- package/ui/split-view/index.windows.js.map +1 -0
- package/ui/styling/background.windows.d.ts +2 -0
- package/ui/styling/background.windows.js +2 -0
- package/ui/styling/background.windows.js.map +1 -0
- package/ui/styling/font.d.ts +4 -0
- package/ui/styling/font.windows.d.ts +25 -0
- package/ui/styling/font.windows.js +213 -0
- package/ui/styling/font.windows.js.map +1 -0
- package/ui/styling/style/index.d.ts +0 -1
- package/ui/styling/style/index.js.map +1 -1
- package/ui/styling/style-properties.d.ts +0 -1
- package/ui/styling/style-properties.js +32 -41
- package/ui/styling/style-properties.js.map +1 -1
- package/ui/styling/style-scope.js +9 -1
- package/ui/styling/style-scope.js.map +1 -1
- package/ui/switch/index.windows.d.ts +20 -0
- package/ui/switch/index.windows.js +97 -0
- package/ui/switch/index.windows.js.map +1 -0
- package/ui/tab-view/index.ios.d.ts +1 -10
- package/ui/tab-view/index.ios.js +4 -21
- package/ui/tab-view/index.ios.js.map +1 -1
- package/ui/tab-view/index.windows.d.ts +29 -0
- package/ui/tab-view/index.windows.js +268 -0
- package/ui/tab-view/index.windows.js.map +1 -0
- package/ui/tab-view/tab-view-common.d.ts +1 -5
- package/ui/tab-view/tab-view-common.js.map +1 -1
- package/ui/text-base/index.android.d.ts +9 -2
- package/ui/text-base/index.android.js +24 -7
- package/ui/text-base/index.android.js.map +1 -1
- package/ui/text-base/index.windows.d.ts +47 -0
- package/ui/text-base/index.windows.js +639 -0
- package/ui/text-base/index.windows.js.map +1 -0
- package/ui/text-field/index.ios.d.ts +9 -2
- package/ui/text-field/index.ios.js +23 -2
- package/ui/text-field/index.ios.js.map +1 -1
- package/ui/text-field/index.windows.d.ts +23 -0
- package/ui/text-field/index.windows.js +186 -0
- package/ui/text-field/index.windows.js.map +1 -0
- package/ui/text-view/index.ios.d.ts +9 -2
- package/ui/text-view/index.ios.js +72 -20
- package/ui/text-view/index.ios.js.map +1 -1
- package/ui/text-view/index.windows.d.ts +12 -0
- package/ui/text-view/index.windows.js +47 -0
- package/ui/text-view/index.windows.js.map +1 -0
- package/ui/time-picker/index.windows.d.ts +18 -0
- package/ui/time-picker/index.windows.js +77 -0
- package/ui/time-picker/index.windows.js.map +1 -0
- package/ui/transition/fade-transition.windows.d.ts +3 -0
- package/ui/transition/fade-transition.windows.js +4 -0
- package/ui/transition/fade-transition.windows.js.map +1 -0
- package/ui/transition/index.d.ts +1 -2
- package/ui/transition/index.windows.d.ts +20 -0
- package/ui/transition/index.windows.js +30 -0
- package/ui/transition/index.windows.js.map +1 -0
- package/ui/transition/modal-transition.ios.js +5 -33
- package/ui/transition/modal-transition.ios.js.map +1 -1
- package/ui/transition/modal-transition.windows.d.ts +3 -0
- package/ui/transition/modal-transition.windows.js +4 -0
- package/ui/transition/modal-transition.windows.js.map +1 -0
- package/ui/transition/page-transition.ios.d.ts +0 -5
- package/ui/transition/page-transition.ios.js +33 -296
- package/ui/transition/page-transition.ios.js.map +1 -1
- package/ui/transition/page-transition.windows.d.ts +3 -0
- package/ui/transition/page-transition.windows.js +4 -0
- package/ui/transition/page-transition.windows.js.map +1 -0
- package/ui/transition/shared-transition-helper.android.d.ts +0 -3
- package/ui/transition/shared-transition-helper.android.js +0 -9
- package/ui/transition/shared-transition-helper.android.js.map +1 -1
- package/ui/transition/shared-transition-helper.d.ts +0 -7
- package/ui/transition/shared-transition-helper.ios.d.ts +0 -36
- package/ui/transition/shared-transition-helper.ios.js +89 -765
- package/ui/transition/shared-transition-helper.ios.js.map +1 -1
- package/ui/transition/shared-transition-helper.windows.d.ts +30 -0
- package/ui/transition/shared-transition-helper.windows.js +155 -0
- package/ui/transition/shared-transition-helper.windows.js.map +1 -0
- package/ui/transition/shared-transition.d.ts +0 -66
- package/ui/transition/shared-transition.js +2 -57
- package/ui/transition/shared-transition.js.map +1 -1
- package/ui/transition/slide-transition.windows.d.ts +3 -0
- package/ui/transition/slide-transition.windows.js +4 -0
- package/ui/transition/slide-transition.windows.js.map +1 -0
- package/ui/utils.windows.d.ts +9 -0
- package/ui/utils.windows.js +21 -0
- package/ui/utils.windows.js.map +1 -0
- package/ui/web-view/index.windows.d.ts +23 -0
- package/ui/web-view/index.windows.js +115 -0
- package/ui/web-view/index.windows.js.map +1 -0
- package/utils/constants.windows.d.ts +2 -0
- package/utils/constants.windows.js +5 -0
- package/utils/constants.windows.js.map +1 -0
- package/utils/debug-source.js +3 -0
- package/utils/debug-source.js.map +1 -1
- package/utils/index.windows.d.ts +20 -0
- package/utils/index.windows.js +94 -0
- package/utils/index.windows.js.map +1 -0
- package/utils/layout-helper/index.windows.d.ts +25 -0
- package/utils/layout-helper/index.windows.js +74 -0
- package/utils/layout-helper/index.windows.js.map +1 -0
- package/utils/mainthread-helper.windows.d.ts +3 -0
- package/utils/mainthread-helper.windows.js +18 -0
- package/utils/mainthread-helper.windows.js.map +1 -0
- package/utils/native-helper.android.d.ts +2 -66
- package/utils/native-helper.android.js.map +1 -1
- package/utils/native-helper.ios.d.ts +2 -105
- package/utils/native-helper.ios.js +4 -15
- package/utils/native-helper.ios.js.map +1 -1
- package/utils/native-helper.windows.d.ts +8 -0
- package/utils/native-helper.windows.js +101 -0
- package/utils/native-helper.windows.js.map +1 -0
- package/utils/platform-check.d.ts +1 -1
- package/utils/platform-check.js.map +1 -1
- package/utils/native-helper.types.d.ts +0 -2
|
@@ -3,115 +3,6 @@ import { isNumber } from '../../utils/types';
|
|
|
3
3
|
import { Screen } from '../../platform';
|
|
4
4
|
import { CORE_ANIMATION_DEFAULTS } from '../../utils/animation-helpers';
|
|
5
5
|
import { ios as iOSUtils } from '../../utils/native-helper';
|
|
6
|
-
import { Color } from '../../color';
|
|
7
|
-
/**
|
|
8
|
-
* Apply a drop shadow behind the destination view during an interactive
|
|
9
|
-
* dismiss so it looks elevated above the source (Apple Music–style).
|
|
10
|
-
*
|
|
11
|
-
* Shadow + rounded corners can't coexist on the same CALayer on iOS — both
|
|
12
|
-
* `masksToBounds = true` and `layer.mask` clip the shadow as part of the
|
|
13
|
-
* compositing pipeline. So the shadow lives on a SIBLING CALayer inserted
|
|
14
|
-
* below the modal's layer in its superlayer (the same pattern NS uses for
|
|
15
|
-
* box-shadow via `outerShadowContainerLayer`). The modal keeps its own
|
|
16
|
-
* `cornerRadius` + `masksToBounds` so subviews still clip to the rounded
|
|
17
|
-
* shape.
|
|
18
|
-
*
|
|
19
|
-
* Because the shadow layer is a sibling (not a child), it doesn't inherit
|
|
20
|
-
* the modal's transform automatically — `syncInteractiveDismissShadow`
|
|
21
|
-
* mirrors transform/position/bounds during the drag.
|
|
22
|
-
*
|
|
23
|
-
* Falsy `shadowConfig` is a no-op.
|
|
24
|
-
*/
|
|
25
|
-
export function applyInteractiveDismissShadow(presentedView, shadowConfig) {
|
|
26
|
-
if (!presentedView?.layer || !shadowConfig)
|
|
27
|
-
return;
|
|
28
|
-
const layer = presentedView.layer;
|
|
29
|
-
const superlayer = layer.superlayer;
|
|
30
|
-
if (!superlayer)
|
|
31
|
-
return;
|
|
32
|
-
const bounds = layer.bounds;
|
|
33
|
-
if (bounds.size.width <= 0 || bounds.size.height <= 0)
|
|
34
|
-
return;
|
|
35
|
-
// Idempotent.
|
|
36
|
-
if (presentedView.__sharedTransitionShadowLayer)
|
|
37
|
-
return;
|
|
38
|
-
const cfg = shadowConfig === true ? {} : shadowConfig;
|
|
39
|
-
const colorInput = cfg.color ?? '#000';
|
|
40
|
-
let cgColor;
|
|
41
|
-
try {
|
|
42
|
-
const nsColor = typeof colorInput === 'string' ? new Color(colorInput) : colorInput;
|
|
43
|
-
cgColor = nsColor.ios?.CGColor || nsColor.CGColor || UIColor.blackColor.CGColor;
|
|
44
|
-
}
|
|
45
|
-
catch (_) {
|
|
46
|
-
cgColor = UIColor.blackColor.CGColor;
|
|
47
|
-
}
|
|
48
|
-
const opacity = isNumber(cfg.opacity) ? cfg.opacity : 0.3;
|
|
49
|
-
const radius = isNumber(cfg.radius) ? cfg.radius : 30;
|
|
50
|
-
const offsetX = isNumber(cfg.offset?.x) ? cfg.offset.x : 0;
|
|
51
|
-
const offsetY = isNumber(cfg.offset?.y) ? cfg.offset.y : 8;
|
|
52
|
-
const cornerRadius = layer.cornerRadius || 0;
|
|
53
|
-
const roundedRectPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(CGRectMake(0, 0, bounds.size.width, bounds.size.height), cornerRadius).CGPath;
|
|
54
|
-
const shadowLayer = CALayer.layer();
|
|
55
|
-
// Match the modal's geometry so shadow tracks 1:1.
|
|
56
|
-
shadowLayer.anchorPoint = layer.anchorPoint;
|
|
57
|
-
shadowLayer.bounds = bounds;
|
|
58
|
-
shadowLayer.position = layer.position;
|
|
59
|
-
shadowLayer.transform = layer.transform;
|
|
60
|
-
shadowLayer.shadowColor = cgColor;
|
|
61
|
-
shadowLayer.shadowOpacity = opacity;
|
|
62
|
-
shadowLayer.shadowRadius = radius;
|
|
63
|
-
shadowLayer.shadowOffset = CGSizeMake(offsetX, offsetY);
|
|
64
|
-
// Explicit shadowPath = shadow renders from this shape outward;
|
|
65
|
-
// the layer itself stays fully transparent.
|
|
66
|
-
shadowLayer.shadowPath = roundedRectPath;
|
|
67
|
-
CATransaction.begin();
|
|
68
|
-
CATransaction.setDisableActions(true);
|
|
69
|
-
superlayer.insertSublayerBelow(shadowLayer, layer);
|
|
70
|
-
CATransaction.commit();
|
|
71
|
-
presentedView.__sharedTransitionShadowLayer = shadowLayer;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Mirror the modal layer's geometry onto the sibling shadow layer so the
|
|
75
|
-
* shadow tracks the interactive drag. Called from the gesture handlers
|
|
76
|
-
* after the modal's state has been updated (either via direct transform
|
|
77
|
-
* in morph mode, or via `interactiveController.updateInteractiveTransition`
|
|
78
|
-
* for the non-morph modal/page case driven by UIViewPropertyAnimator).
|
|
79
|
-
*
|
|
80
|
-
* Reads the layer's **presentation** values when an animation is in flight
|
|
81
|
-
* — model values would point at the animation's end state and snap the
|
|
82
|
-
* shadow there. Falls back to model values when no animation is running.
|
|
83
|
-
*/
|
|
84
|
-
export function syncInteractiveDismissShadow(presentedView) {
|
|
85
|
-
const shadowLayer = presentedView?.__sharedTransitionShadowLayer;
|
|
86
|
-
if (!shadowLayer || !presentedView?.layer)
|
|
87
|
-
return;
|
|
88
|
-
const layer = presentedView.layer;
|
|
89
|
-
const pres = typeof layer.presentationLayer === 'function' ? layer.presentationLayer() : null;
|
|
90
|
-
const src = pres || layer;
|
|
91
|
-
const b = src.bounds;
|
|
92
|
-
CATransaction.begin();
|
|
93
|
-
CATransaction.setDisableActions(true);
|
|
94
|
-
shadowLayer.bounds = b;
|
|
95
|
-
shadowLayer.position = src.position;
|
|
96
|
-
shadowLayer.anchorPoint = src.anchorPoint;
|
|
97
|
-
shadowLayer.transform = src.transform;
|
|
98
|
-
shadowLayer.shadowPath = UIBezierPath.bezierPathWithRoundedRectCornerRadius(CGRectMake(0, 0, b.size.width, b.size.height), src.cornerRadius || 0).CGPath;
|
|
99
|
-
CATransaction.commit();
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Remove the sibling shadow layer. Safe to call repeatedly / when no
|
|
103
|
-
* shadow was applied.
|
|
104
|
-
*/
|
|
105
|
-
export function removeInteractiveDismissShadow(presentedView) {
|
|
106
|
-
const shadowLayer = presentedView?.__sharedTransitionShadowLayer;
|
|
107
|
-
if (!shadowLayer)
|
|
108
|
-
return;
|
|
109
|
-
CATransaction.begin();
|
|
110
|
-
CATransaction.setDisableActions(true);
|
|
111
|
-
shadowLayer.removeFromSuperlayer();
|
|
112
|
-
CATransaction.commit();
|
|
113
|
-
presentedView.__sharedTransitionShadowLayer = null;
|
|
114
|
-
}
|
|
115
6
|
export class SharedTransitionHelper {
|
|
116
7
|
static animate(state, transitionContext, type) {
|
|
117
8
|
const transition = state.instance;
|
|
@@ -143,10 +34,6 @@ export class SharedTransitionHelper {
|
|
|
143
34
|
independent: [],
|
|
144
35
|
};
|
|
145
36
|
}
|
|
146
|
-
// Track every matched source view so cleanup can restore alpha,
|
|
147
|
-
// including duplicates (same album surfacing in multiple lists)
|
|
148
|
-
// that we hide but don't create a snapshot for.
|
|
149
|
-
transition.sharedElements._allPresentingViews = sharedElements.slice();
|
|
150
37
|
if (SharedTransition.DEBUG) {
|
|
151
38
|
console.log(` ${type}: Present`);
|
|
152
39
|
console.log(`1. Found sharedTransitionTags to animate:`, sharedElementTags);
|
|
@@ -159,22 +46,12 @@ export class SharedTransitionHelper {
|
|
|
159
46
|
const pageEndTags = pageEnd?.sharedTransitionTags || {};
|
|
160
47
|
// console.log('pageEndIndependentTags:', pageEndIndependentTags);
|
|
161
48
|
const positionSharedTags = async () => {
|
|
162
|
-
const processedTags = new Set();
|
|
163
49
|
for (const presentingView of sharedElements) {
|
|
164
50
|
const presentingSharedElement = presentingView.ios;
|
|
165
|
-
|
|
166
|
-
if
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Each instance has the same sharedTransitionTag. Only the first
|
|
170
|
-
// one becomes the snapshot origin; the rest must still be hidden
|
|
171
|
-
// so they don't show through behind the animating snapshot.
|
|
172
|
-
if (presentingSharedElement) {
|
|
173
|
-
presentingSharedElement.alpha = 0;
|
|
174
|
-
}
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
processedTags.add(tag);
|
|
51
|
+
// console.log('fromTarget instanceof UIImageView:', fromTarget instanceof UIImageView)
|
|
52
|
+
// TODO: discuss whether we should check if UIImage/UIImageView type to always snapshot images or if other view types could be duped/added vs. snapshotted
|
|
53
|
+
// Note: snapshot may be most efficient/simple
|
|
54
|
+
// console.log('---> ', presentingView.sharedTransitionTag, ': ', presentingSharedElement)
|
|
178
55
|
const presentedView = presented.find((v) => v.sharedTransitionTag === presentingView.sharedTransitionTag);
|
|
179
56
|
const presentedSharedElement = presentedView.ios;
|
|
180
57
|
const pageEndProps = pageEndTags[presentingView.sharedTransitionTag];
|
|
@@ -183,25 +60,15 @@ export class SharedTransitionHelper {
|
|
|
183
60
|
await pageEndProps?.callback(presentedView, 'present');
|
|
184
61
|
}
|
|
185
62
|
// treat images differently...
|
|
186
|
-
let imageSourceChangeListener;
|
|
187
63
|
if (presentedSharedElement instanceof UIImageView) {
|
|
188
|
-
//
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
// map and the snapshot closure would leak).
|
|
192
|
-
imageSourceChangeListener = () => {
|
|
64
|
+
// in case the image is loaded async, we need to update the snapshot when it changes
|
|
65
|
+
// todo: remove listener on transition end
|
|
66
|
+
presentedView.on('imageSourceChange', () => {
|
|
193
67
|
snapshot.image = iOSUtils.snapshotView(presentedSharedElement, Screen.mainScreen.scale);
|
|
194
68
|
snapshot.tintColor = presentedSharedElement.tintColor;
|
|
195
|
-
};
|
|
196
|
-
presentedView.on('imageSourceChange', imageSourceChangeListener);
|
|
69
|
+
});
|
|
197
70
|
snapshot.tintColor = presentedSharedElement.tintColor;
|
|
198
71
|
snapshot.contentMode = presentedSharedElement.contentMode;
|
|
199
|
-
// Seed the snapshot with the source's already-loaded image so the very
|
|
200
|
-
// first frame of the animation isn't blank if the destination image is
|
|
201
|
-
// still loading.
|
|
202
|
-
if (presentingSharedElement instanceof UIImageView) {
|
|
203
|
-
snapshot.image = presentingSharedElement.image;
|
|
204
|
-
}
|
|
205
72
|
}
|
|
206
73
|
iOSUtils.copyLayerProperties(snapshot, presentingSharedElement, pageEndProps?.propertiesToMatch);
|
|
207
74
|
snapshot.clipsToBounds = true;
|
|
@@ -229,115 +96,86 @@ export class SharedTransitionHelper {
|
|
|
229
96
|
startOpacity: presentedView.opacity,
|
|
230
97
|
endOpacity: presentingView.opacity,
|
|
231
98
|
propertiesToMatch: pageEndProps?.propertiesToMatch,
|
|
232
|
-
imageSourceChangeListener,
|
|
233
99
|
});
|
|
234
100
|
// set initial opacity to match the source view opacity
|
|
235
101
|
snapshot.alpha = presentingView.opacity;
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
// style remains the user's intended value (typically 1). If we instead set
|
|
240
|
-
// `view.opacity = 0` and then a transition was interrupted before cleanup
|
|
241
|
-
// (or the page was reused), `startOpacity` here could capture 0 from a prior
|
|
242
|
-
// run and the cleanup below would leave the destination invisible.
|
|
243
|
-
presentingSharedElement.alpha = 0;
|
|
244
|
-
presentedSharedElement.alpha = 0;
|
|
102
|
+
// hide both while animating within the transition context
|
|
103
|
+
presentingView.opacity = 0;
|
|
104
|
+
presentedView.opacity = 0;
|
|
245
105
|
}
|
|
246
106
|
};
|
|
247
107
|
const positionIndependentTags = async () => {
|
|
248
|
-
//
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// (e.g. add `y: -20` to slide while fading), or opt a view out entirely
|
|
259
|
-
// with `sharedTransitionIgnore`.
|
|
260
|
-
const orphanTags = [];
|
|
261
|
-
const seenOrphan = new Set();
|
|
262
|
-
const addOrphan = (tag) => {
|
|
263
|
-
if (!tag || seenOrphan.has(tag) || sharedElementTags.includes(tag))
|
|
264
|
-
return;
|
|
265
|
-
seenOrphan.add(tag);
|
|
266
|
-
orphanTags.push(tag);
|
|
267
|
-
};
|
|
268
|
-
for (const v of presenting)
|
|
269
|
-
addOrphan(v.sharedTransitionTag);
|
|
270
|
-
for (const v of presented)
|
|
271
|
-
addOrphan(v.sharedTransitionTag);
|
|
272
|
-
// Tags the user listed in pageEnd.sharedTransitionTags but that don't
|
|
273
|
-
// match a real view are silently skipped further down — keep them in
|
|
274
|
-
// the iteration so per-tag config still applies to existing orphans.
|
|
275
|
-
for (const tag in pageEndTags)
|
|
276
|
-
addOrphan(tag);
|
|
277
|
-
for (const tag of orphanTags) {
|
|
278
|
-
const pageStartIndependentProps = pageStart?.sharedTransitionTags ? pageStart?.sharedTransitionTags[tag] : null;
|
|
279
|
-
const pageEndProps = pageEndTags[tag];
|
|
280
|
-
let independentView = presenting.find((v) => v.sharedTransitionTag === tag);
|
|
281
|
-
let isPresented = false;
|
|
282
|
-
if (!independentView) {
|
|
283
|
-
independentView = presented.find((v) => v.sharedTransitionTag === tag);
|
|
108
|
+
// independent tags
|
|
109
|
+
for (const tag in pageEndTags) {
|
|
110
|
+
// only handle if independent (otherwise it's shared between both pages and handled above)
|
|
111
|
+
if (!sharedElementTags.includes(tag)) {
|
|
112
|
+
// only consider start when there's a matching end
|
|
113
|
+
const pageStartIndependentProps = pageStart?.sharedTransitionTags ? pageStart?.sharedTransitionTags[tag] : null;
|
|
114
|
+
// console.log('start:', tag, pageStartIndependentProps);
|
|
115
|
+
const pageEndProps = pageEndTags[tag];
|
|
116
|
+
let independentView = presenting.find((v) => v.sharedTransitionTag === tag);
|
|
117
|
+
let isPresented = false;
|
|
284
118
|
if (!independentView) {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
119
|
+
independentView = presented.find((v) => v.sharedTransitionTag === tag);
|
|
120
|
+
if (!independentView) {
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
isPresented = true;
|
|
288
124
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
snapshot
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
125
|
+
const independentSharedElement = independentView.ios;
|
|
126
|
+
if (pageEndProps?.callback) {
|
|
127
|
+
await pageEndProps?.callback(independentView, 'present');
|
|
128
|
+
}
|
|
129
|
+
// let snapshot: UIImageView;
|
|
130
|
+
// if (isPresented) {
|
|
131
|
+
// snapshot = UIImageView.alloc().init();
|
|
132
|
+
// } else {
|
|
133
|
+
const snapshot = UIImageView.alloc().initWithImage(iOSUtils.snapshotView(independentSharedElement, Screen.mainScreen.scale));
|
|
134
|
+
// }
|
|
135
|
+
if (independentSharedElement instanceof UIImageView) {
|
|
136
|
+
// in case the image is loaded async, we need to update the snapshot when it changes
|
|
137
|
+
// todo: remove listener on transition end
|
|
138
|
+
// if (isPresented) {
|
|
139
|
+
// independentView.on('imageSourceChange', () => {
|
|
140
|
+
// snapshot.image = iOSNativeHelper.snapshotView(independentSharedElement, Screen.mainScreen.scale);
|
|
141
|
+
// snapshot.tintColor = independentSharedElement.tintColor;
|
|
142
|
+
// });
|
|
143
|
+
// }
|
|
144
|
+
snapshot.tintColor = independentSharedElement.tintColor;
|
|
145
|
+
snapshot.contentMode = independentSharedElement.contentMode;
|
|
146
|
+
}
|
|
147
|
+
snapshot.clipsToBounds = true;
|
|
148
|
+
const startFrame = independentSharedElement.convertRectToView(independentSharedElement.bounds, transitionContext.containerView);
|
|
149
|
+
const startFrameRect = getRectFromProps(pageStartIndependentProps);
|
|
150
|
+
// adjust for any specified start positions
|
|
151
|
+
const startFrameAdjusted = CGRectMake(startFrame.origin.x + startFrameRect.x, startFrame.origin.y + startFrameRect.y, startFrame.size.width, startFrame.size.height);
|
|
152
|
+
// console.log('startFrameAdjusted:', tag, iOSNativeHelper.printCGRect(startFrameAdjusted));
|
|
153
|
+
// if (pageStartIndependentProps?.scale) {
|
|
154
|
+
// snapshot.transform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(startFrameAdjusted.origin.x, startFrameAdjusted.origin.y), CGAffineTransformMakeScale(pageStartIndependentProps.scale.x, pageStartIndependentProps.scale.y))
|
|
155
|
+
// } else {
|
|
156
|
+
snapshot.frame = startFrame; //startFrameAdjusted;
|
|
157
|
+
// }
|
|
158
|
+
if (SharedTransition.DEBUG) {
|
|
159
|
+
console.log('---> ', independentView.sharedTransitionTag, ' frame:', iOSUtils.printCGRect(snapshot.frame));
|
|
160
|
+
}
|
|
161
|
+
const endFrameRect = getRectFromProps(pageEndProps);
|
|
162
|
+
const endFrame = CGRectMake(startFrame.origin.x + endFrameRect.x, startFrame.origin.y + endFrameRect.y, startFrame.size.width, startFrame.size.height);
|
|
163
|
+
// console.log('endFrame:', tag, iOSNativeHelper.printCGRect(endFrame));
|
|
164
|
+
transition.sharedElements.independent.push({
|
|
165
|
+
view: independentView,
|
|
166
|
+
isPresented,
|
|
167
|
+
startFrame,
|
|
168
|
+
snapshot,
|
|
169
|
+
endFrame,
|
|
170
|
+
startTransform: independentSharedElement.transform,
|
|
171
|
+
scale: pageEndProps.scale,
|
|
172
|
+
startOpacity: independentView.opacity,
|
|
173
|
+
endOpacity: isNumber(pageEndProps.opacity) ? pageEndProps.opacity : 0,
|
|
174
|
+
propertiesToMatch: pageEndProps?.propertiesToMatch,
|
|
175
|
+
zIndex: isNumber(pageEndProps?.zIndex) ? pageEndProps.zIndex : 0,
|
|
176
|
+
});
|
|
177
|
+
independentView.opacity = 0;
|
|
313
178
|
}
|
|
314
|
-
const endFrameRect = getRectFromProps(pageEndProps);
|
|
315
|
-
const endFrame = CGRectMake(startFrame.origin.x + (endFrameRect.x || 0), startFrame.origin.y + (endFrameRect.y || 0), startFrame.size.width, startFrame.size.height);
|
|
316
|
-
// Opacity defaults are side-aware: source-only orphans fade out (1 → 0),
|
|
317
|
-
// destination-only orphans fade in (0 → 1). The values are read back
|
|
318
|
-
// during dismiss as `endOpacity → startOpacity` so the return phase is
|
|
319
|
-
// automatically symmetric. Author-supplied opacity always wins.
|
|
320
|
-
const startOpacity = isNumber(pageStartIndependentProps?.opacity) ? pageStartIndependentProps.opacity : isPresented ? 0 : independentView.opacity;
|
|
321
|
-
const endOpacity = isNumber(pageEndProps?.opacity) ? pageEndProps.opacity : isPresented ? independentView.opacity : 0;
|
|
322
|
-
// Snapshot's initial visible alpha. Default UIImageView alpha is 1;
|
|
323
|
-
// destination-only orphans need to start at 0 so the fade-in is
|
|
324
|
-
// visible, hence we always set it explicitly.
|
|
325
|
-
snapshot.alpha = startOpacity;
|
|
326
|
-
transition.sharedElements.independent.push({
|
|
327
|
-
view: independentView,
|
|
328
|
-
isPresented,
|
|
329
|
-
startFrame,
|
|
330
|
-
snapshot,
|
|
331
|
-
endFrame,
|
|
332
|
-
startTransform: independentSharedElement.transform,
|
|
333
|
-
scale: pageEndProps?.scale,
|
|
334
|
-
startOpacity,
|
|
335
|
-
endOpacity,
|
|
336
|
-
propertiesToMatch: pageEndProps?.propertiesToMatch,
|
|
337
|
-
zIndex: isNumber(pageEndProps?.zIndex) ? pageEndProps.zIndex : 0,
|
|
338
|
-
});
|
|
339
|
-
// Native alpha; see comment in positionSharedTags.
|
|
340
|
-
independentSharedElement.alpha = 0;
|
|
341
179
|
}
|
|
342
180
|
};
|
|
343
181
|
// position all sharedTransitionTag elements
|
|
@@ -345,7 +183,7 @@ export class SharedTransitionHelper {
|
|
|
345
183
|
await positionIndependentTags();
|
|
346
184
|
// combine to order by zIndex and add to transition context
|
|
347
185
|
const snapshotData = transition.sharedElements.presenting.concat(transition.sharedElements.independent);
|
|
348
|
-
snapshotData.sort((a, b) => (a.zIndex
|
|
186
|
+
snapshotData.sort((a, b) => (a.zIndex > b.zIndex ? 1 : -1));
|
|
349
187
|
if (SharedTransition.DEBUG) {
|
|
350
188
|
console.log(`zIndex settings:`, snapshotData.map((s) => {
|
|
351
189
|
return {
|
|
@@ -361,41 +199,9 @@ export class SharedTransitionHelper {
|
|
|
361
199
|
// Important: always set after above shared element positions have had their start positions set
|
|
362
200
|
transition.presented.view.alpha = isNumber(pageStart?.opacity) ? pageStart?.opacity : 0;
|
|
363
201
|
transition.presented.view.frame = CGRectMake(startFrame.x, startFrame.y, startFrame.width, startFrame.height);
|
|
364
|
-
// Optional top-corner rounding for sheet-style transitions (e.g.
|
|
365
|
-
// a modal that's rounded at the bottom of the screen and flattens
|
|
366
|
-
// as it slides up). Setting maskedCorners and masksToBounds here
|
|
367
|
-
// (before the animation) so they're stable during the animated
|
|
368
|
-
// cornerRadius change.
|
|
369
|
-
if (isNumber(pageStart?.cornerRadius) || isNumber(pageEnd?.cornerRadius)) {
|
|
370
|
-
const layer = transition.presented.view.layer;
|
|
371
|
-
// top-left | top-right | bottom-left | bottom-right (CACornerMask bits)
|
|
372
|
-
layer.maskedCorners = 15;
|
|
373
|
-
layer.masksToBounds = true;
|
|
374
|
-
if (isNumber(pageStart?.cornerRadius)) {
|
|
375
|
-
layer.cornerRadius = pageStart.cornerRadius;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
202
|
const cleanupPresent = () => {
|
|
379
203
|
for (const presented of transition.sharedElements.presented) {
|
|
380
|
-
|
|
381
|
-
// during positionSharedTags so it doesn't leak (the snapshot is gone).
|
|
382
|
-
if (presented.imageSourceChangeListener) {
|
|
383
|
-
presented.view.off('imageSourceChange', presented.imageSourceChangeListener);
|
|
384
|
-
presented.imageSourceChangeListener = null;
|
|
385
|
-
}
|
|
386
|
-
// Restore the destination native alpha from the NS opacity (the source
|
|
387
|
-
// of truth). NS opacity wasn't modified during the transition, so this
|
|
388
|
-
// reflects the user's intended visibility (typically 1).
|
|
389
|
-
presented.view.ios.alpha = presented.view.opacity;
|
|
390
|
-
}
|
|
391
|
-
// Restore native alpha on every source view we touched — including
|
|
392
|
-
// duplicates that share a sharedTransitionTag and were hidden without
|
|
393
|
-
// a snapshot. Even though the source page is being removed, the View
|
|
394
|
-
// instances may be reused on subsequent navigations.
|
|
395
|
-
for (const sourceView of transition.sharedElements._allPresentingViews || []) {
|
|
396
|
-
if (sourceView?.ios) {
|
|
397
|
-
sourceView.ios.alpha = sourceView.opacity;
|
|
398
|
-
}
|
|
204
|
+
presented.view.opacity = presented.startOpacity;
|
|
399
205
|
}
|
|
400
206
|
for (const presenting of transition.sharedElements.presenting) {
|
|
401
207
|
presenting.snapshot.removeFromSuperview();
|
|
@@ -403,7 +209,7 @@ export class SharedTransitionHelper {
|
|
|
403
209
|
for (const independent of transition.sharedElements.independent) {
|
|
404
210
|
independent.snapshot.removeFromSuperview();
|
|
405
211
|
if (independent.isPresented) {
|
|
406
|
-
independent.view.
|
|
212
|
+
independent.view.opacity = independent.startOpacity;
|
|
407
213
|
}
|
|
408
214
|
}
|
|
409
215
|
SharedTransition.updateState(transition.id, {
|
|
@@ -426,12 +232,6 @@ export class SharedTransitionHelper {
|
|
|
426
232
|
transition.presented.view.alpha = isNumber(pageEnd?.opacity) ? pageEnd?.opacity : 1;
|
|
427
233
|
const endFrame = getRectFromProps(pageEnd);
|
|
428
234
|
transition.presented.view.frame = CGRectMake(endFrame.x, endFrame.y, endFrame.width, endFrame.height);
|
|
429
|
-
// Animate cornerRadius alongside the page frame. Implicit animation
|
|
430
|
-
// of cornerRadius is supported on iOS 11+, both inside
|
|
431
|
-
// UIView.animateWith… and UIViewPropertyAnimator's animations block.
|
|
432
|
-
if (isNumber(pageEnd?.cornerRadius)) {
|
|
433
|
-
transition.presented.view.layer.cornerRadius = pageEnd.cornerRadius;
|
|
434
|
-
}
|
|
435
235
|
if (pageOut) {
|
|
436
236
|
if (isNumber(pageOut.opacity)) {
|
|
437
237
|
transition.presenting.view.alpha = pageOut?.opacity;
|
|
@@ -510,23 +310,6 @@ export class SharedTransitionHelper {
|
|
|
510
310
|
});
|
|
511
311
|
if (type === 'page') {
|
|
512
312
|
transitionContext.containerView.insertSubviewBelowSubview(transition.presenting.view, transition.presented.view);
|
|
513
|
-
// UIKit's standard pop places the incoming source view off to the
|
|
514
|
-
// left so it can slide into position; for custom shared transitions
|
|
515
|
-
// (where the destination either slides down or morphs to source) we
|
|
516
|
-
// don't want that lateral slide. Pin the source to its fullscreen
|
|
517
|
-
// frame so it stays put underneath the animating destination.
|
|
518
|
-
// Users can still opt into a custom source animation via `pageOut`.
|
|
519
|
-
const sourceDefaults = getRectFromProps(null);
|
|
520
|
-
transition.presenting.view.frame = CGRectMake(0, 0, sourceDefaults.width, sourceDefaults.height);
|
|
521
|
-
}
|
|
522
|
-
// Morph + interactive dismiss: the gesture handler drives the
|
|
523
|
-
// animation via transforms and finalizes by calling cancel/finish
|
|
524
|
-
// on the transitionContext. Running the standard snapshot + spring
|
|
525
|
-
// rig here would race that and frequently call completeTransition(true)
|
|
526
|
-
// *before* the user's gesture ended — causing the dismiss to commit
|
|
527
|
-
// even on a cancel. Skip the rest of the dismiss setup entirely.
|
|
528
|
-
if (state.interactive?.dismiss?.morph && state.interactiveBegan) {
|
|
529
|
-
return;
|
|
530
313
|
}
|
|
531
314
|
// console.log('transitionContext.containerView.subviews.count:', transitionContext.containerView.subviews.count);
|
|
532
315
|
if (SharedTransition.DEBUG) {
|
|
@@ -539,21 +322,11 @@ export class SharedTransitionHelper {
|
|
|
539
322
|
const pageEndTags = pageEnd?.sharedTransitionTags || {};
|
|
540
323
|
const pageReturn = state.pageReturn;
|
|
541
324
|
for (const p of transition.sharedElements.presented) {
|
|
542
|
-
|
|
543
|
-
// value if the dismiss is interrupted; cleanup restores from NS opacity.
|
|
544
|
-
p.view.ios.alpha = 0;
|
|
545
|
-
}
|
|
546
|
-
// Ensure top-corner masking + clipping is in place before we animate
|
|
547
|
-
// cornerRadius back toward the start value. If the present pass already
|
|
548
|
-
// set these, this is a harmless no-op.
|
|
549
|
-
if (isNumber(pageReturn?.cornerRadius)) {
|
|
550
|
-
const layer = transition.presented.view.layer;
|
|
551
|
-
layer.maskedCorners = 15; // top-left | top-right | bottom-left | bottom-right
|
|
552
|
-
layer.masksToBounds = true;
|
|
325
|
+
p.view.opacity = 0;
|
|
553
326
|
}
|
|
554
327
|
// combine to order by zIndex and add to transition context
|
|
555
328
|
const snapshotData = transition.sharedElements.presenting.concat(transition.sharedElements.independent);
|
|
556
|
-
snapshotData.sort((a, b) => (a.zIndex
|
|
329
|
+
snapshotData.sort((a, b) => (a.zIndex > b.zIndex ? 1 : -1));
|
|
557
330
|
if (SharedTransition.DEBUG) {
|
|
558
331
|
console.log(`zIndex settings:`, snapshotData.map((s) => {
|
|
559
332
|
return {
|
|
@@ -592,43 +365,13 @@ export class SharedTransitionHelper {
|
|
|
592
365
|
// add snapshot to animate
|
|
593
366
|
transitionContext.containerView.addSubview(data.snapshot);
|
|
594
367
|
}
|
|
595
|
-
// Hide every source-side shared element now that snapshots have been
|
|
596
|
-
// captured. The animating snapshot covers them; without this, the user
|
|
597
|
-
// sees a "double image" — the real source element AND the snapshot
|
|
598
|
-
// animating over it back to its position. Use the full list (not just
|
|
599
|
-
// the deduped `presenting`) so duplicates from sister lists are hidden
|
|
600
|
-
// too. Restored in cleanupDismiss below.
|
|
601
|
-
for (const sourceView of transition.sharedElements._allPresentingViews || []) {
|
|
602
|
-
if (sourceView?.ios) {
|
|
603
|
-
sourceView.ios.alpha = 0;
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
368
|
const cleanupDismiss = () => {
|
|
607
|
-
// Restore alpha on every source view we hid — including duplicate
|
|
608
|
-
// sources with the same sharedTransitionTag — using NS opacity as
|
|
609
|
-
// the source of truth.
|
|
610
|
-
for (const sourceView of transition.sharedElements._allPresentingViews || []) {
|
|
611
|
-
if (sourceView?.ios) {
|
|
612
|
-
sourceView.ios.alpha = sourceView.opacity;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
369
|
for (const presenting of transition.sharedElements.presenting) {
|
|
370
|
+
presenting.view.opacity = presenting.startOpacity;
|
|
616
371
|
presenting.snapshot.removeFromSuperview();
|
|
617
372
|
}
|
|
618
|
-
for (const presented of transition.sharedElements.presented) {
|
|
619
|
-
// Detach the imageSourceChange listener and restore alpha. Even though
|
|
620
|
-
// the destination page is being torn down, NS Views can outlive their
|
|
621
|
-
// nativeView and we shouldn't leak observers.
|
|
622
|
-
if (presented.imageSourceChangeListener) {
|
|
623
|
-
presented.view.off('imageSourceChange', presented.imageSourceChangeListener);
|
|
624
|
-
presented.imageSourceChangeListener = null;
|
|
625
|
-
}
|
|
626
|
-
if (presented.view.ios) {
|
|
627
|
-
presented.view.ios.alpha = presented.view.opacity;
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
373
|
for (const independent of transition.sharedElements.independent) {
|
|
631
|
-
independent.view.
|
|
374
|
+
independent.view.opacity = independent.startOpacity;
|
|
632
375
|
independent.snapshot.removeFromSuperview();
|
|
633
376
|
}
|
|
634
377
|
SharedTransition.finishState(transition.id);
|
|
@@ -647,11 +390,6 @@ export class SharedTransitionHelper {
|
|
|
647
390
|
transition.presented.view.alpha = isNumber(pageReturn?.opacity) ? pageReturn?.opacity : 0;
|
|
648
391
|
const endFrame = getRectFromProps(pageReturn, getPageStartDefaultsForType(type));
|
|
649
392
|
transition.presented.view.frame = CGRectMake(endFrame.x, endFrame.y, endFrame.width, endFrame.height);
|
|
650
|
-
// Animate cornerRadius back toward the start value (e.g. a sheet
|
|
651
|
-
// re-rounds as it slides offscreen).
|
|
652
|
-
if (isNumber(pageReturn?.cornerRadius)) {
|
|
653
|
-
transition.presented.view.layer.cornerRadius = pageReturn.cornerRadius;
|
|
654
|
-
}
|
|
655
393
|
if (pageOut) {
|
|
656
394
|
// always return to defaults if pageOut had been used
|
|
657
395
|
transition.presenting.view.alpha = 1;
|
|
@@ -712,78 +450,20 @@ export class SharedTransitionHelper {
|
|
|
712
450
|
switch (type) {
|
|
713
451
|
case 'page':
|
|
714
452
|
interactiveState.transitionContext.containerView.insertSubviewBelowSubview(state.instance.presenting.view, state.instance.presented.view);
|
|
715
|
-
// Pin the source view to its fullscreen frame so it doesn't slide
|
|
716
|
-
// in from off-left as the user drags. The default UIKit pop expects
|
|
717
|
-
// to animate the source from off-left; for shared-element / morph
|
|
718
|
-
// transitions we want the source to stay put underneath the
|
|
719
|
-
// animating destination so the visuals feel connected.
|
|
720
|
-
{
|
|
721
|
-
const sourceDefaults = getRectFromProps(null);
|
|
722
|
-
state.instance.presenting.view.frame = CGRectMake(0, 0, sourceDefaults.width, sourceDefaults.height);
|
|
723
|
-
}
|
|
724
453
|
break;
|
|
725
454
|
}
|
|
726
|
-
// Apply the dismiss shadow here, not from the gesture handler. UIKit
|
|
727
|
-
// reparents the presented view into the transition containerView as
|
|
728
|
-
// part of starting the interactive transition; applying earlier would
|
|
729
|
-
// attach the shadow to the now-orphaned old superlayer.
|
|
730
|
-
if (state.interactive?.dismiss?.shadow && state.instance?.presented?.view) {
|
|
731
|
-
applyInteractiveDismissShadow(state.instance.presented.view, state.interactive.dismiss.shadow);
|
|
732
|
-
}
|
|
733
455
|
}
|
|
734
456
|
static interactiveUpdate(state, interactiveState, type, percent) {
|
|
735
457
|
if (interactiveState) {
|
|
736
|
-
if (state.interactive?.dismiss?.morph) {
|
|
737
|
-
// Morph mode: the gesture handler drives the destination view's
|
|
738
|
-
// transform directly. We only forward the event so observers can
|
|
739
|
-
// react to the gesture's percentage.
|
|
740
|
-
SharedTransition.notifyEvent(SharedTransition.interactiveUpdateEvent, {
|
|
741
|
-
id: state?.instance?.id,
|
|
742
|
-
type,
|
|
743
|
-
percent,
|
|
744
|
-
});
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
458
|
if (!interactiveState.added) {
|
|
748
|
-
// Defer setup until the gesture has clearly committed to a horizontal
|
|
749
|
-
// dismiss motion. A vertical scroll on a descendant scroll view also
|
|
750
|
-
// triggers the page-level pan gesture (NS pan recognizers don't yield
|
|
751
|
-
// to scroll-view pans by default), producing near-zero percent values.
|
|
752
|
-
// Without this guard we'd mount the dismiss snapshots and hide the
|
|
753
|
-
// destination during a scroll, making the image look "stuck" until the
|
|
754
|
-
// cancel animation runs.
|
|
755
|
-
if (Math.abs(percent) < 0.05) {
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
459
|
interactiveState.added = true;
|
|
759
460
|
for (const p of state.instance.sharedElements.presented) {
|
|
760
|
-
|
|
761
|
-
// if the user lets go without crossing the threshold, cancel uses NS
|
|
762
|
-
// opacity as source of truth to restore.
|
|
763
|
-
p.view.ios.alpha = 0;
|
|
461
|
+
p.view.opacity = 0;
|
|
764
462
|
}
|
|
765
463
|
for (const p of state.instance.sharedElements.presenting) {
|
|
766
464
|
p.snapshot.alpha = p.endOpacity;
|
|
767
465
|
interactiveState.transitionContext.containerView.addSubview(p.snapshot);
|
|
768
466
|
}
|
|
769
|
-
// Re-mount independent (orphan) snapshots so they're available to
|
|
770
|
-
// animate alongside the gesture. They were removed in present cleanup
|
|
771
|
-
// but the records (including their snapshots) live on transition.sharedElements.
|
|
772
|
-
// Source-only orphans were left at alpha 0 since present; their snapshots
|
|
773
|
-
// pick up where the present animation left off (endOpacity).
|
|
774
|
-
for (const ind of state.instance.sharedElements.independent) {
|
|
775
|
-
ind.snapshot.alpha = ind.endOpacity;
|
|
776
|
-
interactiveState.transitionContext.containerView.addSubview(ind.snapshot);
|
|
777
|
-
}
|
|
778
|
-
// Hide every source-side shared element (including duplicates that share
|
|
779
|
-
// a sharedTransitionTag in sister lists) so the user only sees the
|
|
780
|
-
// animating snapshot — not the snapshot AND the real source element
|
|
781
|
-
// simultaneously ("double image" during interactive dismissal).
|
|
782
|
-
for (const sourceView of state.instance.sharedElements._allPresentingViews || []) {
|
|
783
|
-
if (sourceView?.ios) {
|
|
784
|
-
sourceView.ios.alpha = 0;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
467
|
const pageStart = state.pageStart;
|
|
788
468
|
const startFrame = getRectFromProps(pageStart, getPageStartDefaultsForType(type));
|
|
789
469
|
interactiveState.propertyAnimator = UIViewPropertyAnimator.alloc().initWithDurationDampingRatioAnimations(1, 1, () => {
|
|
@@ -792,25 +472,8 @@ export class SharedTransitionHelper {
|
|
|
792
472
|
iOSUtils.copyLayerProperties(p.snapshot, p.view.ios, p.propertiesToMatch);
|
|
793
473
|
p.snapshot.alpha = 1;
|
|
794
474
|
}
|
|
795
|
-
// Animate orphan snapshots back toward their start state alongside
|
|
796
|
-
// the rest of the dismiss. For source-only orphans this is the
|
|
797
|
-
// fade-back-in that mirrors the fade-out of present.
|
|
798
|
-
for (const ind of state.instance.sharedElements.independent) {
|
|
799
|
-
ind.snapshot.alpha = ind.startOpacity;
|
|
800
|
-
if (ind.scale) {
|
|
801
|
-
ind.snapshot.transform = ind.startTransform;
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
804
|
-
ind.snapshot.frame = ind.startFrame;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
475
|
state.instance.presented.view.alpha = isNumber(state.pageReturn?.opacity) ? state.pageReturn?.opacity : 0;
|
|
808
476
|
state.instance.presented.view.frame = CGRectMake(startFrame.x, startFrame.y, state.instance.presented.view.bounds.size.width, state.instance.presented.view.bounds.size.height);
|
|
809
|
-
// Drive cornerRadius via the same percent the user controls — round
|
|
810
|
-
// the page back up as they pan it offscreen.
|
|
811
|
-
if (isNumber(state.pageReturn?.cornerRadius)) {
|
|
812
|
-
state.instance.presented.view.layer.cornerRadius = state.pageReturn.cornerRadius;
|
|
813
|
-
}
|
|
814
477
|
});
|
|
815
478
|
}
|
|
816
479
|
interactiveState.propertyAnimator.fractionComplete = percent;
|
|
@@ -822,108 +485,17 @@ export class SharedTransitionHelper {
|
|
|
822
485
|
}
|
|
823
486
|
}
|
|
824
487
|
static interactiveCancel(state, interactiveState, type) {
|
|
825
|
-
if (state?.instance && interactiveState && state.interactive?.dismiss?.morph) {
|
|
826
|
-
// Morph mode cancel — spring the destination view back to identity.
|
|
827
|
-
const view = state.instance.presented?.view;
|
|
828
|
-
let didFinalize = false;
|
|
829
|
-
const finalize = () => {
|
|
830
|
-
if (didFinalize)
|
|
831
|
-
return;
|
|
832
|
-
didFinalize = true;
|
|
833
|
-
if (view)
|
|
834
|
-
view.transform = CGAffineTransformIdentity;
|
|
835
|
-
// Restore the source-side element alphas that the gesture hid on
|
|
836
|
-
// engagement so the source page is correct when shown again.
|
|
837
|
-
for (const v of state.instance.sharedElements?._allPresentingViews || []) {
|
|
838
|
-
if (v?.ios)
|
|
839
|
-
v.ios.alpha = v.opacity;
|
|
840
|
-
}
|
|
841
|
-
if (interactiveState.transitionContext) {
|
|
842
|
-
interactiveState.transitionContext.cancelInteractiveTransition();
|
|
843
|
-
interactiveState.transitionContext.completeTransition(false);
|
|
844
|
-
}
|
|
845
|
-
SharedTransition.updateState(state?.instance?.id, {
|
|
846
|
-
interactiveBegan: false,
|
|
847
|
-
interactiveCancelled: true,
|
|
848
|
-
});
|
|
849
|
-
SharedTransition.notifyEvent(SharedTransition.interactiveCancelledEvent, {
|
|
850
|
-
id: state?.instance?.id,
|
|
851
|
-
type,
|
|
852
|
-
});
|
|
853
|
-
};
|
|
854
|
-
if (view) {
|
|
855
|
-
iOSUtils.animateWithSpring({
|
|
856
|
-
animations: () => {
|
|
857
|
-
view.transform = CGAffineTransformIdentity;
|
|
858
|
-
// Cross-fade sources back IN PARALLEL with the spring so
|
|
859
|
-
// they're visible from frame one of the snap-back. This
|
|
860
|
-
// also makes the source restoration robust to a new
|
|
861
|
-
// gesture interrupting our completion callback.
|
|
862
|
-
for (const v of state.instance.sharedElements?._allPresentingViews || []) {
|
|
863
|
-
if (v?.ios)
|
|
864
|
-
v.ios.alpha = v.opacity;
|
|
865
|
-
}
|
|
866
|
-
},
|
|
867
|
-
completion: () => finalize(),
|
|
868
|
-
});
|
|
869
|
-
// Safety: ensure finalize runs even if the animation completion
|
|
870
|
-
// is dropped (e.g. interrupted by another gesture).
|
|
871
|
-
setTimeout(finalize, 800);
|
|
872
|
-
}
|
|
873
|
-
else {
|
|
874
|
-
finalize();
|
|
875
|
-
}
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
if (state?.instance && interactiveState && !interactiveState.added) {
|
|
879
|
-
// The gesture engaged the interactive transition but never crossed the
|
|
880
|
-
// motion threshold in interactiveUpdate (e.g., the user was scrolling
|
|
881
|
-
// vertically). Nothing was visually set up — just tell UIKit we're
|
|
882
|
-
// cancelling and clear the flags.
|
|
883
|
-
if (interactiveState.transitionContext) {
|
|
884
|
-
interactiveState.transitionContext.cancelInteractiveTransition();
|
|
885
|
-
interactiveState.transitionContext.completeTransition(false);
|
|
886
|
-
}
|
|
887
|
-
SharedTransition.updateState(state?.instance?.id, {
|
|
888
|
-
interactiveBegan: false,
|
|
889
|
-
interactiveCancelled: true,
|
|
890
|
-
});
|
|
891
|
-
SharedTransition.notifyEvent(SharedTransition.interactiveCancelledEvent, {
|
|
892
|
-
id: state?.instance?.id,
|
|
893
|
-
type,
|
|
894
|
-
});
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
488
|
if (state?.instance && interactiveState?.added && interactiveState?.propertyAnimator) {
|
|
898
489
|
interactiveState.propertyAnimator.reversed = true;
|
|
899
490
|
const duration = isNumber(state.pageEnd?.duration) ? state.pageEnd?.duration / 1000 : CORE_ANIMATION_DEFAULTS.duration;
|
|
900
491
|
interactiveState.propertyAnimator.continueAnimationWithTimingParametersDurationFactor(null, duration);
|
|
901
492
|
setTimeout(() => {
|
|
902
493
|
for (const p of state.instance.sharedElements.presented) {
|
|
903
|
-
|
|
904
|
-
p.view.ios.alpha = p.view.opacity;
|
|
905
|
-
}
|
|
906
|
-
// Restore alpha on every source view we hid during interactiveUpdate
|
|
907
|
-
// (including duplicates from sister lists).
|
|
908
|
-
for (const sourceView of state.instance.sharedElements._allPresentingViews || []) {
|
|
909
|
-
if (sourceView?.ios) {
|
|
910
|
-
sourceView.ios.alpha = sourceView.opacity;
|
|
911
|
-
}
|
|
494
|
+
p.view.opacity = 1;
|
|
912
495
|
}
|
|
913
496
|
for (const p of state.instance.sharedElements.presenting) {
|
|
914
497
|
p.snapshot.removeFromSuperview();
|
|
915
498
|
}
|
|
916
|
-
// Tear down orphan snapshots that interactiveUpdate re-mounted. The
|
|
917
|
-
// source-only orphans' view.alpha stays at 0 — we're returning to the
|
|
918
|
-
// presented (modal-on-top) state, so they should remain hidden until
|
|
919
|
-
// a real dismiss completes. Cancel/finish for the next dismiss owns
|
|
920
|
-
// the eventual restore.
|
|
921
|
-
for (const ind of state.instance.sharedElements.independent) {
|
|
922
|
-
ind.snapshot.removeFromSuperview();
|
|
923
|
-
// Reset snapshot alpha to its post-present end state so a subsequent
|
|
924
|
-
// interactiveUpdate picks up from the right place.
|
|
925
|
-
ind.snapshot.alpha = ind.endOpacity;
|
|
926
|
-
}
|
|
927
499
|
state.instance.presented.view.alpha = 1;
|
|
928
500
|
interactiveState.propertyAnimator = null;
|
|
929
501
|
interactiveState.added = false;
|
|
@@ -941,263 +513,15 @@ export class SharedTransitionHelper {
|
|
|
941
513
|
}
|
|
942
514
|
}
|
|
943
515
|
static interactiveFinish(state, interactiveState, type) {
|
|
944
|
-
if (state?.instance && interactiveState && state.interactive?.dismiss?.morph) {
|
|
945
|
-
// Morph mode finish — animate the destination view to the matching
|
|
946
|
-
// source element's frame and fade it out, then complete.
|
|
947
|
-
const view = state.instance.presented?.view;
|
|
948
|
-
const destPresented = state.instance.sharedElements?.presented?.[0];
|
|
949
|
-
const destTag = destPresented?.view?.sharedTransitionTag;
|
|
950
|
-
const matchingSource = state.instance.sharedElements?.presenting?.find?.((p) => p.view.sharedTransitionTag === destTag);
|
|
951
|
-
const srcIos = matchingSource?.view?.ios;
|
|
952
|
-
const destSharedIos = destPresented?.view?.ios;
|
|
953
|
-
const containerView = interactiveState.transitionContext?.containerView;
|
|
954
|
-
let targetTransform = null;
|
|
955
|
-
if (view && srcIos && containerView) {
|
|
956
|
-
const sourceFrame = srcIos.convertRectToView(srcIos.bounds, containerView);
|
|
957
|
-
const bounds = view.bounds;
|
|
958
|
-
if (bounds.size.width > 0 && bounds.size.height > 0 && sourceFrame.size.width > 0 && sourceFrame.size.height > 0) {
|
|
959
|
-
// Anchor the modal's transform on the *internal* matched
|
|
960
|
-
// element (e.g. the album art inside the modal) so that the
|
|
961
|
-
// modal and the matched element converge along the same
|
|
962
|
-
// trajectory to the source thumbnail. Without this, the modal
|
|
963
|
-
// scales around its own center while the matched-element
|
|
964
|
-
// snapshot flies separately to source — making the two visibly
|
|
965
|
-
// drift apart mid-spring.
|
|
966
|
-
//
|
|
967
|
-
// Pick a uniform scale that takes the modal's matched element
|
|
968
|
-
// to the source's size. For square album art (both sides) this
|
|
969
|
-
// is exact; for slightly off-square sources (e.g. 360×380) we
|
|
970
|
-
// match width and let the small remainder be hidden by the
|
|
971
|
-
// fade-out.
|
|
972
|
-
const destSharedFrameInModal = destSharedIos ? destSharedIos.convertRectToView(destSharedIos.bounds, view) : null;
|
|
973
|
-
const sx = sourceFrame.size.width / bounds.size.width;
|
|
974
|
-
const sy = sourceFrame.size.height / bounds.size.height;
|
|
975
|
-
const targetCx = sourceFrame.origin.x + sourceFrame.size.width / 2;
|
|
976
|
-
const targetCy = sourceFrame.origin.y + sourceFrame.size.height / 2;
|
|
977
|
-
const modalCx = bounds.size.width / 2;
|
|
978
|
-
const modalCy = bounds.size.height / 2;
|
|
979
|
-
let tx;
|
|
980
|
-
let ty;
|
|
981
|
-
if (destSharedFrameInModal && destSharedFrameInModal.size.width > 0 && destSharedFrameInModal.size.height > 0) {
|
|
982
|
-
// Place the internal album art's center on the source's
|
|
983
|
-
// center. After scaling around modal center, internal art's
|
|
984
|
-
// visual center = modalCenter + (internalArtCenter - modalCenter) * scale + translation.
|
|
985
|
-
const internalCx = destSharedFrameInModal.origin.x + destSharedFrameInModal.size.width / 2;
|
|
986
|
-
const internalCy = destSharedFrameInModal.origin.y + destSharedFrameInModal.size.height / 2;
|
|
987
|
-
tx = targetCx - modalCx - (internalCx - modalCx) * sx;
|
|
988
|
-
ty = targetCy - modalCy - (internalCy - modalCy) * sy;
|
|
989
|
-
}
|
|
990
|
-
else {
|
|
991
|
-
// Fallback: align modal center on source center.
|
|
992
|
-
tx = targetCx - modalCx;
|
|
993
|
-
ty = targetCy - modalCy;
|
|
994
|
-
}
|
|
995
|
-
targetTransform = CGAffineTransformConcat(CGAffineTransformMakeTranslation(tx, ty), CGAffineTransformMakeScale(sx, sy));
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
// Build a snapshot fly-back for each shared element so the album
|
|
999
|
-
// art (etc.) does a real shared-element transition to its source
|
|
1000
|
-
// frame in parallel with the destination view's shrink+fade.
|
|
1001
|
-
// Without this, the image rides along with the morphing view and
|
|
1002
|
-
// lands at a visually offset position rather than precisely back
|
|
1003
|
-
// at the source thumbnail.
|
|
1004
|
-
const sharedSnapshots = [];
|
|
1005
|
-
if (containerView) {
|
|
1006
|
-
for (const presented of state.instance.sharedElements?.presented || []) {
|
|
1007
|
-
const destSharedView = presented.view?.ios;
|
|
1008
|
-
const tag = presented.view?.sharedTransitionTag;
|
|
1009
|
-
if (!destSharedView || !tag)
|
|
1010
|
-
continue;
|
|
1011
|
-
const sourceShared = state.instance.sharedElements?.presenting?.find?.((p) => p.view.sharedTransitionTag === tag);
|
|
1012
|
-
const srcSharedView = sourceShared?.view?.ios;
|
|
1013
|
-
if (!srcSharedView)
|
|
1014
|
-
continue;
|
|
1015
|
-
const startFrame = destSharedView.convertRectToView(destSharedView.bounds, containerView);
|
|
1016
|
-
const endFrame = srcSharedView.convertRectToView(srcSharedView.bounds, containerView);
|
|
1017
|
-
if (startFrame.size.width <= 0 || endFrame.size.width <= 0)
|
|
1018
|
-
continue;
|
|
1019
|
-
const snap = UIImageView.alloc().init();
|
|
1020
|
-
if (destSharedView instanceof UIImageView && destSharedView.image) {
|
|
1021
|
-
snap.image = destSharedView.image;
|
|
1022
|
-
snap.contentMode = destSharedView.contentMode;
|
|
1023
|
-
snap.tintColor = destSharedView.tintColor;
|
|
1024
|
-
}
|
|
1025
|
-
else {
|
|
1026
|
-
try {
|
|
1027
|
-
snap.image = iOSUtils.snapshotView(presented.view.ios, Screen.mainScreen.scale);
|
|
1028
|
-
}
|
|
1029
|
-
catch (e) {
|
|
1030
|
-
continue;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
snap.clipsToBounds = true;
|
|
1034
|
-
snap.layer.cornerRadius = destSharedView.layer?.cornerRadius || 0;
|
|
1035
|
-
snap.frame = startFrame;
|
|
1036
|
-
containerView.addSubview(snap);
|
|
1037
|
-
// Hide the destination shared element so we don't see it
|
|
1038
|
-
// shrinking inside the morphing view at the same time the
|
|
1039
|
-
// snapshot is flying back to source — that would double up.
|
|
1040
|
-
destSharedView.alpha = 0;
|
|
1041
|
-
sharedSnapshots.push({
|
|
1042
|
-
snap,
|
|
1043
|
-
endFrame,
|
|
1044
|
-
endCornerRadius: srcSharedView.layer?.cornerRadius || 0,
|
|
1045
|
-
srcSharedView,
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
// Identify which source NS views have a matching fly-back snapshot.
|
|
1050
|
-
// Those sources stay at alpha 0 through the spring and are restored
|
|
1051
|
-
// inside finalize(), so the snapshot doesn't overlay an already-
|
|
1052
|
-
// visible source mid-flight (which looks like two images for a beat).
|
|
1053
|
-
const snapshottedSources = new Set();
|
|
1054
|
-
for (const entry of sharedSnapshots) {
|
|
1055
|
-
if (entry.srcSharedView)
|
|
1056
|
-
snapshottedSources.add(entry.srcSharedView);
|
|
1057
|
-
}
|
|
1058
|
-
// Source-only orphan views (album thumbnails, badges, etc.) were
|
|
1059
|
-
// already restored to their opacity by the gesture handler at morph
|
|
1060
|
-
// engagement, so we don't need fade-in snapshots here — they're
|
|
1061
|
-
// already visible behind the morphing modal.
|
|
1062
|
-
let didFinalize = false;
|
|
1063
|
-
const finalize = () => {
|
|
1064
|
-
if (didFinalize)
|
|
1065
|
-
return;
|
|
1066
|
-
didFinalize = true;
|
|
1067
|
-
// Detach the imageSourceChange listeners that were attached during
|
|
1068
|
-
// PRESENT — the destination View is about to be torn down.
|
|
1069
|
-
for (const presented of state.instance.sharedElements?.presented || []) {
|
|
1070
|
-
if (presented.imageSourceChangeListener) {
|
|
1071
|
-
presented.view.off('imageSourceChange', presented.imageSourceChangeListener);
|
|
1072
|
-
presented.imageSourceChangeListener = null;
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
// Restore the snapshotted sources (held at alpha 0 through the
|
|
1076
|
-
// spring) immediately before removing the snapshot, so the
|
|
1077
|
-
// snapshot's last frame and the source's first visible frame
|
|
1078
|
-
// coincide exactly — no double-image, no gap.
|
|
1079
|
-
for (const entry of sharedSnapshots) {
|
|
1080
|
-
const srcSharedView = entry.srcSharedView;
|
|
1081
|
-
if (srcSharedView) {
|
|
1082
|
-
const nsView = state.instance.sharedElements?.presenting?.find?.((p) => p.view?.ios === srcSharedView)?.view;
|
|
1083
|
-
srcSharedView.alpha = nsView ? nsView.opacity : 1;
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
for (const { snap } of sharedSnapshots) {
|
|
1087
|
-
snap.removeFromSuperview();
|
|
1088
|
-
}
|
|
1089
|
-
// Independent (orphan) views were restored at gesture engagement,
|
|
1090
|
-
// so they're already visible. Just tear down any leftover present-
|
|
1091
|
-
// phase snapshots that weren't removed in present cleanup, and
|
|
1092
|
-
// ensure alpha is set from NS opacity for safety in case the
|
|
1093
|
-
// engagement code path was bypassed.
|
|
1094
|
-
for (const independent of state.instance?.sharedElements?.independent || []) {
|
|
1095
|
-
if (independent.view?.ios) {
|
|
1096
|
-
independent.view.ios.alpha = independent.view.opacity;
|
|
1097
|
-
}
|
|
1098
|
-
independent.snapshot?.removeFromSuperview();
|
|
1099
|
-
}
|
|
1100
|
-
if (view) {
|
|
1101
|
-
view.transform = CGAffineTransformIdentity;
|
|
1102
|
-
view.alpha = 1;
|
|
1103
|
-
}
|
|
1104
|
-
// Remove the sibling shadow view if interactive engagement added one.
|
|
1105
|
-
removeInteractiveDismissShadow(view);
|
|
1106
|
-
SharedTransition.finishState(state.instance.id);
|
|
1107
|
-
if (interactiveState.transitionContext) {
|
|
1108
|
-
interactiveState.transitionContext.finishInteractiveTransition();
|
|
1109
|
-
interactiveState.transitionContext.completeTransition(true);
|
|
1110
|
-
}
|
|
1111
|
-
SharedTransition.notifyEvent(SharedTransition.finishedEvent, {
|
|
1112
|
-
id: state?.instance?.id,
|
|
1113
|
-
type,
|
|
1114
|
-
action: 'interactiveFinish',
|
|
1115
|
-
});
|
|
1116
|
-
};
|
|
1117
|
-
if (view && targetTransform) {
|
|
1118
|
-
const shadowViewToAnimate = view.__sharedTransitionShadowView;
|
|
1119
|
-
iOSUtils.animateWithSpring({
|
|
1120
|
-
animations: () => {
|
|
1121
|
-
view.transform = targetTransform;
|
|
1122
|
-
view.alpha = 0;
|
|
1123
|
-
// Drive the sibling shadow view through the same spring so it
|
|
1124
|
-
// shrinks + translates with the modal as it morphs to the
|
|
1125
|
-
// source. Also fade it out so it doesn't linger behind the
|
|
1126
|
-
// finalizing snapshot.
|
|
1127
|
-
if (shadowViewToAnimate) {
|
|
1128
|
-
shadowViewToAnimate.transform = targetTransform;
|
|
1129
|
-
shadowViewToAnimate.alpha = 0;
|
|
1130
|
-
}
|
|
1131
|
-
// Restore source-side element alphas IN PARALLEL with the
|
|
1132
|
-
// destination's morph + fade — but skip sources that have
|
|
1133
|
-
// a flying snapshot. Those are held at alpha 0 and
|
|
1134
|
-
// revealed by finalize() right before the snapshot is
|
|
1135
|
-
// removed, so the user never sees both the snapshot AND
|
|
1136
|
-
// the source visible at the same time.
|
|
1137
|
-
for (const v of state.instance.sharedElements?._allPresentingViews || []) {
|
|
1138
|
-
if (v?.ios && !snapshottedSources.has(v.ios)) {
|
|
1139
|
-
v.ios.alpha = v.opacity;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
// Source-only orphan views were already restored at
|
|
1143
|
-
// engagement; nothing more to do for them here.
|
|
1144
|
-
// Fly each shared-element snapshot to its source frame.
|
|
1145
|
-
// Rides the same spring so the image lands precisely back
|
|
1146
|
-
// at the source position as the destination view fades.
|
|
1147
|
-
for (const { snap, endFrame, endCornerRadius } of sharedSnapshots) {
|
|
1148
|
-
snap.frame = endFrame;
|
|
1149
|
-
snap.layer.cornerRadius = endCornerRadius;
|
|
1150
|
-
}
|
|
1151
|
-
},
|
|
1152
|
-
completion: () => finalize(),
|
|
1153
|
-
});
|
|
1154
|
-
// Safety: ensure finalize runs even if the animation completion
|
|
1155
|
-
// is dropped (e.g. interrupted by another gesture).
|
|
1156
|
-
setTimeout(finalize, 800);
|
|
1157
|
-
}
|
|
1158
|
-
else {
|
|
1159
|
-
// No target transform; ensure sources are still restored.
|
|
1160
|
-
for (const v of state.instance.sharedElements?._allPresentingViews || []) {
|
|
1161
|
-
if (v?.ios)
|
|
1162
|
-
v.ios.alpha = v.opacity;
|
|
1163
|
-
}
|
|
1164
|
-
finalize();
|
|
1165
|
-
}
|
|
1166
|
-
return;
|
|
1167
|
-
}
|
|
1168
516
|
if (state?.instance && interactiveState?.added && interactiveState?.propertyAnimator) {
|
|
1169
517
|
interactiveState.propertyAnimator.reversed = false;
|
|
1170
518
|
const duration = isNumber(state.pageReturn?.duration) ? state.pageReturn?.duration / 1000 : CORE_ANIMATION_DEFAULTS.duration;
|
|
1171
519
|
interactiveState.propertyAnimator.continueAnimationWithTimingParametersDurationFactor(null, duration);
|
|
1172
520
|
setTimeout(() => {
|
|
1173
|
-
// Restore alpha on every source view we hid (including duplicates) so
|
|
1174
|
-
// the source page is visible if shown again.
|
|
1175
|
-
for (const sourceView of state.instance.sharedElements._allPresentingViews || []) {
|
|
1176
|
-
if (sourceView?.ios) {
|
|
1177
|
-
sourceView.ios.alpha = sourceView.opacity;
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
521
|
for (const presenting of state.instance.sharedElements.presenting) {
|
|
522
|
+
presenting.view.opacity = presenting.startOpacity;
|
|
1181
523
|
presenting.snapshot.removeFromSuperview();
|
|
1182
524
|
}
|
|
1183
|
-
for (const presented of state.instance.sharedElements.presented) {
|
|
1184
|
-
if (presented.imageSourceChangeListener) {
|
|
1185
|
-
presented.view.off('imageSourceChange', presented.imageSourceChangeListener);
|
|
1186
|
-
presented.imageSourceChangeListener = null;
|
|
1187
|
-
}
|
|
1188
|
-
}
|
|
1189
|
-
// Restore alpha on independent (orphan) source views and tear down
|
|
1190
|
-
// their snapshots. Without this, source-only orphans (any non-matched
|
|
1191
|
-
// sharedTransitionTag on the source page) stay at alpha 0 forever,
|
|
1192
|
-
// leaving the page visibly blank where they used to be. The non-morph
|
|
1193
|
-
// interactive path drives a propertyAnimator instead of running the
|
|
1194
|
-
// dismiss snapshot/animation rig, so the standard cleanup doesn't run.
|
|
1195
|
-
for (const ind of state.instance.sharedElements.independent) {
|
|
1196
|
-
if (ind.view?.ios) {
|
|
1197
|
-
ind.view.ios.alpha = ind.view.opacity;
|
|
1198
|
-
}
|
|
1199
|
-
ind.snapshot?.removeFromSuperview();
|
|
1200
|
-
}
|
|
1201
525
|
SharedTransition.finishState(state.instance.id);
|
|
1202
526
|
interactiveState.propertyAnimator = null;
|
|
1203
527
|
interactiveState.added = false;
|