1

Well, might not be clear with the title. I have pulled this right out of the MultipleDetailView sample code from Apple. Every time the user selects a row from the table in the pop over, detailViewController is allocated the FirstDetailViewController and SecondDetailViewController again. Instead of allocating and initializing the view controller over and over, I want to assign the existing and already allocated and initialized view controller if existing to the detailViewController on the selection of the row. I have modified the Split View Template instead of the sample code to achieve what I need. Code from the program:

This is the AppDelegate.h file:

@interface iPadHelloWorldAppDelegate : NSObject <UIApplicationDelegate> {

    UIWindow *window;

    UISplitViewController *splitViewController;

    MasterViewController *masterViewController;
    DetailViewController *detailViewController;
    SecondDetailViewController *secondDetailViewController;
}

This is the AppDelegate.m file:

 masterViewController = [[MasterViewController alloc] init];
 UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:masterViewController];
 detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:nil];
 secondDetailViewController = [[SecondDetailViewController alloc] initWithNibName:@"SecondDetailView" bundle:nil];
 splitViewController = [[UISplitViewController alloc] init];
 splitViewController.viewControllers = [NSArray arrayWithObjects:navigationController, detailViewController, nil];
    splitViewController.delegate = detailViewController;
    // Add the split view controller's view to the window and display.
    [window addSubview:splitViewController.view];
    [window makeKeyAndVisible];

This is the MasterViewController.m:

- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    NSUInteger row = indexPath.row;
    [self.appDelegate.splitViewController viewWillDisappear:YES];
    self.tempArrays = [NSMutableArray arrayWithArray:self.appDelegate.splitViewController.viewControllers];
    [self.tempArrays removeLastObject];
    if (row == 0) {
        [self.tempArrays addObject:self.appDelegate.detailViewController];
        self.appDelegate.splitViewController.delegate = self.appDelegate.detailViewController;
    }
    if (row == 1) {
        [self.tempArrays addObject:self.appDelegate.secondDetailViewController];
        self.appDelegate.splitViewController.delegate = self.appDelegate.secondDetailViewController;
    }
    self.appDelegate.splitViewController.viewControllers = self.tempArrays;
    [self.appDelegate.splitViewController viewWillAppear:YES];
}

This is the DetailViewController.m:

#pragma mark -
#pragma mark Split view support

- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {

    barButtonItem.title = @"Master List";
    [navigationBar.topItem setLeftBarButtonItem:barButtonItem animated:NO];
    self.popoverController = pc;
}

// Called when the view is shown again in the split view, invalidating the button and popover controller.
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {

    [navigationBar.topItem setLeftBarButtonItem:nil animated:NO];
    self.popoverController = nil;
}

I am able to lazy load the view controllers, but when I tap the bar button for the popover and jump to the second view controller, the second view controller does not show the pop over. When I jump back to the first detail view controller, the popover is displayed.

Basically, here is a similar question. But the link to the drop box there doesn't work.

Community
  • 1
  • 1
Viraj
  • 1,880
  • 22
  • 31

3 Answers3

3

Since you have asked me to have shot - here it goes. The SplitViewController is rather buggy, from our opinion here. We have encountered many problems if you don't stick to exactly the way Apple does it in their sample code.

First of all, I would suggest you take the sample code again and start from scratch, since it seems you have modified a lot.

As for your problem: In your delegate and the MainWindow.xib you set up your SplitViewController. The most important thing is to not set up the viewControllers array the way you do it.

I encountered the problem that if I overwrite the RootViewController, it messes up the SplitViewController and produces bugs like the one you encounter.

Try setting up your RootViewController (the TableViewController) only once and never overwrite it in the viewControllers property. This seems to be OK for the DetailViewController, though.

Secondly, you code should be placed elsewhere, not in the RootViewController. This should be for the tableView datasource and content only.

Try this and feedback here, I'll follow asap.

Best of luck.

EDIT: code addition - do this in your RootViewController:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Navigation logic may go here. Create and push another view controller.

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {


DetailViewController *dvC = [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];

// take the original view controller from the splitviewcontroller as root
// appDelegateiPad defined like this in my appdelegate:
// #define appDelegateiPad ((AppDelegate_iPad *)[[UIApplication sharedApplication] delegate]) 
NSArray *viewControllers = [[NSArray alloc] initWithObjects:[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:0], dvC, nil];
        appDelegateiPad.splitViewController.viewControllers = viewControllers;
//careful with this, set it to whatever your delegate is in your case           
appDelegateiPad.splitViewController.delegate = dvC;
        [viewControllers release];  


//this is my version
//i have the popoverController property in my detailviewcontroller. this is where my splitviewcontroller delegate methods are. you need to set the popovercontroller property in the class where your splitviewcontroller delegate methos are
    dvC.popoverController = [[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:1] popoverController];     

    } 
}
Thromordyn
  • 1,581
  • 4
  • 17
  • 45
Icky
  • 1,055
  • 1
  • 12
  • 30
  • @Icky: Hey, thanks for the answer and yes, the splitviewcontroller is really buggy. I followed what you told. I set up the splitView in my delegate and MainWindow.xib. But how do I set up the view controllers array ? I am going nuts trying to solve this :) Anyways thanks for your help. – Viraj Mar 15 '11 at 08:23
  • @Icky: Btw, I just want to simplify my question here again. I want to load a new view controller when a row is selected in the root view controller and when the same row is selected again, I should not allocate for the view controller again, as it is already been allocated once. The sample code from Apple doesn't allow that. – Viraj Mar 15 '11 at 08:27
  • it can be ok to allocate a new view controller, it depends on what you need. Check my code above - it works for me... – Icky Mar 15 '11 at 08:44
  • @Icky: I am sorry, I may not have been clear. I do not want to allocate again. Basically lazy load the view controller if it has been already allocated before. – Viraj Mar 15 '11 at 09:02
  • hm, where is this controller. if it has been loaded, it must be stored somewhere. but the main problem ist not the detailview. the error message arrives from you rootviewcontroller. and this is the point: do not replace it.... fetch it from your splitviewcontroller, the way i do it above. – Icky Mar 15 '11 at 09:25
  • @Icky: Right got that. I will try it and reply asap. Thanks for your help. – Viraj Mar 15 '11 at 10:06
  • @Icky: I tried what you told. But now, when I move from one VC to another, the pop over is not displayed in the second VC. If I jump back to the first one, the pop over reappears. – Viraj Mar 16 '11 at 08:07
  • I have modified the question. – Viraj Mar 16 '11 at 09:57
  • @Icky: I have updated the question with the new code. Is it right ? – Viraj Mar 16 '11 at 10:36
  • 1
    you are not setting the popovercontroller. your new view controller should have a popoverController property, which needs to be set to the current popoverController, once you replace it. Something like: dvC.popoverController = [[[appDelegateiPad.splitViewController viewControllers]objectAtIndex:1] popoverController]; – Icky Mar 16 '11 at 11:22
  • @Icky: Can you edit your answer to put the code ? So I will get a better idea. – Viraj Mar 16 '11 at 11:40
  • @Icky: Hello Icky. No, I still have not been able to fix it. Back to square one on it. – Viraj Mar 17 '11 at 10:22
  • well, maybe setup your code again, i think u nearly had it. i think u needed to take care of the popovercontroller. once you create the detailviewcontroller, you create a popovercontroller. where do you do that? in IB? if so, try doing it programatically. – Icky Mar 17 '11 at 11:59
  • I am doing it programmatically, but I am sure I am messing it up somewhere. I have to set up my code again for it. – Viraj Mar 17 '11 at 12:13
0

Try allocating the viewControllers in the viewDidLoad in this object. Give both viewcontrollers a title and put them in an array. These titles you can use for both cells textlabels if needed. In the didSelectRowAtIndexPath you can get the correct viewcontroller for the selected row

UIViewController <SubstitutableDetailViewController> *detailViewController = [theArray objectAtIndex:indexPath.row];

Edit:

@interface SomeClass : NSObject {
  NSArray *controllerArray;
}
@end
@implementation SomeClass

- (void) viewDidLoad {
  controllerArray = [[NSArray alloc] initWithObject://yourControllers//,nil];
  [super viewDidLoad]
}

- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section {

    // Two sections, one for each detail view controller.
    return 2;
}

- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

  //Create Cell

  UIViewCOntroller *controller = [controllerArray objectAtIndex:indexPath.row];
  cell.textLabel.text = controller.title;

  return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  [detailViewController autorelease]; 
  detailViewController = [[controllerArray objectAtIndex:indexPath.row] retain];
}

@end
Mats Stijlaart
  • 5,058
  • 7
  • 42
  • 58
  • This is a solution (Lazy loading). But every time you select a row, an if statement is executed. Less sufficient. See my edit for the solution i would choose. – Mats Stijlaart Feb 16 '11 at 09:48
  • Hey i tried this out... I got this error - Popovers cannot be presented from a view which does not have a window. – Viraj Feb 16 '11 at 10:04
  • Is this code in your UISPlitViewController? Where is the error (Code)? – Mats Stijlaart Feb 16 '11 at 10:21
  • I guess it would be easier if you can have a look at the code of the sample program MultipleDetailViews. Its in the RootViewController.m. I am using the exact same code from the example and changed it as you suggested. It is in the UISplitVIewController. – Viraj Feb 16 '11 at 10:59
  • Hey Mats, I still have no clue on how to solve this. I have updated the question with some things that I modified in the program. – Viraj Mar 01 '11 at 11:24
0

It isn't necessary to create a new instance of the detail view controller or update the viewControllers in the split view controller.

Try this.

    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
    {

        UIApplication *application = [UIApplication sharedApplication];
        AppDelegate *appDelegate = (AppDelegate*) [application delegate];
        appDelegate.detailViewController.label.text = @"Detail view controller updated";

    }

PS: Please, drag a label to the detail for testing.