← 输入框|github.com|刷新
Skip to content

fix(core): nested tab frames stay loaded and never outrank the selected tab - #11446

Merged
NathanWalker merged 1 commit into
mainfrom
fix/nested-tab-frame-stack
Oct 2, 2026
Merged

NathanWalker merged 1 commit into
mainfrom
fix/nested-tab-frame-stack

Conversation

@NathanWalker

Copy link
Copy Markdown
Contributor

PR Checklist

What is the current behavior?

A Frame hosted as TabViewItem.view (per-tab navigation stacks) can lose its navigation bookkeeping through two separate mechanisms, both reproduced deterministically in apps/toolbox (new nested-tab-frame pages) on the iPhone simulator and a Pixel 6a API 35 emulator:

  1. TabView.items re-assigned with an array holding the same items (what a reactive renderer does on every update). onItemsChanged removed and re-added every item, which unloaded and tore down the selected item's view. On iOS the re-added item's Frame was never loaded again, because TabViewItem.canBeLoaded is only set after the load cascade has already run, so frame.isLoaded stayed false while the tab was on screen and every navigate() sat in the queue until an app called frame.callLoaded(). On Android the frame's native view was recreated detached from its tab fragment. On both platforms the selected tab went blank.
  2. Android preloads the neighbouring tabs (androidOffscreenTabLimit defaults to 1), so their frames load and navigate while hidden, and navigate() pushed them to the top of the frame stack. Right after startup Frame.topmost() returned the off-screen Demos frame instead of the selected Home frame, and after selecting a tab it returned that tab's hidden neighbour. Frame.topmost().navigate() pushed a page into the invisible tab, and the hardware back button (Frame.goBack(Frame.topmost(window))) would have acted on it too.

What is the new behavior?

  • TabViewBase.onItemsChanged keeps items present in both arrays attached; only removed items are torn down and only new ones added (the Android override resets tab specs only for removed items). iOS additionally loads the selected item's view once its controller exists, so an item added to a loaded TabView at the selected index is not left unloaded.
  • ViewBase gains _isChildPresented(child); TabViewBase overrides it for the selected item. FrameBase._pushInFrameStack() only promotes a frame that every ancestor presents; a hidden frame is kept at the bottom of the stack (still found by Frame.getFrameById) until its tab is selected, when the existing _pushInFrameStackRecursive() call promotes it.

Verified on device after the change: the reassignment keeps isLoaded=true and pushes keep working on both platforms; on Android Frame.topmost() is the selected tab's frame at startup and after every selection, with the preloaded neighbour parked below (stack=[Third, root, Home, Demos] after selecting Demos).

Unit tests: tab-view-common.spec.ts (retained/removed/added items, item moved between TabViews, _isChildPresented) and new frame stack presentation cases in frame-common.spec.ts (hidden frames stay off the top, remain findable by id, preloaded tab frame navigating does not outrank the selected tab, selection promotes it).

Fixes #11444.

…ed tab

Re-assigning TabView.items with an array holding the same items removed and
re-added every item, which unloaded and tore down the selected item's view.
On iOS the re-added item's frame never loaded again (canBeLoaded is false
until its controller exists), so navigation queued forever; on Android the
frame's native view was recreated detached from its tab fragment. Items kept
across the change now stay attached.

Android preloads the neighbouring tabs, so their frames load and navigate
while hidden; navigate() pushed them above the selected tab's frame, making
Frame.topmost() (and the hardware back button) act on an invisible tab. A frame
hidden by an ancestor is now kept at the bottom of the frame stack until its
tab is selected.

Fixes #11444
@nx-cloud

nx-cloud Bot commented Sep 23, 2026 •

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 13e4800

Command Status Duration Result
nx test apps-automated -c=android ✅ Succeeded 3m 48s View ↗
nx run-many --target=test --configuration=ci --... ✅ Succeeded <1s View ↗

💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗


☁️ Nx Cloud last updated this comment at 2026-09-23 20:54:47 UTC

@pkg-pr-new

pkg-pr-new Bot commented Sep 23, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@nativescript/core@11446
npm i https://pkg.pr.new/@nativescript/vite@11446
npm i https://pkg.pr.new/@nativescript/webpack@11446

commit: 13e4800

NathanWalker added a commit to nativescript-community/octane that referenced this pull request Sep 23, 2026
The driver could not place a <tabviewitem> under a <tabview>, nor a view
under an item, so tabbed apps had to build their TabView imperatively. A tab
view's items now mirror its node's children: an item is listed once it holds
a view, because core rejects a view-less item and a child attaches after its
parent.

Items are assigned once per batch, after every child has attached. Core tears
down the items it keeps whenever the array changes (fixed in NativeScript
9.1.3, NativeScript/NativeScript#11446), so a mount must reach it as a single
assignment to keep the selected tab loaded on earlier releases.
NathanWalker added a commit to nativescript-community/octane that referenced this pull request Sep 23, 2026
The driver could not place a <tabviewitem> under a <tabview>, nor a view
under an item, so tabbed apps had to build their TabView imperatively. A tab
view's items now mirror its node's children: an item is listed once it holds
a view, because core rejects a view-less item and a child attaches after its
parent.

Items are assigned once per batch, after every child has attached. Core tears
down the items it keeps whenever the array changes (fixed in NativeScript
9.1.3, NativeScript/NativeScript#11446), so a mount must reach it as a single
assignment to keep the selected tab loaded on earlier releases.
@NathanWalker
NathanWalker merged commit 75cbfc5 into main Oct 2, 2026
10 checks passed
@NathanWalker
NathanWalker deleted the fix/nested-tab-frame-stack branch October 2, 2026 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nested Frame in TabViewItem: navigation bookkeeping stalls on both platforms (different mechanisms)

1 participant