Skip to content

Interactive transitions: ownership and limits ​

An interactive gesture is not just an animation with a progress value. The native host decides which controllers or pages participate, whether the gesture can start, when it commits, and when those pages can be disposed. The router must keep its history, guards and result promises consistent with that decision.

This rc.3 investigation does not add a uniform interactive-transition API. It documents the published baseline (NativeScript Core 9.1.3, NativeScript-Vue 3.1.2, iOS runtime 9.1.0 and Android runtime 9.1.1) and separates it from experimental upstream work.

Actual iOS 26 simulator: reversed and committed gestures, zoom, guards and source removal.

Capability matrix ​

SurfaceOwner and progressCommit/cancelRouter integration and limits
Programmatic push / backCore Frame or the selected native host owns the transition; transition selects its animationNo public seek/reverse handleGuards run before router navigation. Await navigation completion; do not interpret a diagnostics duration as gesture progress.
iOS Frame edge BackUIKit navigation controller owns the interactive transitionUIKit decides from the real gestureRouter reconciles committed native history. Active page leave guards hold the gesture before it starts. useBackGesture() is null on iOS.
iOS 18+ zoomFromUIKit preferredTransition, when exposed by the runtimeUIKit controls zoom and interactive dismissalThe router supplies a weak source view and a synchronous admission predicate. A missing/unloaded source returns nil. An explicit Frame default transition can override zoom.
Core SharedTransition interactive dismissCore's iOS shared-transition implementation owns its recognizer/animatorA native pop; no router-owned reversible sessionA leave guard cannot veto this dismissal. Do not use it for an unsaved-edit flow that depends on that guard. Shared elements on Android do not imply equivalent interactive dismissal.
Android 14+ predictive BackAndroidX callback provides progress; the router's default preview presents the previous native PageCancel resets presentation; release runs router Back and guardsuseBackGesture() observes progress. It does not acquire ownership of the history or a native Fragment transaction. Root/modal-root and guarded-page behavior differ.
Experimental Android Core previewProposed version-1 Frame beginBackPreview sessionPresentation-only update/cancel/finish/dispose; finish never popsRequires the pinned candidate below. Published Core falls back to the existing preview. Not an AndroidX FragmentManager predictive-pop implementation.
UIKit split / custom hostsEach host owns its controllers and lifecycleDepends on that hostFrame evidence does not establish parity for split columns, multiple windows or third-party hosts.

See zoom/shared elements, Android Back, guards and native hosts for the supported APIs.

Observation is not control ​

Android's progress callbacks can drive page-owned decorative UI. They do not authorize calling popBackStack, disposing a page or resolving a modal result. With backPreview: false, the app can animate its own content from useBackGesture(); it still delegates navigation to the router.

UIKit's transition coordinator exposes interaction/completion context. The investigation fixture samples it and attaches completion observers without replacing the navigation controller delegate or gesture recognizer. percentComplete is not a documented cross-platform, continuously sampled progress contract: Apple's description ties it to the transition into its noninteractive phase. A future observer needs an explicit availability model, not fabricated 0…1 values on every host.

The Android Frame implementation uses its own entry history and Fragment replacement; it does not establish a FragmentManager back stack. Adding addToBackStack solely to obtain AndroidX seeking would change replace, clear-history, modal and recreation contracts. That is upstream architecture work, not an animation-library adapter.

Guard and lifecycle ordering ​

  1. A page leave guard's active state must be known before native iOS dismissal starts. An asynchronous question cannot retroactively hold a gesture UIKit already owns. Use the guarded native Back button or router Back to ask the user.
  2. Android suppresses the preview while a page leave guard is active. Global/asynchronous guards can still reject the navigation after release; the preview must restore presentation.
  3. A cancelled gesture retains the entry, Page and pending result. A completed pop removes the entry and settles its result once. A cancellation must not be represented as a temporary pop followed by a second push.
  4. New navigation, host unloading, changed top/target, and source removal invalidate assumptions made at gesture start. Completion callbacks must verify the original entry/session before mutating anything.
  5. A native modal's dismissal and an inner Frame's pop are different operations. Popping the inner detail must not settle the modal result or alter the presenting stack.

SharedTransition's interactive-dismiss limitation remains explicit. Global guards are not a blanket promise to veto arbitrary native UIKit transitions.

Decision and possible adapter boundary ​

Keep the existing APIs for rc.3. Do not introduce router.transition.progress, seek() or a universal cancel() method from this evidence.

A future host capability proposal should distinguish:

  • Observation: optional progress, interaction-ended and native-completion notifications, each tagged with host/window, entry identity and a session ID. Unknown progress remains unavailable. Completion must distinguish commit, cancel and interruption.
  • Presentation control: an optional, versioned session owned by a host. It may update/cancel/finish presentation, but never mutate router history. Admission is synchronous; guards/history/results remain router-owned.
  • Cleanup: one terminal outcome; dispose listeners/temporary native views on host unload, entry replacement, cancellation or failed admission. A stale callback cannot pop a newer entry. Subscribe only while needed; avoid a permanent JS frame loop in the shipping router.
  • Fallback: ordinary native navigation when a host/runtime cannot provide the contract, with no promise of visual parity. Reduce Motion suppresses optional transforms while preserving the same navigation/results.

An optional Jolt adapter could animate app-owned decoration or implement an explicitly supported presentation session. It must not take over UIKit's delegate, treat an Android preview as a Fragment seek, or become a runtime dependency. Adapter implementation is deferred until host capabilities and disposal are demonstrated.

Evidence and upstream status ​

The opt-in iOS fixture and Android runner live in scripts/native/transitions. The validation report identifies versions, scenarios and gaps. These are native simulator/emulator checks, not physical-device performance results.

The Core proposal NativeScript #11558 was still open and unmerged when checked on 2026-10-24. Its pinned candidate is fadb58f034e403933028f86746280375ea9811ff; this work does not change the demo's published dependencies. Existing candidate evidence covers a separate API 34/35/36 matrix and a Frame recreation fix. It must not be described as shipped Core behavior.

Reduce Motion is read at navigation/gesture admission (motion: 'system', 'reduced' or 'full'). Native controls remain the accessible alternative to gestures. Automated input and accessibility labels do not establish VoiceOver/TalkBack speech, focus restoration with assistive technology, or acceptable motion on physical devices.

Primary references ​

Host capability follow-up #145 remains outside the rc.3 release gate.

Observation proposal v0 ​

The #145 proposal and prototype observe ordinary UIKit Back without replacing delegates or polling. Native cancellation/commit and unsubscribe/current-replacement controls have recorded evidence, while entry identity mapping, modal inner Back and several lifetime/host cases remain gaps. The decision is to keep it in research: there is no new public observer, continuous progress promise or Jolt adapter. See the native report.

Released under the MIT License.