Pass a delegate method multi-levels up a navigationController stack
I have a button in a toolbar that has a popover associated with it. Within the popover, I've set up a navigationcontroller. What I'm trying to achieve is for a view controller two or three levels down in the navigationcontroller stack to change the state of the button that originally called the popover. I've managed to do this, but it requires a couple of delegates and开发者_JAVA技巧 seems very clunky; my reason for posting here is to figure out if there is a more elegant and efficient solution.
So, to get started:
//ProtocolDeclaration.h
@protocol ADelegate <NSObject>
- (void)changeButtonState;
@end
@protocol BDelegate <NSObject>
- (void)passTheBuckUpTheNavChain;
@end
Then, for my MainController that holds the button:
// MainController.h
#import "A_TableController.h"
#import "ProtocolDeclaration.h"
@class A_TableController;
@interface MainController : UIViewController <ADelegate>
...
@end
// MainController.m
- (IBAction)buttonPressed:(id)sender {
A_Controller *ac = [[[A_Controller alloc] init] autorelease];
ac.ADelegate = self;
UINavigationController *nc = [[[UINavigationController alloc] initWithRootViewController:ac] autorelease];
UIPopoverController *pc = [[[UIPopoverController alloc] initWithContentViewController:nc] autorelease];
[pc presentPopoverFromBarButtonItem...]
}
// ADelegate Method in MainController.m
- (void)changeButtonState
{
self.button.style = ....
}
Now, for A_Controller, my rootViewController for my navController:
//A_Controller.h
#import "B_Controller.h"
#import "ProtocolDeclaration.h"
@class B_Controller;
@interface A_Controller : UITableViewController <BDelegate>
{
id<ADelegate> delegate;
...
}
@property (assign) id<ADelegate> delegate;
...
@end
//A_Controller.m
//In the method that pushes B_Controller onto the stack:
B_Controller *bc = [[[B_Controller alloc] init] autorelease];
bc.BDelegate = self;
[self.navigationController pushViewController:bc animated:YES];
//In the BDelegate Method in A_Controller:
- (void)passTheBuckUpTheNavChain
{
[ADelegate changeButtonState];
}
Lastly, in B_Controller:
//B_Controller.h
#import "ProtocolDeclaration.h"
@interface A_Controller : UITableViewController
{
id<BDelegate> delegate;
...
}
@property (assign) id<BDelegate> delegate;
...
@end
//B_Controller.m
//Where it's necessary to change the button state back up in MainController:
[BDelegate passTheBuckUpTheNavChain];
Now, this works, but it seems like a sort of Rube-Goldberg-ish way of doing it. I tried init'ing both A_Controller and B_Controller in MainController and setting B_Controller's delegate to MainController right there, and then using a NSArray of the two viewcontrollers to set the navcontroller stack, but it really messed up the way the viewcontrollers appeared in the navcontroller: I'd get a back button even on the rootviewcontroller of the navcontroller and you could just keep clicking Back and going round and round the navcontroller stack instead of stopping at the root. Any ideas on a better way to do this?
If you want to decouple the view controllers you could define a notification, and just post that one.
This way only the root view controller that receives the notification needs to know about the deep nested view controller.
Define the notification like this:
// In .h
extern NSString* const BlaControllerDidUpdateNotification;
// In .m
NSString* const BlaControllerDidUpdateNotification = @"BlaControllerDidUpdateNotification";
The deeply nested controller (BlaController
) need to post the message like this:
[[NSNotificationCenter defaultCenter]
postNotificationName:BlaControllerDidUpdateNotification
object:self];
And the root view controller need to act on it with something like this:
// In init or the like:
[[NSNotificationCenter defaultCender]
addObserver:self
selector:@selector(blaControllerDidUpdateNotification:)
name:BlaControllerDidUpdateNotification
object:nil];
// And then define this method:
-(void)blaControllerDidUpdateNotification:(NSNotification*)notification {
// Update UI or whatever here.
}
精彩评论