Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-28497] iOS: Hierarchy error occurs with SplitWindow and NavigationWindow as of 10.0.0

GitHub Issuen/a
TypeBug
PriorityCritical
StatusClosed
ResolutionFixed
Resolution Date2021-07-20T09:46:20.000+0000
Affected Version/sRelease 10.0.0
Fix Version/sRelease 10.0.1
ComponentsiOS
LabelsNavigationWindow, SplitWindow, ios, regression
ReporterBrian GarcĂ­a
AssigneeJoshua Quick
Created2021-06-24T08:09:59.000+0000
Updated2021-07-20T09:46:20.000+0000

Description

With SDK 10.0.0.GA and the following example, iOS throws the error below. In lower versions it works properly
<Alloy>
  <SplitWindow backgroundColor="white" showMasterInPortrait="true">

    <!-- First window is the masterView -->
    <NavigationWindow>
      <Window title="Master View">
        <ListView>
          <ListSection headerTitle="Some items">
            <ListItem title="Item 1" />
            <ListItem title="Item 2" />
            <ListItem title="Item 3" />
          </ListSection>
        </ListView>
      </Window>
    </NavigationWindow>

    <!-- Second window is the detailView -->
    <NavigationWindow>
      <Window title="Detail View">
        <Label>I am the detail view.</Label>
      </Window>
    </NavigationWindow>
  </SplitWindow>
</Alloy>
'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<UINavigationController: 0x7fda2d885000> should have parent view controller:<TiViewController: 0x7fda2ac61920> but requested parent is:<TiRootViewController: 0x7fda2c02a200>'

Attachments

FileDateSize
SplitWithNavChildWindows.png2021-06-25T03:47:10.000+0000886855

Comments

  1. Joshua Quick 2021-06-25

    I made a Classic version of your Alloy app in pure JS and it worked fine...
       const masterWindow = Ti.UI.createWindow({ title: "Master View" });
       masterWindow.add(Ti.UI.createListView({
       	sections: [
       		Ti.UI.createListSection({
       			items: [
       				{ properties: { title: "Row 1" } },
       				{ properties: { title: "Row 2" } },
       				{ properties: { title: "Row 3" } },
       			],
       		}),
       	],
       }));
       
       const detailWindow = Ti.UI.createWindow({ title: "Detail View" });
       detailWindow.add(Ti.UI.createLabel({ text: "Hello World!" }));
       
       const splitWindow = Ti.UI.iOS.createSplitWindow({
       	detailView: Ti.UI.createNavigationWindow({ window: detailWindow }),
       	masterView: Ti.UI.createNavigationWindow({ window: masterWindow }),
       	backgroundColor: "white",
       	showMasterInPortrait: true,
       });
       splitWindow.open();
       
    !SplitWithNavChildWindows.png|thumbnail! ---- However, if I wrap the SplitWindow within a NavigationWindow, it *crashes* as of Titanium 10.0.0. This is happening because there is a NavigationWindow within a NavigationWindow which will cause this exception. Your Alloy XML isn't set up to do this, but I'm *guessing* Alloy is wrongly setting it up this way when it does the JS code generation.
       const masterWindow = Ti.UI.createWindow({ title: "Master View" });
       masterWindow.add(Ti.UI.createListView({
       	sections: [
       		Ti.UI.createListSection({
       			items: [
       				{ properties: { title: "Row 1" } },
       				{ properties: { title: "Row 2" } },
       				{ properties: { title: "Row 3" } },
       			],
       		}),
       	],
       }));
       
       const detailWindow = Ti.UI.createWindow({ title: "Detail View" });
       detailWindow.add(Ti.UI.createLabel({ text: "Hello World!" }));
       
       const splitWindow = Ti.UI.iOS.createSplitWindow({
       	detailView: Ti.UI.createNavigationWindow({ window: detailWindow }),
       	masterView: Ti.UI.createNavigationWindow({ window: masterWindow }),
       	backgroundColor: "white",
       	showMasterInPortrait: true,
       });
       Ti.UI.createNavigationWindow({ window: splitWindow }).open();
       
       [INFO]  *** Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'child view controller:<UINavigationController: 0x7fd9c586a600> should have parent view controller:<TiViewController: 0x7fd9c60157a0> but requested parent is:<TiRootViewController: 0x7fd9c581c400>'
       [INFO]  *** First throw call stack:
       [INFO]  (
       [INFO]          0   CoreFoundation                      0x00007fff20422fba __exceptionPreprocess + 242
       [INFO]          1   libobjc.A.dylib                     0x00007fff20193ff5 objc_exception_throw + 48
       [INFO]          2   CoreFoundation                      0x00007fff20422e98 -[NSException initWithCoder:] + 0
       [INFO]          3   UIKitCore                           0x00007fff23f89a24 -[UIViewController _addChildViewController:performHierarchyCheck:notifyWillMove:] + 329
       [INFO]          4   ClassicAppTest                      0x000000010660f4e4 -[TiUINavigationWindowProxy windowDidOpen] + 100
       
  2. Joshua Quick 2021-06-25

    I've confirmed that ticket [TIMOB-27865] has caused this regression. It's caused by the new \[TiUINavigationWindowProxy windowDidOpen\] method added... https://github.com/appcelerator/titanium_mobile/pull/12233/files#diff-370f5f4b50cfdee279868eb38fb97f1954436094edbe253d4b3805e99725e65f *Possible Solution:* I think we need to change the [TiWindowProxy. windowHoldingController ](https://github.com/appcelerator/titanium_mobile/blob/master/iphone/TitaniumKit/TitaniumKit/Sources/API/TiWindowProxy.m#L506-L513) method to be walk up the view hierarchy to find the first parent controller like this...
       - (UIViewController *)windowHoldingController
       {
         for (UIResponder *responder = [[self view].superview nextResponder]; responder != nil; responder = [responder nextResponder]) {
           if ([responder isKindOfClass:[UIViewController class]]) {
             return (UIViewController *) responder;
           }
         }
         return [[TiApp app] controller];
       }
       
  3. Joshua Quick 2021-06-28

    PR (master): https://github.com/appcelerator/titanium_mobile/pull/12930
  4. Ewan Harris 2021-07-20

    Merged to master and 10_0_X

JSON Source