katch
katch

Reputation: 930

Delegates in iOS

I am a newbie to iOS world, so please ignore the obvious.

I am pushing a viewController(HelpViewController) on top of another viewController(MainViewController). When a particular action happens in the HelpViewController, I would like to update a variable inside the MainViewController. I understand for this I need to use delegate. Here is my delegate header...

@protocol ViewControllerDelegate <NSObject>
@required
- (void) switchToggled:(BOOL)status;
@end
// Protocol Definition ends here
@interface ViewDelegate : NSObject
{
    // Delegate to respond back
    id <ViewControllerDelegate> _delegate;
}
@property (nonatomic,strong) id delegate;

-(void)sendMessage:(BOOL)status; // Instance method

@end

and implementation...

@implementation ViewDelegate

@synthesize delegate;

-(id)init {

    self = [super init];

    return self;

}

-(void)sendMessage:(BOOL)status
{
    [delegate switchToggled:status];
}

- (void)dealloc
{
    [super dealloc];
}
@end

So Now If I want to implement Protocol ViewControllerDelegate I need to specify in MainViewController, which I do as follows --

MainViewController <ViewControllerDelegate>

and

#pragma mark - ViewControllerDelegate delegate
-(void)switchToggled:(BOOL)status{
    NSLog(@"Switch Toggled(%d) Message passed to MainViewController",status);
}

My question is how do I specify Object, which delegate property needs to point to, so that it can come back to MainViewController's "switchToggled".

One way I do is by having property inside HelpViewController as follows -

MainViewController.m
HelpViewController *helpVC = [[HelpViewController alloc] init];
helpVC.mainView = self;
[self.navigationController pushViewController:helpVC animated:YES];
[helpVC release];

HelpViewController.h
    @property (nonatomic) MainViewController *mainView;
HelpViewController.m
@synthesize mainView;
ViewDelegate *myDelegate = [[ViewDelegate alloc] init];
// assign delegate
myDelegate.delegate = mainView;
[myDelegate sendMessage];
[myDelegate release];

Is this correct way to implement or there is better way to achieve this or am I totally wrong.

Thanks

Upvotes: 0

Views: 2039

Answers (2)

meaning-matters
meaning-matters

Reputation: 22946

You should do:

// HelpViewController.h
@protocol HelpDelegate

- (void)switchToggled:(BOOL)status;

@end


// HelpViewController.m
@interface HelpViewController : UIViewController

@property (nonatomic, assign) id<HelpDelegate> delegate;

- (id)initWithDelegate:(id<HelpDelegate>)delegate

@end

@implementation HelpViewController
- (id)initWithDelegate:(id<HelpDelegate>)delegate
{
    if (self = [super init])
    {
        self.delegate = delegate;
    }
}

- (void)sendMessage:(BOOL)status
{
    [self.delegate switchToggled:status];
}


// MainViewController.h 
#import "HelpViewController.h"

@interface MainViewController.h : UIViewController <HelpDelegate>


// MainViewController.m

- (void)someMethod
{
    HelpViewController* viewController;
    viewController = [HelpViewController alloc] initWithDelegate:self];
    ...
}

#pragma mark - Help Delegate

- (void)switchToggled:(BOOL)status
{
    ...
}
  • Give the delegate a name that makes clear to which class it belongs.
  • You don't need the extra class/files for ViewDelegate/ViewControllerDelegate. Just define the delegate in header of class it belongs to: HelpViewController.n in this case.
  • Similar: Implement the delegate method switchToggled: in the real class MainViewController, and not in the extra/unnecessary class ViewDelegate.
  • The purpose of delegates is to avoid class dependencies. By including MainViewController in HelpViewController you create such a dependency. This is not necessary as I show, and is wrong design.
  • You were also creating a circular dependency, because MainViewController already needed HelpViewController in order to show it, and now they need each other the other way around for sending the event.
  • Alternatively you can make HelpViewController's delegate public, have an init without argument, and expect users to set it with helpViewController.delegate = self; or something. But this would only make sense when the delegate being set is optional (which don't seems the case here, so adding it to the init method is appropriate).

Upvotes: 1

Francesco
Francesco

Reputation: 1848

I tell you what I would have done:

1) the protocol definition is ok, but do NOT create the class ViewDelegate, so:

//ViewControllerDelegate.h
@protocol ViewControllerDelegate <NSObject>
@required
- (void) switchToggled:(BOOL)status;
@end

2) Your implementation of the delegate method in MainViewController is ok.

3) Now... the important point:

    //interface
@interface HelpViewController : UIViewController //or whatever superclass..
{
    id <ViewControllerDelegate> _delegate;
}
@property (nonatomic,strong) id<ViewControllerDelegate> delegate;

@end
//implementation
@implementation HelpViewController

- (void)someMethodWhichCallsTheDelegate
{
    //do something
    ...
    // call delegate
    //if switchToggled: were optional then add the following
    //if ([self.delegate respondToSelector:@selector(switchToggled:)]) {
    [self.delegate switchToggled:status];
}
@end

4) Now you have to assign the delegate:

//MainViewController.m
HelpViewController *helpVC = [[HelpViewController alloc] init];
helpVC.delegate = self;
[self.navigationController pushViewController:helpVC animated:YES];
[helpVC release];

And that's it!

BTW: if this delegate is related only to HelpViewControllerthen add the protocol definition where you define the interface of the class, it is not necessary to create a separate header file. If instead the protocol is "global", then it can have some sense to declare it separately.

Upvotes: 0

Related Questions