Hi,
I am trying to use a delegate method on a universal app. The delegate method works fine on the iPhone, but not on the iPad. I think it is because the iPad runs the view in a Popover. Here is the code I am using:
FlipsideViewController.h
FlipsideViewController.m
The addChild method gets called to add a new Child and opens the AddViewController. On the iPad, the content view is switched to the AddViewController.
AddViewController.h
AddViewController.m
In the end, the savechild method in AddViewController.m is called, but it never calls the delegate method.
Where is the problem?
Thanks
I am trying to use a delegate method on a universal app. The delegate method works fine on the iPhone, but not on the iPad. I think it is because the iPad runs the view in a Popover. Here is the code I am using:
FlipsideViewController.h
Code:
@class FlipsideViewController;
@protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
@end
@interface FlipsideViewController : UITableViewController <NSFetchedResultsControllerDelegate, AddViewControllerDelegate, UIPopoverControllerDelegate,MFMailComposeViewControllerDelegate> {
NSFetchedResultsController *fetchedResultsController;
NSManagedObjectContext *managedObjectContext;
NSManagedObjectContext *addingManagedObjectContext;
}
@property (weak, nonatomic) IBOutlet id <FlipsideViewControllerDelegate> delegate;
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;
@property (nonatomic, strong) NSManagedObjectContext *addingManagedObjectContext;
- (IBAction)addChild;
- (void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath;
- (void)done:(id)sender;
- (void)helpButton:(id)sender;
- (void)aboutButton:(id)sender;
@end
FlipsideViewController.m
Code:
- (IBAction)addChild {
AddViewController *addViewController = [[AddViewController alloc] initWithStyle:UITableViewStyleGrouped];
addViewController.delegate = self;
// Create a new managed object context for the new book -- set its persistent store coordinator to the same as that from the fetched results controller's context.
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addingManagedObjectContext = addingContext;
[addingManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
addViewController.child = (Child *)[NSEntityDescription insertNewObjectForEntityForName:@"Child" inManagedObjectContext:addingContext];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
[self.navigationController pushViewController:addViewController animated:YES];
}
else {
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:addViewController];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(done:)];
navController.navigationItem.leftBarButtonItem = cancelButton;
addViewController.contentSizeForViewInPopover = CGSizeMake(320, 480);
MainViewController *sharedData = [MainViewController sharedMainViewController];
[sharedData.flipsidePopoverController setContentViewController:navController animated:YES];
}
}
/**
Add controller's delegate method; informs the delegate that the add operation has completed, and indicates whether the user saved the new book.
*/
- (void)addViewController:(AddViewController *)controller didFinishWithSave:(BOOL)save {
NSLog(@"Got IT");
if (save) {
/*
The new book is associated with the add controller's managed object context.
This is good because it means that any edits that are made don't affect the application's main managed object context -- it's a way of keeping disjoint edits in a separate scratchpad -- but it does make it more difficult to get the new book registered with the fetched results controller.
First, you have to save the new book. This means it will be added to the persistent store. Then you can retrieve a corresponding managed object into the application delegate's context. Normally you might do this using a fetch or using objectWithID: -- for example
NSManagedObjectID *newBookID = [controller.book objectID];
NSManagedObject *newBook = [applicationContext objectWithID:newBookID];
These techniques, though, won't update the fetch results controller, which only observes change notifications in its context.
You don't want to tell the fetch result controller to perform its fetch again because this is an expensive operation.
You can, though, update the main context using mergeChangesFromContextDidSaveNotification: which will emit change notifications that the fetch results controller will observe.
To do this:
1 Register as an observer of the add controller's change notifications
2 Perform the save
3 In the notification method (addControllerContextDidSave:), merge the changes
4 Unregister as an observer
*/
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
NSError *error;
if (![addingManagedObjectContext save:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
//exit(-1); // Fail
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addingManagedObjectContext];
}
// Release the adding managed object context.
self.addingManagedObjectContext = nil;
// Dismiss the view to return to the main list
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
[self.navigationController popViewControllerAnimated:YES];
}
else {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:@"FlipsideViewController" bundle:nil];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:controller];
MainViewController *sharedData = [MainViewController sharedMainViewController];
[sharedData.flipsidePopoverController setContentViewController:navController animated:YES];
}
}
The addChild method gets called to add a new Child and opens the AddViewController. On the iPad, the content view is switched to the AddViewController.
AddViewController.h
Code:
//
// AddViewController.h
// iBehave
//
// Created by Ahan Malhotra on 10/8/11.
// Copyright (c) 2011 Random Widgets. All rights reserved.
//
#import "DetailViewController.h"
@protocol AddViewControllerDelegate;
@interface AddViewController : DetailViewController
@property (nonatomic, weak) id <AddViewControllerDelegate> delegate;
- (IBAction)cancel:(id)sender;
- (IBAction)savechild:(id)sender;
@end
@protocol AddViewControllerDelegate
- (void)addViewController:(AddViewController *)controller didFinishWithSave:(BOOL)save;
@end
AddViewController.m
Code:
//
// AddViewController.m
// iBehave
//
// Created by Ahan Malhotra on 10/8/11.
// Copyright (c) 2011 Random Widgets. All rights reserved.
//
#import "AddViewController.h"
#import "Child.h"
#import "FlipsideViewController.h"
@implementation AddViewController
@synthesize delegate;
#pragma mark View lifecycle
- (void)viewDidLoad {
[super viewDidLoad];
// Override the DetailViewController viewDidLoad with different navigation bar items and title.
self.title = @"New Child";
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
target:self action:@selector(cancel:)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave
target:self action:@selector(savechild:)];
// Set up the undo manager and set editing state to YES.
[self setUpUndoManager];
self.editing = YES;
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any properties that are loaded in viewDidLoad or can be recreated lazily.
[self cleanUpUndoManager];
}
#pragma mark Save and cancel operations
- (IBAction)cancel:(id)sender {
//FlipsideViewController *flipsideViewController = [FlipsideViewController alloc];
//[flipsideViewController.self addViewController:self didFinishWithSave:NO];
}
- (IBAction)savechild:(id)sender {
NSLog(@"Save");
if (child.name == nil) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Please Enter A Value In The Name Field." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alert show];
return;
}
[self.delegate addViewController:sender didFinishWithSave:YES];
}
@end
In the end, the savechild method in AddViewController.m is called, but it never calls the delegate method.
Where is the problem?
Thanks