Getting the delegate to work between two view controllers - iphone

I am a newbie to iPhone development and have some basic questions to ask about protocols and delegates. I have two view controllers: view controller and viewcontroller2nd. I have UITextField in one of them and would like to type something (like a name) in it and in the viewcontroller2nd, I have a UILabel and i would like it to appear Hello, name when the UITextField is changed.
I am following this video: http://www.youtube.com/watch?v=odk-rr_mzUo to get the basic delegate to work in a single view controller.
I am using protocols to implement this:
SampleDelegate.h
#import <Foundation/Foundation.h>
#protocol ProcessDelegate <UITextFieldDelegate>
#optional
- (BOOL)textFieldShouldReturn:(UITextField *)textField;
#end
#interface SampleDelegate : NSObject
{
id <ProcessDelegate> delegate;
}
#property (retain) id delegate;
#end
SampleDelegate.m
#import "SampleDelegate.h"
#implementation SampleDelegate
#synthesize delegate;
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
lbl.text = [NSString stringWithFormat:#"Hello, %#",txtField.text];
[txtField resignFirstResponder];
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#import "SampleDelegate.h"
#interface ViewController : UIViewController <ProcessDelegate>
{
IBOutlet UITextField *txtField;
}
#end
Viewcontroller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
ViewController2nd.h
#import <UIKit/UIKit.h>
#interface ViewController2nd : UIViewController <ProcessDelegate> {
IBOutlet UILabel *lbl;
}
#end
and ViewController2nd.m is standard code from Xcode.
My question is how do i link my delegate function to my viewcontroller and viewcontroller2nd to get it working?
Pardon me if the question is stupid.. Need some guidance. Do point me any other mistakes that i am doing as well.. Thanks..

Your delegation is a bit... Off.
Firstly: Don't override UIKit delegate methods through protocol inheritance. It's pointless. Why not just make your class conform to the specified delegate in the first place?
#protocol ProcessDelegate //No more protocol inheritance!
//...
#end
Secondly: When an object has defined a protocol, a valid instance of that object must be in use by its delegate (or at least passed to it). So, anything that wants to be the delegate of SampleDelegate (really a bad name for a class, by the way) would initialize a valid SampleDelegate object, and call -setDelegate: as though it were any other property.
//#import "SampleDelegate"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//make this a property, so it isn't crushed when the function exits.
SampleDelegate *myDelegateObject = [[SampleDelegate alloc]init];
[myDelegateObject setDelegate:self]; //conform to the delegate
}
Thirdly: You don't actually define any delegate methods! What's the point of delegation if there's nothing to delegate!l
#protocol ProcessDelegate
-(void)someMethod;
#end
Fourth, and most important: Never, ever, ever, ever use the retain, or strong storage specifiers with a delegate! Delegate objects are supposed to be weak or assign to prevent nasty retain cycles.
#property (assign, nomatomic) id delegate;

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.

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

Defining delegate function in separate file (instead of in ViewController)

I have a ViewController, and a UIView.
The UIView has a delegate, and the delegate function is set in the ViewController.
All I want to do, is have the delegate function defined in a separate file. So the UIView.m #imports the separate file, instead of all the ViewControllers which use the UIView.
I believe this is a standard procedure, but keep falling over myself trying to get it to work. :| Would really appreciate some help. Thanks.
myViewController.h
#import <UIKit/UIKit.h>
#import "myUIView.h"
#protocol ModalViewDelegate
-(void)didReceiveMessage:(NSString *)message;
#end
#interface myViewController : UIViewController <ModalViewDelegate>
#property (nonatomic, strong) myUIView *myUIViewItem;
#end
myViewController.m
#import "myViewController.h"
#import "myUIView.h"
#interface myViewController ()
#end
#implementation myViewController
#synthesize myUIViewItem;
- (void)didReceiveMessage:(NSString *)message { //<<< THIS IS WHAT
NSLog(#"Message from button: %#", message); //<<< NEEDS MOVING
}
- (void)viewDidLoad
{
…
myUIViewItem.delegate = self;
…
myUIView.h
#import <UIKit/UIKit.h>
#protocol ModalViewDelegate;
#interface myUIView : UIView {
id<ModalViewDelegate> delegate;
}
#property (nonatomic, strong) id<ModalViewDelegate> delegate;
myUIView.m
#import "myUIView.h"
#import "myViewController.h"
#implementation myUIView
#synthesize delegate;
...
[delegate didReceiveMessage:#"Data from UIView!"];
well, there is one method actually,
Just take one .h file and lets say connectionDelegate.h and declare your protocol init
In connectionDelegate.h:
#import <UIKit/UIKit.h>
#protocol ConnectionDelegate
-(void)getResult:(NSString*)_result;
#end
Then in your view controller:
#import "ConnectionDelegate.h"
#interface myViewController : UIViewController <ConnectionDelegate>
{
id delegate;
}
then in .m file, by just call the method
[delegate getResult:_result];
Edit regarding the warnings:
You need to declare the method in view controller, you need to do like this.
[self getResult:urlDataString];
-(void)getResult:(NSString*)_result{
[delegate getResult:_result];
}
Based on your comment:
I want to have the function 'didReceiveMessage' defined in a SEPARATE
file. So that I don't have to repeat it in every ViewController that
uses the UIView and delegate. e.g. ModalViewDelegate_Action.h and
ModalViewDelegate_Action.m
The way I was given was to use a subclass, and that's been working great for me. In my iOS projects I have a class called BaseViewController, which is a subclass of UIViewController. I put lots of code in it related to HUD management, NSOperations management, etc. Then virtually all my view controllers are subclasses of it.

How can I execute code from another methods property

Sorry if the question is not to clear, this is what im trying to do. I have an object, a delegate and a view controller.
Object pseudo-code
#interface Car: NSObject {
UIColor *color;
}
#property (assign)UIColor *color;
- (void) setColor(UIColor col);
#end
#implementation Car
#synthetize color;
// i know that synthesize makes this function redundant.
// I just used it to demonstrate
// the need to access an instance method.
- (void) setColor(UIColor col)
{
color = col;
}
#end
delegate code
#interface myDelegate: UIApplicationDelegate {
Car *car;
UIViewController *theView;
}
#property (assign)Car *car;
#end
#implementation myDelegate
#synthesize car;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
theView = [[MyViewController alloc]init];
return YES;
}
#end
View pseudo Code
#interface MyViewController: UIViewController {
MyDelegate *appDelegate;
}
#property (retain) MyDelegate *appDelegate;
#end
#implementation MyViewController
#synthesize appDelegate;
- (void)viewDidLoad {
self.appDelegate = (MyDelegate*)[[UIApplication sharedApplication] delegate];
/// THIS IS MY ISSUSE!
[self.appDelegate.car setColor(UIColor);
}
#end
Can anyone explain or point me to where i can understand why [self.appDelegate.car setColor()] gives me a compile error that reads "Unknown component setColor of a property".
Im sure there is a way to do this in objective C as i would do it in python, java or other OO language.
Any help would be greatly appreciated
Cheers
Rudy
You are not using UIColor as a pointer.
Try using UIColor * instead of just UIColor and the compiler will stop complaining
First of all, the Car class has problems. The color property should be defined as retain, not assign. assign is generally for non-object type properties, such as an NSInteger, BOOL, int, etc., and for a special case of objects that shouldn’t be retained because they’d create retain cycles. Also, - (void)setColor(UIColor col); is not a valid method name, as it is written as if it were a function. In Objective-C, each parameter is preceded by a colon :.
For example, take the following method:
- (void)setBodyColor:bodyColor lowerColor:lowerColor upperColor:upperColor;
While that is technically a valid method signature, it is generally written differently to make its usage more clear. As it’s defined above, each parameter is of type id, which is a generic object type. To make things clearer, you cast each argument to the type of objects they represent:
- (void)setBodyColor:(UIColor *)bodyColor
lowerColor:(UIColor *)lowerColor
upperColor:(UIColor *)upperColor;
In addition to being defined incorrectly, it’s also superfluous since defining a read-write property named color implies that a -setColor: method will be defined. The code would look like this:
#interface Car: NSObject {
UIColor *color;
}
#property (retain) UIColor *color;
#end
#implementation Car
#synthetize color;
- (void)dealloc {
[color release];
[super dealloc];
}
// If you need to override a method, that’s fine
- (void) setColor:(UIColor *)aColor
{
[aColor retain];
[color release];
color = aColor;
// do more stuff
}
#end
On to your delegate, it also has problems. First, myDelegate is defined as a subclass of UIApplicationDelegate, which is not even a class: it’s a protocol (or interface) that other objects can conform to. The car property should also be defined as retain, since it’s an object that your app delegate owns. Here, theView (which should likely be renamed to something like theViewController) should be typed as MyViewController to make it more clear.
#interface MyDelegate : NSObject <UIApplicationDelegate> {
Car *car;
MyViewController *theView;
}
#property (retain) Car *car;
#end
#implementation MyDelegate
#synthesize car;
- (void)dealloc {
[car release];
[theView release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
theView = [[MyViewController alloc] init];
return YES;
}
#end
The MyViewController class has problems in that the appDelegate is defined as retain when it should likely be assign. The app delegate itself is creating the view controller using alloc/init, meaning the app delegate “owns” the view controller. The view controller shouldn’t retain the app delegate because that would create a retain cycle (see Retain Cycles).
MyViewController.h
// forward declaration
#class MyDelegate;
#interface MyViewController: UIViewController {
MyDelegate *appDelegate; // non-retained
}
#property (assign) MyDelegate *appDelegate;
#end
MyViewController.m
#import "MyViewController.h"
#import "MyDelegate.h"
#implementation MyViewController
#synthesize appDelegate;
- (void)viewDidLoad {
self.appDelegate = (MyDelegate*)[[UIApplication sharedApplication] delegate];
[self.appDelegate.car setColor:[UIColor clearColor]];
/// THIS IS MY ISSUSE!
// [self.appDelegate.car setColor(UIColor);
}
#end

A question of objective-C protocol

I try to learn protocol of objective C.
I write two files, the first one is FirstViewController.h, and in which there is a protocol "print". I declare FirstViewController class in successViewController with the delegate method "print".
The question is why the console output is "C". Why I can not get the "B" output? Why the protocol method did not perform?
#import <UIKit/UIKit.h>
#import "FirstViewController.h"
#interface successViewController : UIViewController <FirstViewControllerDelegate> {
}
#end
#import "successViewController.h"
#import "FirstViewController.h"
#implementation successViewController
- (void)viewDidLoad {
FirstViewController *firstViewController= [[FirstViewController alloc] init];
firstViewController.delegate=self;
NSLog(#"C");
[super viewDidLoad];
}
-(void) print{
NSLog(#"B");
}
#end
#import <Foundation/Foundation.h>
#class FirstViewController;
#protocol FirstViewControllerDelegate <NSObject>
- (void) print;
#end
#interface FirstViewController : NSObject {
id <FirstViewControllerDelegate> delegate;
}
#property (nonatomic, assign) id <FirstViewControllerDelegate> delegate;
#end
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize delegate;
#end
Because you never call the print method. Where were you expecting it to be called?
Objective-C protocols allow you to specify that a class is capable of performing certain actions. In your example, the successViewController is declared FirstViewControllerDelegate, meaning it is capable of handing the duties required by FirstViewController of its delegate. It is more of a programming contract between classes, one that can be verified by the compiler.
As a side note, classes in Objective-C should always start with a capital letter, methods should always start lowercase. Your FirstViewController follows this rule, but the successViewController does not.
You need to call the method you want to use.
[successViewController print];
You never call the delegates print method. A delegate can not read your mind and automagically call stuff. Lets take a small example how how delegates are supposed to work.
Assume we have a class called Delay, the only thing it do is to wait for a time when start is called, and then tell it's delegate that it has waited. Optionally the delegate can tell the Delay how long to wait, if the client do not care, a 1 second delay is assumed.
Some rules:
First argument of all delegate methods should be the sender itself, never have delegate methods with no arguments.
Delegate method name should include one of the words:
will - if method is called before something unavoidable occurs. Example applicationWillTerminate:
did - if method is called after something has occurred. Example scrollViewDidScroll:
should - if the method return a BOOL to signal if something should occur. Example textFieldShouldClear:
Name the method to tell what has occurred, not what you expect the delegate to do.
Only exception is if the client is expected to return something, then that something should be part of the name. Example: tableView:editingStyleForRowAtIndexPath:
Here is the simple definition and implementation. Notice that I do not even check if the delegate has been set, since calling methods on nil is just ignored anyway.
// Delay.h
#protocol DelayDelegate;
#interface Delay : NSObject {
#private
id<DelayDelegate> _delegate;
}
#property(nonatomic, assign) id<DelayDelegate> delegate;
-(void)start;
#end
#protocol DelayDelegate <NSObject>
#required
-(void)delayDidComplete:(Delay*)delay;
#optional
-(NSTimeInterval)timeIntervalForDelay:(Delay*)delay;
#end
// Delay.m
#interface Delay
#synthesize = delegate = _delegate;
-(void)start {
NSTimeInterval delay = 1.0;
if ([self.delegate respondsToSelector:#selector(timeIntervalForDelay:)]) {
delay = [self.delegate timeIntervalForDelay:self];
}
[self performSelector:#selector(fireDelay) withObject:nil afterDelay:delay];
}
-(void)fireDelay {
[self.delegate delayDidComplete:self];
}
#end