Where do I put delegate methods and how do I invoke them? - iphone

As a new iOS developer, I have finally stumbled across delegates. I'm trying to follow a tutorial: http://gabriel-tips.blogspot.com/2011/05/input-accessory-view-how-to-add-extra.html, But I'm having difficulty understanding where I am supposed to put the actual delegate methods.
Secondly, would anyone mind providing a dumbed down explanation of how a delegate method is invoked?
Thanks!

A delegate is simply a class that agrees to do work for another class. The delegate methods are invoked by the delegating class. The delegate must therefore, provide an implementation of the appropriate method. Let's make a simple view controller with a table view.
// MyViewController.h
#interface MyViewController : UIViewController <UITableViewDelegate>
#property (nonatomic, retain) UITableView *myTableView;
#end
Here in the MyViewController.h file I have declared my view controller to be a delegate of type UITableViewDelegate (it really means it implements the UITableViewDelegate protocol. More on this later). I have thus agreed to respond to requests to my view controller. The requests will come from the table view called myTableView. However, simply stating that I adhere to UITableViewDelegate does not make my view controller a delegate of anything. I must specify that directly:
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)loadView
{
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
myTableView.delegate = self;
self.view = myTableView;
}
#end
Here I specifically set MyViewController to be the delegate of myTableView. Now whenever the table view wants to ask its delegate to do something, it will send that message to my view controller. Thus, MyViewController MUST provide implementations of the appropriate delegate methods:
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)loadView
{
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
myTableView.delegate = self;
self.view = myTableView;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Selected section:%i row:%i", indexPath.section, indexPath.row);
}
#end
Here I have provided an implementation for the delegate method tableView:didSelectRowAtIndexPath: which will be called by myTableView when it is appropriate (the user selects a row).
Here you can find all the delegate methods defined in UITableViewDelegate. Some are required and others are optional:
http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UITableViewDelegate_Protocol/Reference/Reference.html
In order to be a delegate of a class you should know which methods you are required to provide implementations for.
If you wanted to create your own delegate definitions, you would create a new protocol. You do not retain your delegates (see the property declaration), as this creates a retain cycle:
// MyViewController.h
#class MyViewController;
#protocol MyViewControllerDelegate
- (void)viewController:(MyViewController *)viewController didChangeSelection:(NSIndexPath *)newIndexPath;
#end
#interface MyViewController : UIViewController <UITableViewDelegate>
#property (nonatomic, retain) UITableView *myTableView;
#property (nonatomic, assign) id<MyViewControllerDelegate> delegate;
#end
Here we have created a new protocol. Any class that wants to be respond to the viewController:didChangeSelection: message could now do so. Just like with the table view above it would set the delegate to itself and then implement the method. Now that you have a delegate, you can invoke the method at an appropriate time.
// MyViewController.m
#import "MyViewController.h"
#implementation MyViewController
- (void)loadView
{
myTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
myTableView.delegate = self;
self.view = myTableView;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Selected section:%i row:%i", indexPath.section, indexPath.row);
indexPath = [NSIndexPath indexPathForRow:1 inSection:0];
[self.delegate viewController:self didChangeSelection:indexPath];
}
#end
Now the delegate can receive the message and do what it wants knowing that my view controller changed the selection.

A delegate pattern is a convenient way to allow for the communication between independent controller that allows for loose coupling. So let's say you have a pattern like this:
A
/ \
B C
Where A instantiates B and C. Communicating between A to B and A to C are easy but how would you communicate between B and C? B to A? C to A? There are a couple different ways you could do so such as Key-Value Observing or Block Callbacks. Delegation, though, are still most frequently used although Blocks are coming on strong.
In this example, object A instantiates object B to create an object and fill it with info. How would object B pass the new object back to A as you want to keep things loose? Well, with these 9 easy steps, you can do it too! It may not make sense but we'll start with ClassB…
// ClassB.h
#protocol ClassBDelegate; //(1)
#interface ClassB : NSObject
#property (nonatomic, weak) id<ClassBDelegate>bDelegate; //(2)
-(void)makeNewObjectAndSendBack;
#end
#protocol ClassBDelegate : NSObject //(3)
-(void) classB:(Class B *)theClassB finishedWithObject:(id)finishedObject; //(4)
#end
ClassB.m
#implementation
#synthesize bDelegate = _bDelegate; //(5)
-(void)makeNewObjectAndSendBack {
//something something something
[self.bDelegate classB:self finishedWithObject:newObject]; //(6)
}
#end
define the protocol that will be established later
set an instance of an object that will conform to that protocol
set up the protocol
set up the method call that you'll use to send the finishedObject back to A.
synthesize the delegate
after you do what you need to do, you send it back using the method
you defined in 4
// ClassA.h
#interface ClassA : NSObject <ClassBDelegate> //(7)
#property (nonatomic, strong) ClassB theClassB;
-(void)yourMethodToDoSomething;
#end
ClassA.m
#implementation
#synthesize theClassB = _theClassB;
-(void)randomMethod {
self.theClassB = [ClassB new];
self.theClassB.bDelegate = self; //(8)
[self.theClassB makeNewObjectAndSendBack];
}
-(void) classB:(Class B *)theClassB finishedWithObject:(id)finishedObject { //(9)
[self doSomethingWithFinishedObject:finishedObject]; //ta-da!
}
#end
7.Conform to the ClassBDelegate protocol. This basically says that you
will implement the methods defined in the protocol definition.
8.Set the classB object's delegate object as self! This is crucial and
often skipped.
9.Implement the delegate method for when you get the new object back.
So the process, in short is: A instanciate B. A sets B's delegate as self. A tells B to do something. B does something and sends object back via delegate method. A gets it back.
For more information, including what you can do with protocols, check out:
Big Nerd Ranch talk about Protocols
Part 1
Part 2
Part 3
Good luck!

It seems you might be a little confused as to what delegates are and how they are used. Here are two links to Apple documentation you might find useful: A conceptual overview and a more in depth explaination.

Related

calling functions and passing information between views

Thanks in advance for your help!
In the main ViewController.m of my project I am adding a customized tableView like so:
messageController = [[MyMessagesController alloc] initWithStyle:UITableViewStylePlain];
[self addChildViewController:messageController];
[self.view addSubview:messageController.view];
Then, in the MyMessagesController.m section tableView didSelectRowAtIndexPath: I'd like to write code that would take effect in the ViewController.m where it was created and added as a childViewController.
How can I access the functions of the ViewController.m from MyMessagesController.m?
Can I make it a delegate somehow so I could call [delegate functionName];?
Could I pass information back to the ViewController.m? About which of the rows in table was selected by sending through an NSString or NSArray or anything?
Yes, use a delegate, if you are unsure how best to accomplish this, here is a good reference from Apple about delegate programming
Got it figured out, here's how you turn one viewController into a delegate for another:
In the .h of the parent controller -
#interface ViewController : UIViewController <NameOfDelegate> {
In the .m of the parent controller, once you create the new view -
newViewController.delegate = self;
and also:
- (void)functionToCall:(id)sender {
NSLog(#"Function Called!!!");
}
In the .h of the newViewController you're adding -
#protocol NameOfDelegate;
#interface newViewController : UIViewController/OR/TableViewController {
id <NameOfDelegate> delegate;
}
#property (nonatomic, strong) id <NameOfDelegate> delegate;
#end
#protocol NameOfDelegate
- (void)functionToCall:(id)sender;
#end
in the .m of the newViewController -
#implementation newViewController
#synthesize delegate;
and when you're ready to call the function in your delegate -
[delegate functionToCall:self];

IOS setting a custom delegate

I'm working through the IOS HelloWorld example and I have a question regarding setting the delegate for a TextField. In the example it was as easy as control-dragging from the TextField to the ViewController. But now say I wanted to create a custom class to act as my delegate as so:
#import <Foundation/Foundation.h>
#interface SweetAssDelegate : NSObject <UITextFieldDelegate>
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField;
#end
#import "SweetAssDelegate.h"
#implementation SweetAssDelegate
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField
{
NSLog(#"Calling Delegate");
[theTextField resignFirstResponder];
return YES;
}
#end
How can I set this class to be the delegate of the TextField? As far as I can tell there is not way to accomplish this through the GUI. I tried manually setting the delegation after window load with no success:
#import "ViewController.h"
#import "SweetAssDelegate.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *inputField;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
SweetAssDelegate *foo = [[SweetAssDelegate alloc] init];
[self.inputField setDelegate:foo];
NSLog(#"Delegate: %#", self.inputField.delegate);
}
I actually receive some sort of memory exception when bringing up the keyboard? Any ideas? Thanks.
As a side question, where should I always use viewDidLoad to initialize any variables? I noticed that init was not being called???
Your delegate object, foo, is allowed to fall out of scope and is released at the end of viewDidLoad and by the time the keyboard comes up, it doesn't exist anymore. Make it an ivar (or property) of your view controller, or otherwise make sure that foo doesn't fall out of scope at the end of viewDidLoad.
Thus, it could be something like:
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UITextField *inputField;
#property (strong, nonatomic) SweetAssDelegate *foo;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
self.foo = [[SweetAssDelegate alloc] init];
[self.inputField setDelegate:self.foo];
NSLog(#"Delegate: %#", self.inputField.delegate);
}
Your textfield delegate must have the implemented to be your textfield delegate I guess.
A delegate manages the communication between objects, which means your custom delegate must allow communication between objects and must provide methods, the textfield can work with...
Another example is a tableView:
You can make a custom delegate which implements the delegates and then calls some tableview related Methods...
Here this code might be interesting for you:
#interface myCustomDelegateForTextFields <UITextFieldDelegate>
#end
#implementation myCustomDelegateForTextFields
- (BOOL)textFieldShouldReturn:(UITextField *)theTextField {
return TRUE;
}
#end
#implementation ViewController
myCustomDelegateForTextFields *txtfielddelegate = [[myCustomDelegateForTextFields alloc] init];
UITextField *whatever;
whatever.delegate = txtfielddelegate;
//your textfield now listens to the BOOL method in your custom delegate
#end
Is it that what u were looking for? :)
you can ofc pack the myCustomDelegateForTextField delegate in another class and call the class

Delegate Method Protocol Not Working - Objective C

I am working on a splitView application for my iPad. I have implemented a UIButton called as Upload. On clicking on it, a UITableView appears inside a UIPoverController. On clicking on any of the contents, I want to display some respective site in my UIwebView in UIDetailView. For this I have implemented a delegate method protocol. I have used the following lines of code in UploadTableViewController.h file::
#protocol UploadTableViewDelegate <NSObject>
#required
- (void)selected:(NSString *)his;
#end
#interface UploadSpaceTableViewController : UITableViewController{
id<UploadSpaceTableViewDelegate> delegate;
}
#property (retain) id delegate;
#end
In the corresponding .m file I have used the following lines of code ::
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (delegate != nil) {
NSString *hisSelected = [keys objectAtIndex:indexPath.row];
NSLog(#"%# lolwa", hisSelected);
[delegate selected:hisSelected];
}
}
in the .m file of class where I have implemented the function Selected, the code is ::
- (void)selected:(NSString *)Key {
NSLog(#"hello");
[self.UploadSpaceTableViewPopover dismissPopoverAnimated:YES];
}
-(IBAction)uploadpressed:(id)sender{
Upload.delegate = self;
self.Upload = [[UploadSpaceTableViewController alloc]
initWithStyle:UITableViewStylePlain];
self.UploadTableViewPopover = [[UIPopoverController alloc]
initWithContentViewController:UploadSpace];
[self.UploadTableViewPopover presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
However, I am unable to get hello (written in the function Selected) NSLogged in gdb. This is the first time that I am using this delegate method protocol. I am unable to sort this out. Can someone help me out ? Thanks and regards.
[delegate keySelected:hisKeySelected]; is your first problem. You don't declare a delegate method named -keySelected:, you declare a delegate method named -Selected:.
Your second problem is the fact that you are most definitely not the delegate of your table view. In order for a delegate method like -didSelectRowAtIndexPath: to be called, you must be the table's delegate.
PS, don't begin instances, or method names, with an uppercase letter. In ObjC, uppercase letters indicate a class.
EDIT: this is what your UploadSpaceTableViewController header should look like:
#protocol UploadTableViewDelegate <NSObject>
#required
- (void)selected:(NSString *)his;
#end
#interface UploadSpaceTableViewController : UITableViewController<UITableViewDataSource, UITableViewDelegate>
#property (nonatomic, assign) id <UploadSpaceTableViewDelegate>delegate; //delegates are weak!!!
#end
And the .m, I will skip a lot of the unnecessary stuff:
-(void)viewDidLoad {
[self.tableView setDelegate:self];
[self.tableView setDataSource:self];
}
//other code
Furthermore, your delegate is declared retain, which is an absolutel No-No in ObjC. Declare is weak if using ARC, or assign if not.
You are also producing a nil delegate in in your -uploadPressed: method by setting it before you explicitly own or initialize the object. Here's how it should look:
self.Upload = [[UploadSpaceTableViewController alloc]initWithStyle:UITableViewStylePlain];
Upload.delegate = self;
Delegation works like this
declare a protocol - you have done this
declare a delegate property - you have done this
In the class which you want to be the delegate say it conforms to the protocoll
#interface MyClass : MySuperClass <UploadTableViewDelegate>
set the delegate property so the delegate class can get the delegate messages
uploadSpaceTVC.delegate = self;
call the delegate methods in your non delegate class (UploadSpaceTableViewController)
[self.delegate selected:#"test"];

Call a ViewController method from NSObject Class

I am trying to call a method thats in my ViewController from a NSObject Class thats doing some parsing.
I initally call a connection class I have made wich downloads some data from my server, I then pass this data over to a parser class I have made, now what I am trying to do is pass this data back to the viewcontroller and reload the tableview thats in this view (thats on a navigation stack)
anyway this is causing some errors and I think it might be the way I am trying to call this method thats doing it. here is how I call it.
MyViewController *myViewController = [[MyViewController alloc] init];
[myViewController initFilterArray:filteredArray];
Now I think this is causing an issue because I am allocating a new viewcontroller object? is that right.. not to sure of the terminoligy.. but yea..
the result of which is that reloaddata is only calling
numberOfSectionsInTableView
tableView:numberOfRowsInSection
then thats it.
any help would be appreciated.
UPDATE:
so I am trying to set up a protocol/delegate to see if that fixes my problem.
so in my class.h this is what I am doing
#protocol PassParsedData <NSObject>
#required
- (void) sendMyArray:(NSArray *)modelArray;
#end
//..
id <PassParsedData> delegate;
//..
#property (strong) id delegate;
then in class.m
//..method
[[self delegate]sendMyArray:filteredArray];
//..
so thats my class, then over in my view controller where I want to call this sendMyArray I do this
viewcontroller.h
#import "class.h" //delegates & protocols
//..
interface VehicleSearchViewController : UITableViewController <PassParsedData> {
//..
then i call it like this
viewcontroller.m
//..
- (void)sendArray:(NSArray *)array
{
ICMfgFilterArray = array;
[self.tableView reloadData];
}
One way of doing this would be the recommended approach of delegates and protocols.
Your NSObject declares a protocol. The ViewController actually implements the protocol and sets itself as the delegate. Then the NSObject calls the method (not knowing who implements it). It is a loosely-coupled way to communicate between objects.
I actually recently wrote a blog post on a basic introduction to protocols and delegates if you're interested...
UPDATE
Based on your update above in question.
Don't forget to set your ViewController to be the delegate.
- (void)viewDidLoad {
// State that you will take care of messages from graphView (provided you have the protocol implementation!)
self.yourClass.delegate = self;
}
And the method in your ViewController should match the protocol signature. So in ViewController.m
- (void) sendMyArray:(NSArray *)modelArray {
ICMfgFilterArray = array;
[self.tableView reloadData];
}

iphone: Implement delegate in class

I am trying to call up a modal table view controller using presentModalViewController but I am not sure what to do about the delegate. The following code gives me an error:
MyRidesListView *controller = [[MyRidesListView alloc] init];
controller.delegate = self;
[self presentModalViewController:controller animated:YES];
[controller release];
Error:
Request for member 'delegate' is something not a structure or union
Now, I realized there is no delegate property in my MyRidesListView class. So, how would I add a reference to my delegate there? What am I missing here?
Generally delegates are properties defined as such:
id<NameOfDelegateProtocol> delegate;
And:
#property (nonatomic, assign) id<NameOfDelegateProtocol> delegate;
EDIT: You said your parent class is UITableViewController. You may have wanted to do this:
controller.tableView.delegate = self;
Why do you think you need a delegate? Why not just remove the "controller.delegate = self" line. Otherwise you need to implement a delegate system the way I outline below or else make MyRidesListView a subclass of a viewcontroller that implements delegates.
It looks like you cut and pasted some sample code that uses a delegate, then substituted your own viewcontroller that doesn't provide a delegate. If you don't know what the delegate is for, then just delete that line.
I'll cut and paste some actual code from one of my test programs to show you how it's done:
from the Interface file:
Add a delegate instance variable to your class and make it a property so you can use the "blah.delegate = foo" syntax to set it.
#interface BAPClient : NSObject {
CGSize imageSize;
id <BAPClientDelegate> delegate;
}
#property (nonatomic, readonly) CGSize imageSize;
#property (nonatomic, assign) id <BAPClientDelegate> delegate;
#end
// define the protocol spoken. (what the delegate must implement)
#protocol BAPClientDelegate
- (void)addTile:(BAPTile *)tile;
#end
in the implementation, you must call the delegate at the appropriate time:
- (void)deliverTile:(BAPTile *) tile {
NSLog(#"%s tile=%p",__FUNCTION__,tile);
if ([self delegate])
[[self delegate] addTile:tile];
[tile release];
}
Try to set the delegate object by the setter
[controller setDelegate:self];
This often works wonders.