MKMapViewDelegate derived class and delegate assignment - iphone

This is probably more of an objective-c question over iOS but I've seen some example code similar to the following that I'd like to better understand.
#interface MyMapView : MKMapView <MKMapViewDelegate> {
// ivars specific to derived class
}
#property(nonatomic,assign) id<MKMapViewDelegate> delegate;
#end
#implementation MyMapView
- (id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// initialize the ivars specific to this class
// Q1: Why invoke super on this delegate that's also a property of this class?
super.delegate = self;
zoomLevel = self.visibleMapRect.size.width * self.visibleMapRect.size.height;
}
return self;
}
#pragma mark - MKMapViewDelegate methods
// Q2: Why intercept these callbacks, only to invoke the delegate?
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
if( [delegate respondsToSelector:#selector(mapView:regionWillChangeAnimated:)] )
{
[delegate mapView:mapView regionWillChangeAnimated:animated];
}
}
#end
My two questions are:
1. Why would one invoke the super.delegate and also only declare the 'delegate' as a property?
2. Why intercept all of the delegate calls only to forward them back to the delegate?
I appreciate any insights.

Apple's documentation explicitly states that you should avoid subclass MKMapView:
http://developer.apple.com/library/ios/#documentation/MapKit/Reference/MKMapView_Class/MKMapView/MKMapView.html#//apple_ref/doc/uid/TP40008205
Although you should not subclass the MKMapView class itself, you can
get information about the map view’s behavior by providing a delegate
object.
So i guess this delegate "forward" pattern is used to not break things.
I use a little different approach to subclass MKMapView. To minimize breakage i use two classes. One that subclass MKMapView and just override the init/dealloc method and assign/release the delegate property to a instance of the other class. The other class is a subclass of NSObject that implements the MKMapViewDelegate protocol and will be the one that does the real work.
MyMapView.h
#interface MyMapView : MKMapView
#end
MyMapView.m
// private map delegate class
#interface MapDelegate : NSObject <MKMapViewDelegate>
// instance is not alive longer then MKMapView so use assign to also solve
// problem with circular retain
#property(nonatomic, assign) MKMapView *mapView;
#end
#implementation MapDelegate
#synthesize mapView;
- (id)initWithMapView:(ReportsMapView *)aMapView {
self = [super init];
if (self == nil) {
return nil;
}
self.mapView = aMapView;
return self;
}
// MKMapViewDelegate methods and other stuff goes here
#end
#implementation MyMapView
- (id)init {
self = [super init];
if (self == nil) {
return nil;
}
// delegate is a assign property
self.delegate = [[MapDelegate alloc] initWithMapView:self];
return self;
}
- (void)dealloc {
((MapDelegate *)self.delegate).mapView = nil;
[self.delegate release];
self.delegate = nil;
[super dealloc];
}
#end
The mapView property for MapDelegate class is not strictly needed but is probably useful if want to do things to the map view that that is not a result of some MKMapViewDelegate method call, timers etc.

Why would one invoke the super.delegate and also only declare the 'delegate' as a property?
Ans. As you are making a custom mapview it is important to call the delegates too.We are invoking the super class delegate to send control from the custom Mapview.
Why intercept all of the delegate calls only to forward them back to the delegate?
Ans.At that line of code we are sending back the control to that delegate method declared in super class to do some useful thing.
Hope it will solve the query.

Related

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
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).
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.

event delegation

I have two uiviewcontroller: MainViewController and SecondaryViewControlle. In MainViewController I do:
[self.view addSubView:SecondaryViewControlle.view];
The SecondaryViewController is a button by pressing the function to be performed by a MainViewController. How to do?
You'd start by defining a protocol in your SecondViewControlle.h file, something like:
#protocol SecondViewControlleDelegate
- (void) doSomething
#end
You would also need to add a "delegate" ivar to your SecondViewControlle .h file. It would be the delegate line:
#interface SecondViewControlle : UIViewController
...
...
...
#property (nonatomic, assign) id delegate; // all you need to do is add this line inside your interface declarations
...
...
...
#end
Then, when you create / instantiate your SecondaryViewControlle from your MainViewController, make certain to add the MainViewController as the delegate like so:
SecondaryViewControlle.delegate = self;
[self.view addSubView:SecondaryViewControlle.view];
Now the "delegate" of your SecondaryViewControlle view controller points back to your MainViewController.
And when the button is pressed, you can simply do something like:
- (IBAction) buttonIsPressed: (id) sender
{
[delegate doSomething];
}
Now, I need to give you some advice here.
1 ) DO NOT use the class names as object names. Instead of having an object named "SecondViewControlle", name it something different (and start it with a lower case, which is Objective-C convention), something like "moreDetailVC".
2) I've told you how to do this with a delegate pattern, but this may not be the most appropriate way to do whatever it is that you're trying to do. After all, the MainViewController object (which should be renamed mainVC to differentiate the object from the class) is not on screen or visible so maybe there's a better place to put the functionality?
Option A
It's quicker, and easier, but lacks the maintainability, since there is no contract stating that SecondaryViewController needs to bother calling anything, and self.parentViewController could be any UIViewController.
Option B
The delegate pattern; this is my preference, it's obvious what's happening, what's required, and there's a nice solid contract that states, if you want to initialise me, give me a delegate.
Option C
If SecondaryViewController has to notify multiple objects, it would be quick to use the NSNotificationCenter, but as with Option A, there's no contract, should you need to notify many objects, you would need to remember to listen for notifications on those objects - since this is not the question, I won't go into detail, it's just here for the information
Option A
Within MainViewController.m, do something like so:
SecondaryViewController *viewcontroller = [[SecondaryViewController alloc] initWithNibName:#"SecondaryView" bundle:nil];
[self addChildViewController:viewcontroller];
//set viewcontroller.view frame
[self.view addSubview:viewcontroller.view];
[viewcontroller didMoveToParentViewController:self];
Inside MainViewController.h
-(void) performButtonClickAction;
Inside MainViewController.m:
-(void) performButtonClickAction {
//Do something constructive
}
and then inside the SecondaryViewController.m:
-(IBAction) buttonPressed:(id) sender {
[self.parentViewController performButtonClickAction];
}
Option B
Inside SecondaryViewController.h
#protocol SecondaryViewControllerDelegate <NSObject>
-(void) eventAFromViewController:(UIViewController *) viewController;
-(void) eventBFromViewController:(UIViewController *) viewController;
#end
#interface SecondaryViewController : UIViewController {
id<SecondaryViewControllerDelegate> delegate;
}
#property (assign, nonatomic) id<SecondaryViewControllerDelegate> delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil delegate:(id<SecondaryViewControllerDelegate>) theDelegate;
#end
Inside SecondaryViewController.m
#synthesize delegate = _delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil delegate:(id<SecondaryViewControllerDelegate>) theDelegate
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.delegate = theDelegate;
}
return self;
}
-(IBAction) buttonPressed:(id) sender {
if( self.delegate != nil ) {
[_delegate eventAFromViewController:self];
}
else {
//No delegate
}
}

Protocols and accessing another class's methods

So I'm fairly new to objective-c and am trying to wrap my head around protocols. I'll use an example to illustrate my question.
Suppose I have a "Calculate" class that performs various methods. I also have "Class1" and "Class2" that perform the same methods in "Calculate".
Now from my understanding I can use protocols to access methods from "Calculate" without the need for inheritance (Therefore saving the need to replicate the same code in Class1 and Class2).
My understanding is also that I have to implement the protocols in Class1 and Class2, therefore I'd have to type out those methods anyways. So whats the point of protocols?
I want to use the methods of "Calculate" without making it a superclass of Class1 and Class2. So I started exploring protocols, I've read the documentation but I still don't understand how this is being achieved. If some one can explain protocols in layman's it would be appreciated.
Inheritance would allow you to not have to duplicate code. Protocols (what other programming languages call interfaces) implement a Can-Do structure of OOP. Meaning when a class implements a protocol, that class is saying that it can do a certain set of methods. It is still up to that class to implement the method as they see fit.
Here is a developer reference from Apple:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/ObjectiveC/Chapters/ocProtocols.html
A protocol is a set of method declarations. It's main purpose is to allow flexible relationships between classes.
Let's say I want a variety of classes to send out logging messages but I don't want them responsible for knowing what happens to the messages after they're sent. I create a Logger protocol that is then implemented by a ConsoleWriter class and a DiskWriter class. The class wanting to send the message doesn't know or care which one it's talking to; it just talks to something it knows as id<Logger>.
I don't know with what types of languages you have experience. But an Object-C protocol is very much like an Interface in .NET. It's purpose is to define a contract (interface, footprint, etc.) so that the actual "Type" of the object is not necessary to be known, but just what it can do.
That being said you can define a protocol "MyProtocol.h" that has a few properties and methods. Then you can implement this protocol on a class. You will not need to add the members of the protocol in the header of the class, but simply need to write the concrete implementation in the implementation.
What this does is allow you to reference object by their defined interface and not their type. So you can use the id type instead of the actual class type.
Hope this helps.
Protocols are almost like a portable header file. They describe methods that can or should be implemented by any class that conforms to the protocol. This is different from inheritance where a subclass automatically implements the methods of its super class and those methods can optionally be overridden on a subclass by subclass basis.
I suspect that you have some OOP background so I won't go too much into subclassing other than to say a subclass is very often a specialized or more specific version of the super class. In other words: every subclass is a kind of its superclass, but every superclass is not necessarily a type of subclass.
Protocols in ObjC are often used in delegate patterns where ClassA needs to know that ClassB can perform some kind of action. Here's an example:
// ClassA.h
#import "ClassB.h"
#interface ClassA <ClassBProtocol>
// Some variables
#end
// ClassA.m
#implementation ClassA
- (id)init {
if ( (self = [super init]) ) {
ClassB *classB = [[ClassB alloc] init]; // Create an instance of ClassB
classB.delegate = self; // Set ourself as the delegate which means we want ClassB to tell us what to do
}
return self;
}
// Introduced by ClassBProtocol
- (void)doSomethingCoolWithString:(NSString *)string {
// Do something here, it's up to ClassA what to do
}
#end
// ClassB.h
#protocol ClassBProtocol <NSObject>
- (void)doSomethingCoolWithString:(NSString *)string;
#end
#interface ClassB
#property (nonatomic, weak) id <ClassBProtocol>delegate;
// Some variables
#end
//ClassB.m
#implementation ClassB
#synthesize delegate;
- (id)init {
if ( (self = [super init]) ) {
if (delegate && [delegate respondsToSelector:#selector(doSomethingCoolWithString:)]) {
[delegate doSomethingCoolWithString:#"A String"];
}
}
return self;
}
#end
Below Example for Simple Protocol & Property:
---> ViewController.h File
#import <UIKit/UIKit.h>
#import "MyVC.h"
#interface ViewController : UIViewController<MyVCProtocol>
{
IBOutlet UILabel *label;
IBOutlet UIButton *btnPush;
MyVC *vc;
}
-(IBAction)Buttonclicked;
#end
---> ViewController.m File
#import "ViewController.h"
#implementation ViewController
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(IBAction)Buttonclicked
{
vc = [[MyVC alloc]initWithNibName:#"MyVC" bundle:nil];
vc.delegate=self;
[self.navigationController pushViewController:vc animated:YES];
}
-(void)GetText:(NSString *)text
{
label.textAlignment=UITextAlignmentCenter;
label.text=text;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
#end
---> MyVC.h File
#import <UIKit/UIKit.h>
#protocol MyVCProtocol <NSObject>
-(void)GetText:(NSString *)text;
#end
#interface MyVC : UIViewController
{
IBOutlet UITextField *m_TextField;
IBOutlet UIButton *m_Button;
id <MyVCProtocol> delegate;
}
#property(nonatomic, retain)id <MyVCProtocol> delegate;
-(IBAction)ButtonClicked;
#end
---> MyVC.m File
#import "MyVC.h"
#implementation MyVC
#synthesize delegate;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
}
-(IBAction)ButtonClicked
{
[delegate GetText:m_TextField.text];
[self.navigationController popViewControllerAnimated:YES];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end

Delegate works in controller A but not in controller B

This is the delegate
#protocol DropControllerDelegate;
#interface DropController : NSObject
id<DropControllerDelegate> delegate;
}
#property (nonatomic, assign) id<DropControllerDelegate> delegate;
+ (DropController*) sharedController;
#protocol DropControllerDelegate <NSObject>
- (void)openUserButtons;
- (void)startUpload;
- (void)uploadDone;
- (void)uploadFailed;
- (void)startDownload;
- (void)downloadDone;
- (void)subFolderLoaded;
#end
This is the singleton code:
static DropController *sharedCont = nil;
#pragma mark Singleton stuff
+ (DropController *) sharedController {
#synchronized(self) {
if (!sharedCont)
sharedCont = [[DropController alloc] init];
return sharedCont;
}
return sharedCont;
}
The code to set the delegate is (in both myControllerA and myControllerB):
DropController* dropHelper = [DropController sharedController];
dropHelper.delegate = self;
I'm able to receive the calls in one controller but not in the other controller (the code is a copycat), this is driving me crazy!
As i said in my comment, since you are using a shared instance of that object, it will overwrite the previously set delegate to the new object. So i would suggest to hold a array in your sharedController. Add objects to that array which will want to become the delegates to the sharedController.
Then iterate through that array and call the method over each object in it. Its pretty simple.
Edit:
When you assign the delegate in your viewcontroller A with the statement dropHelper.delegate = self; object A becomes the delegate. Calling the method [delegate openUserButtons]; will trigger the method in viewcontroller A. But when you do dropHelper.delegate = self; in viewcontroller B, [delegate openUserButtons]; will call the method in object B. It will not call both the methods from a and b. Since there is only one delegate variable and you are using a shared singleton object.

How to implement target-action-mechanism for custom control?

I'm going to write my own custom control that is very different from UIButton. It's so much different that I decided to write it from scratch. So all I subclass is UIControl.
When my control is touched up inside, then I want to fire a message in means of target-action. The user of that class may instantiate it and then add some targets and actions for this event.
i.e. imagine I would call internally a method -fireTargetsForTouchUpEvent. How could I maintain this target-action-mechanism in my class? Do I have to add all targets and actions to my own array and then just call selectors (the actions) on the target objects in a for-loop? Or is there a more intelligent way to do it?
I imagine to provide some methods for adding targets and actions for some events like that touch up event (I raise that manually by calling a internal method when that happens). Any idea?
I just want to clarify what #Felixyz said because it wasn't clear to me at first.
If you are subclassing UIControl, even if you are going to have a custom event, you don't have to keep track of your own targets/actions. The functionality is already there, all you have to do is call the code below in your subclass to trigger the event:
[self sendActionsForControlEvents:UIControlEventValueChanged];
Then in the view or view controller that instantiates your custom UIControl, just do
[customControl addTarget:self action:#selector(whatever) forControlEvents:UIControlEventValueChanged];
For custom event, just define your own enum (for example, UIControlEventValueChanged is equal to 1 << 12). Just make sure it is within the permitted range defined by UIControlEventApplicationReserved
You have the right idea. Here is how I would do it:
#interface TargetActionPair : NSObject
{
id target;
SEL action;
}
#property (assign) id target;
#property (assign) SEL action;
+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)selector;
- (void)fire;
#end
#implementation TargetActionPair
#synthesize target;
#synthesize action;
+ (TargetActionPair *)pairWithTarget:(id)aTarget andAction:(SEL)anAction
{
TargetActionPair * newSelf = [[self alloc] init];
[newSelf setTarget:aTarget];
[newSelf setAction:anAction];
return [newSelf autorelease];
}
- (void)fire
{
[target performSelector:action];
}
#end
With that class in place, storing your target/action pairs is pretty straightforward:
MyCustomControl.h:
#import "TargetActionPair.h"
#interface MyCustomControl : UIControl
{
NSMutableArray * touchUpEventHandlers;
}
- (id)init;
- (void)dealloc;
- (void)addHandlerForTouchUp:(TargetActionPair *)handler;
#end
MyCustomControl.m:
#import "TargetActionPair.h"
#implementation MyCustomControl
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
touchUpEventHandlers = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[touchUpEventHandlers release];
}
- (void)addHandlerForTouchUp:(TargetActionPair *)handler
{
[touchUpEventHandlers addObject:handler];
}
- (void) fireTargetsForTouchUpEvent
{
[touchUpEventHandlers makeObjectsPerformSelector:#selector(fire)];
}
#end
After that, setting up the control would be done as follows:
[instanceOfMyControl addHandlerForTouchUp:
[TargetActionPair pairWithTarget:someController
andAction:#selector(touchUpEvent)];
Since you're planning to subclass UIControl, you can just use
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents;
Using this, any class can register itself as a target for any events it wants to on your custom controller.