0
votes

This is somewhat long-winded, but I think better to be clear. I can't seem to find the definitive way to do this, and the problem is subtle (I think).

I have an application whose GUI hierarchy looks something like:

  • Main List (has a custom TableCell and height)
    • Sub List (for a selected Main List item)
      • Sub List Info (on a selected Sub List item)
        • Further info
    • Settings (for a selected Main List item)
      • Settings Info (on a selected Settings item)
        • Further info

Once a main list item is selected, the user is able to swap between Sub List and Settings, each of which have their own GUI sub-hierarchy. The switch is made using a toolbar.

I've tried several approaches and nothing seems to work exactly right.

First try (inspired by other questions here)

  • UINavigationController
    • UITableViewControllerBase (loaded from nib)
      • TableViewController1 (loaded programmatically on didSelectRowAtIndexPath)
      • TableViewController2 (can be swapped from TableViewController1)

I pass a reference to the rootViewController through as the various objects are created, then use that to do the switch.

[rootViewController switchViews]

In rootViewController.switchViews():

[TableViewController2 alloc] init];
[TableViewController1 removeFromSuperview];
[self.view insertSubview:TableViewController2.view atIndex:0]
. . .

Basically, rootViewController manages the switch. This works for the switch, but when I navigate back through the hierarchy to the Main List, it doesn't initialize the table cells properly.

In cellForRowAtIndexPath:

static NSString *CellId = @"CellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIDentifier:CellId];
if (cell == nil)
{
  ... (this does not get called)
}

because the code in the brackets doesn't get called, the table view looks "generic", that is, the custom cell is not used. While this might be an unrelated error, the whole approach here didn't seem right to me. My understanding is that the active view controller should be handling the switch. So I tried something different . . .

Second try (inspired by Beginning iPhone Programming book):

  • UINavigationController
    • UITableViewControllerBase (loaded from nib)
      • UIViewControllerParent (loaded programmatically on didSelectRowAtIndexPath)
        • TableViewController1 (loaded programmatically on parent's loadView)
        • TableViewController2 (can be swapped from TableViewController1)

Here we're enclosing both the nested table views in a controller parent, and letting the parent do all the work.

In loadView() of parent:

TableViewController1 *tvc1 = [[UITableViewController alloc] init];
self.tableViewController1 = tvc1;
[self.view insertSubview:tableViewController1.view atIndex:0];
[tvc1 release];

The issue here is that tableViewController1, although inited, does not have a valid view at the time when it's being inserted. So the loadView function gets called again and again . . . I also tried putting this code in viewDidLoad, same problem.

I read the example code TheElements from the iOS developer site, and was originally going to go that route. The problem is that after the user switches views in my application, there's more screens that he can drill down into. I want all those screens to be part of the application's hierarchy (so that we can go back to the Main List when we want to). While TheElements illustrates a switch, it just switches views in a single UIViewController - I need to be able to drill down after the switch to new screens and have all those screens be a part of the UINavigationViewController's hierarchy, for navigation purposes.

I'm new to all this so it's possible I'm thinking of things in the wrong way. Hence the question. I'm sure this design is somewhat common, so even though there are undoubtedly many ways to do it, I'm looking for the best practice. The key thing here is that after the switch is made, the user should be able to navigate further down in the UINavigationViewController's hierarchy, and be able to navigate back up. That requires loading multiple UIViewControllers. I believe.

Many thanks for the time (and apologies for the long post).

1

1 Answers

1
votes

So, if I understand your question correctly, you want to switch between two view controllers at the top of a navigation controller's stack. Why don't you just let the navigation controller do the work and use setViewControllers:animated:, replacing just the last view controller in the existing array.

Example, with an additional flip animation (in SubListViewController):

- (void)switchToSettings:(id)sender
{
    [UIView transitionWithView:self.navigationController.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft animations:^ {
        SettingsViewController *settingsVC = [[[SettingsViewController alloc] initWithStyle:UITableViewStyleGrouped] autorelease];
        settingsVC.mainListItem = self.mainListItem;
        NSMutableArray *mutableViewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
        [mutableViewControllers removeLastObject];
        [mutableViewControllers addObject:settingsVC];
        [self.navigationController setViewControllers:mutableViewControllers animated:NO];
    } completion:^(BOOL finished) {}];
}