iPad SplitViewController Recursive Drill Down - Access DetailViewController

Discussion in 'iOS Programming' started by daveywc, Nov 4, 2011.

  1. daveywc, Nov 4, 2011
    Last edited: Nov 4, 2011

    daveywc macrumors newbie

    Joined:
    Nov 4, 2011
    #1
    I have taken a project based on the standard iPad SplitViewerController template and implemented recursive drill down navigation to any number of levels as follows:

    Firstly I created a new view controller (named NavItemController) which I push onto the controller stack in the didSelectRowAtIndexPath method of the RootViewController as follows:

    Code:
    NavItemController  *navItemController = [[NavItemController alloc] initWithNibName:@"NavItemController" bundle:[NSBundle mainBundle]];
        navItemController.title = catalogue.name;
        [[self navigationController] pushViewController:navItemController animated:YES];
    I then use this view controller for all navigation up and down my tree structure (so the RootViewController is now only used to show the initial root level of navigation i.e. items with no parent).

    That all works nicely.

    Now I am trying to update the label on the detail view (detailViewDescriptionLabel) when I select an item in the NavItemController. To do this I first added an outlet to my NavItemController:

    Code:
     @property (nonatomic, strong) IBOutlet DetailViewController *detailViewController;
    
    and configured it in InterfaceBuilder by adding a view controller from the library to the Objects list, changing its class to DetailViewController and hooking up the the outlet that I found under File's Owner.

    At this point if I step through my code I find that it correctly sets the detailItem in my detail view to the object selected in my NavController - and it seems to correctly set the detailDescriptionLabel.text value to a value from this detailItem. However this is not reflected in the UI (it does still work if I do this from the RootViewController).

    I'm guessing that I have not hooked something up correctly or missed a step somewhere - I am (obviously) pretty new to iOS - any pointers would be appreciated.
     
  2. dejo Moderator

    dejo

    Staff Member

    Joined:
    Sep 2, 2004
    Location:
    The Centennial State
    #2
    Perhaps if you provided the code where you set the detailItem and where you update the UI based on it, we can better tell what you're doing. It's hard to debug descriptions.

    Also, you don't really "pop onto" the nav stack. You either "push onto" or "pop off of". Using the right phrasing should make it less confusing to those you seek help from.
     
  3. daveywc thread starter macrumors newbie

    Joined:
    Nov 4, 2011
    #3
    More Info

    OK here is some more of my code as requested.

    First the didSelectRowAtIndexPath method from my RootViewController:

    Code:
    - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
        detailViewController.detailItem = selectedObject;
        
       // Setup NavItemController
        NavItem *navItem = ((NavItem *) selectedObject);
        Catalogue *catalogue = [navItem catalogue];
        DataLoader *dataLoader =[(DocsOnTapAppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
        [dataLoader drillDownIntoNavItems:catalogue];
        NavItemController  *navItemController = [[NavItemController alloc] initWithNibName:@"NavItemController" bundle:[NSBundle mainBundle]];
        //navItemController.detailViewController = self.detailViewController;
        navItemController.title = catalogue.name;
        [[self navigationController] pushViewController:navItemController animated:YES];
    }
    and the corresponding setDetailItem and configureView methods from my DetailViewController.

    Code:
    - (void)setDetailItem:(NSManagedObject *)managedObject
    {
    	if (_detailItem != managedObject) {
    		_detailItem = managedObject;
    		
            // Update the view.
            [self configureView];
    	}
        
        if (self.popoverController != nil) {
            [self.popoverController dismissPopoverAnimated:YES];
        }		
    }
    
    - (void)configureView
    {
        // Update the user interface for the detail item.
    
        // Normally should use accessor method, but using KVC here avoids adding a custom class to the template.
        NSString *text = [[self.detailItem valueForKey:@"name"] description];
        self.detailDescriptionLabel.text = text;
        NSLog(@"%@",text);
    }
    The above works i.e. it does update the text on the label in the DetailViewController.

    Then from my NavItemController class (the controller pushed from the RootViewController above) my didSelectRowAtIndexPath method:


    Code:
    - (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        // Set the detail item in the detail view controller.
        NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
        detailViewController.detailItem = selectedObject;
        
        // Setup NavItemController
        NavItem *navItem = ((NavItem *) selectedObject);
        Catalogue *catalogue = nil;
        Document *document = nil;
        
        if (navItem.catalogue != nil) // Load child catalogues and documents
        {
            catalogue = [navItem catalogue];
            DataLoader *dataLoader =[(DocsOnTapAppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
            [dataLoader drillDownIntoNavItems:catalogue];
            NavItemController  *navItemController = [[NavItemController alloc] initWithNibName:@"NavItemController" bundle:[NSBundle mainBundle]];
            //navItemController.detailViewController = self.detailViewController;
            navItemController.title = catalogue.name;
            
            UISplitViewController *svc = [(DocsOnTapAppDelegate *)[[UIApplication sharedApplication] delegate] splitViewController];
                   
            [[svc navigationController] pushViewController:navItemController animated:YES];
        } 
        else if (navItem.document != nil) // Load attachments
        {
            document = [navItem document];
            DataLoader *dataLoader =[(DocsOnTapAppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
            [dataLoader drillDownIntoNavItems:document];
            NavItemController  *navItemController = [[NavItemController alloc] initWithNibName:@"NavItemController" bundle:[NSBundle mainBundle]];
            //navItemController.detailViewController = self.detailViewController;
            navItemController.title = [document name];
            [[self navigationController] pushViewController:navItemController animated:YES];
            
            // Display document in detail viewer
        }
        
        else // attachment
        {
            // Display attachment in popover
        }
    }
    This does not update the text on my label in the DetailViewController. To clarify that statement it does appear to update the detailItem and the label text when I step through the code. This is confirmed by the NSLog entry that I have in the configureView method. However the label on the screen never updates to the new value.
     
  4. daveywc thread starter macrumors newbie

    Joined:
    Nov 4, 2011
    #4
    Resolution

    I ended up resolving my problem by creating my project from scratch using iOS 5 and the Master Detail template. I also selected to use the storyboarding feature.

    I was able to use the MasterViewController (same as RootViewController in earlier template) for my recursive navigation i.e. I did not need to create a separate view controller.

    The code in the MasterViewController didSelectRowAtIndexPath method ended up being:

    Code:
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {
        NSManagedObject *selectedObject = [[self fetchedResultsController] objectAtIndexPath:indexPath];
        self.detailViewController.detailItem = selectedObject; 
        
        NavItem *navItem = ((NavItem *) selectedObject);
        Catalogue *catalogue = nil;
        Document *document = nil;
        
        if (navItem.catalogue != nil) // Load child catalogues and documents
        {
            catalogue = [navItem catalogue];
            DataLoader *dataLoader =[(AppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
            [dataLoader drillDownIntoNavItems:catalogue];
            UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:[NSBundle mainBundle]];
            MasterViewController  *controller = [storyBoard instantiateViewControllerWithIdentifier:@"master"];
            controller.managedObjectContext = self.managedObjectContext;
            controller.title = catalogue.name;
            
            [[self navigationController] pushViewController:controller animated:YES];
        } 
        else if (navItem.document != nil) // Load attachments
        {
            document = [navItem document];
            DataLoader *dataLoader =[(AppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
            [dataLoader drillDownIntoNavItems:document];
            UIStoryboard *storyBoard = [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:[NSBundle mainBundle]];
            MasterViewController  *controller = [storyBoard instantiateViewControllerWithIdentifier:@"master"];
            controller.managedObjectContext = self.managedObjectContext;
            controller.title = document.name;
            [[self navigationController] pushViewController:controller animated:YES];
            
            // Display document in detail viewer
        }
        
        else // attachment
        {
            // Display attachment in popover
        }
    }
    
    Also important is the viewWillDisappear method in the MasterViewController:

    Code:
    - (void)viewWillDisappear:(BOOL)animated
    {
    	if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
            // back button was pressed. We know this is true because self is no longer
            // in the navigation stack. 
            DataLoader *dataLoader =[(AppDelegate *)[[UIApplication sharedApplication] delegate] dataLoader];
            [dataLoader drillUpIntoNavItems];
        }
        
        [super viewWillDisappear:animated];
    }
     

Share This Page