Problem
Tapping the tab's button to return to the root window of the tab orphans any windows between the current window and the root window.
Why This Is A Problem
Orphaned windows equate to leaked memory.
Expected Behavior
Tapping the tab's button should close every window that is open on that tab's stack, not just the current window.
Reproduction
Drop the following in an app.js. Touch "S1: Touch Me", then "S2: Touch Me". Notice in the log which windows were opened, and which were closed.
var tabGroup = Ti.UI.createTabGroup();
function openWindowOnStack(i) {
var win = Ti.UI.createWindow({
title: 'I Am ' + i
});
win.addEventListener('open', function() {
Ti.API.info('opened ' + i + '!');
if (i < 4) {
openWindowOnStack(i + 1);
}
});
win.addEventListener('close', function() {
Ti.API.info('closed ' + i + '!');
});
tab.open(win);
}
var outer = Ti.UI.createWindow({
title: 'I Am Parent',
rightNavButton: Ti.UI.createButton({ title: 'S1: Touch Me' })
});
var tab = Ti.UI.createTab({
window: outer, title: 'S2: Touch Me'
});
tabGroup.addTab(tab);
outer.rightNavButton.addEventListener('click', function() {
openWindowOnStack(0);
});
tabGroup.open();
Current Output
[INFO] opened 0!
[INFO] opened 1!
[INFO] opened 2!
[INFO] opened 3!
[INFO] opened 4!
[INFO] closed 4!
Expected Output
[INFO] opened 0!
[INFO] opened 1!
[INFO] opened 2!
[INFO] opened 3!
[INFO] opened 4!
[INFO] closed 3!
[INFO] closed 2!
[INFO] closed 1!
[INFO] closed 0!
Workaround
By following the three rules at the top of this app.js, I am able to clean up any orphaned windows./* To keep track of and clean up windows that have been opened on a specific tab, you must do the following: - Whenever you open a window, use the "openInTab(tab, win)" function. - On every window, listen for its "close" event and call the "trackClosedWindow(tab, win)" function. - Whenever you want to close all but the root window of a tab, call the "closeAllInTab(tab)" function. - Whenever a root window gains focus, it must call "closeAllInTab(tab)" on its tab. */ function openWindowOnStack(i) { var win = Ti.UI.createWindow({ title: 'Window ' + i }); win.addEventListener('open', function(evt) { Ti.API.info('Opened: ' + evt.source.title); if (i < 4) { openWindowOnStack(i + 1); } }); win.addEventListener('close', function(evt) { Ti.API.info('Closed: ' + evt.source.title); trackClosedWindow(tab, evt.source); }); openInTab(tab, win); } var outer = Ti.UI.createWindow({ title: 'Root Window', rightNavButton: Ti.UI.createButton({ title: 'S1: Touch Me' }) }); outer.addEventListener('focus', function() { // Make sure all children have closed... closeAllInTab(tab); }); outer.rightNavButton.addEventListener('click', function() { openWindowOnStack(0); }); function trackClosedWindow(tab, window) { var windows = tab.windows || []; // Iterate through backwards because windows further down the tree are more likely to be removed. for (var w = windows.length - 1; w >= 0; w--) { if (windows[w] == window) { windows[w] = null; windows.splice(w, 1); break; } } tab.windows = windows; } function closeAllInTab(tab) { var windows = tab.windows || []; // Iterate through forwards because we want to close the most recent window last for a nice animation to the root. for (var w = 0; w < windows.length; w++) { windows[w].close(); windows[w] = null; } tab.windows = windows = []; } function openInTab(tab, window) { var windows = tab.windows || []; windows.push(window); tab.open(window); tab.windows = windows; } var tabGroup = Ti.UI.createTabGroup(); var tab = Ti.UI.createTab({ window: outer, title: 'S2. Touch Me' }); tabGroup.addTab(tab); tabGroup.open();note to QE - this may not fit easily into current template.
Tested on Ti Studio 1.0.7.201112080131 Ti Mob SDK 1.8.0.1.v20111208124750 OSX Lion iPhone 4S OS 5.0.1 Expected behavior is shown