Using delegate to pass data between views - iphone

I am building a utility application which shares data between main view and flip view. Actually, it is not exactly the flip view that's holding data, it's the custom view that's an instance of the flip view when it gets loaded. I have explained the specifics in my previous thread here, but I haven't got a solution yet. And I have redeveloped my code, hopefully this time I could make myself clear.
The general concept here is I create and store data in my main view, and pass it to the flip side view using the predefined delegate in the FlipViewController. Then in the FlipViewController, I store the data in my own delegate and pass it to the custom view which implements my own delegate method. The following is the main portions of the code.
MainViewController.m (only adopts <FlipsideViewControllerDelegate> protocol)
- (IBAction)showInfo:(id)sender {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
controller.chart = data;
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
FlipsideViewController.h
#protocol FlipsideViewControllerDelegate;
#protocol ChartDelegate;
#interface FlipsideViewController : UIViewController {
id <FlipsideViewControllerDelegate> delegate;
id <ChartDelegate> delegate2;
DataModel *chart;
}
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
#property (nonatomic, assign) id <ChartDelegate> delegate2;
#property (nonatomic, retain) DataModel *chart;
- (IBAction)done:(id)sender;
#end
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#protocol ChartDelegate <NSObject>
- (void)getParams:(DataModel *)dataModel;
#end
FlipsideViewController.m
#synthesize delegate, delegate2;
#synthesize chart;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
if ([delegate2 respondsToSelector:#selector(getParams:)]) {
[delegate2 getParams:chart];
}
}
customDrawing.h
#interface customDrawing : UIView <ChartDelegate>{
DataModel *chartData;
}
#property (nonatomic, retain) DataModel *chartData;
#end
customDrawing.m
#synthesize chartData;
-(void)getParams:(DataModel *)dataModel{
chartData = dataModel;
}
It turns out the data didn't get passed to the chartData object in my custom view. HELP?

You are missing the fundamentals. I do not think you need delegates to achieve this task but here we go.
A protocol is like a contract. In you FlipsideViewController class you defined the protocol which essentially states if you conform to this protocol then you must implement this method.
How do you conform to a protocol?
In MainViewController the #interface will look something like this
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
The fact that you have the protocol written in angled brackets means that you promise to conform to the protocol and therefore have to implement
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
in your MainViewController.m.
Now when MainNavigationController set's itself as the delegate (controller.delegate = self;) it finishes the link. This allows the FlipsideViewController to call
[delegate flipsideViewControllerDidFinish:self];
Which will call the method defined in MainViewController which dismisses the modal view controller.
You have defined a second protocol (you could have added the method to the first and then you would not have to adopt two protocols) and as others have pointed out you have not linked the classes up by doing
controller.delegate2 = self;
This would not solve your problem. You would still need to conform to the ChartDelegate by adding it to the declaration. Once you have done that you will still not be out of the water because the method is not correct.
Full solution - not using delegates as they are not really required here
MainViewController.h
#interface MainViewController : UIViewController <FlipsideViewControllerDelegate>
- (IBAction)showInfo:(id)sender;
#end
MainViewController.m
#implementation MainViewController
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller
{
[self dismissModalViewControllerAnimated:YES];
}
- (IBAction)showInfo:(id)sender
{
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"FlipsideView" bundle:nil];
controller.delegate = self;
/*
* The labelText property is defined in the header for FlipsideViewController
* In this case this is the easiest way to get data from this controller to
* the controller we are about to display
*/
controller.labelText = #"WHAT EVER YOU WANT TO SEND"; // <---- sending data
controller.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:controller animated:YES];
[controller release];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#end
FlipsideViewController.h
#class FlipsideViewController;
#protocol FlipsideViewControllerDelegate
- (void)flipsideViewControllerDidFinish:(FlipsideViewController *)controller;
#end
#interface FlipsideViewController : UIViewController
/*
* These properties have been added. The label is used for displaying the text
* and needs to be hooked up in Interface builder
*
* The NSString is the property that is holding the data passed from MainViewController
*/
#property (nonatomic, retain) IBOutlet UILabel *testLabel;
#property (nonatomic, copy) NSString *labelText; from MainViewControlller
#property (nonatomic, assign) id <FlipsideViewControllerDelegate> delegate;
- (IBAction)done:(id)sender;
#end
FlipsideViewController.m
#implementation FlipsideViewController
#synthesize delegate = _delegate;
/*
* We need to synthesise out properties so we get our getters and setters created
*/
#synthesize testLabel = _testLabel;
#synthesize labelText = _labelText;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
/*
* This is called once the view is set up and all connections have been made in
* interface builder. Therefore we can now set the text of our test label
*/
self.testLabel.text = labelText;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
#pragma mark - Actions
- (IBAction)done:(id)sender
{
[self.delegate flipsideViewControllerDidFinish:self];
}
- (void)dealloc
{
/*
* Memory management for the ivars we added
*/
[_testLabel release];
[_labelText release];
[super dealloc];
}
#end

You have two properties: delegate and delegate2. You are assigning a value to delegate, but calling the method on delegate2 later.

You need to assign the delegate2 (your customDrawing class). You are only assigning the 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.

How to call a method from a UIViewController thats already on the UINavigationController stack

I have a UIViewController on a UINavigationStack and from this UIView I load another view not onto the stack but as a subview. This view that I load is just a preferences view for the app that I overlay onto what ever is showing.
i.e.
myViewController <- on the stack button touch loads as a subview to myViewController
+ prefrencesViewController
My question is, is there a way to call a method thats in myViewController from prefrencesViewController? I am trying to use delegates and protocols but its not working, so I am hoping there is either an easy way to do this I don't know about yet or maybe I could get some help with my delegate/protocol...
This is what my code looks like for delegate and protocol set up
//prefrencesViewController.h
#protocol GetPrefrencesViewControllerDelegate <NSObject>
-(void)reloadViewFromSavedPrefrences;
#end
//delegates and protocols
#property (nonatomic, weak) id <GetPrefrencesViewControllerDelegate> delegate;
//prefrencesViewController.m
//delegates and protocols
#synthesize delegate;
//.. inside button action
[[self delegate] reloadViewFromSavedPrefrences];
//myViewController.h
#import "prefrencesViewController.h"
#interface myViewController : UIViewController <UITabBarDelegate, GetGUIEncodedData, GetPrefrencesViewControllerDelegate> {
// prefrencesViewController set up
prefrencesViewController *pvc;
#property (strong, nonatomic) prefrencesViewController *pvc;
//myViewontroller.h
#synthesize pvc;
- (void)viewDidLoad
{
//..
[pvc setDelegate:self];
}
//Delegate and prefrences.. Saved pressed reload the view here.
-(void)reloadViewFromSavedPrefrences {
NSLog(#"WORKED");
}
any help would be greatly appreciated
I'm not sure that you are following the steps that I will present below but if you don't here is the example.
PresentedViewController.h
//import stuff
#protocol PresentedViewControllerDelegate <NSObject>
-(void)methodThatSouldBeImplementedByOtherController; //you can add params
#end
#interface PresentedViewController : UIViewController {
//instance variables
}
#property (nonatomic, assign(week for ARK)) id<PresentedViewControllerDelegate>delegate
//public methods here
PresentedViewController.m
#implementation PresentedViewController
#synthesize delegate;
//method implementation here
-(IBAction)buttonThatWillCallTheDelegate:(id)sender {
if([self.delegate respondsToSelector:#selector(methodThatSouldBeImplementedByOtherController)]) {
[self.delegate methodThatSouldBeImplementedByOtherController];
}
}
ControllerThatWillPresent.h
#interface ControllerThatWillPresent : UIViewController <PresentedViewControllerDelegate> {
//instance variables
}
//some methods maybe
ControllerThatWillPresen.m
#implementation ControllerThatWillPresen
-(void)methodThatWillShowTheVC {
PresentedViewController *vc = [PresentedViewController alloc] init]; //initWithNibname...
vc.delegate = self;
//presentVc, pushVc, addChild ...
}
-(void)methodThatSouldBeImplementedByOtherController {
//do stuff in delegate method
}

Custom Delegate, dismiss popover from another view

I could use some help with custom delegates. I'm trying to make a protocol that sends a message to its delegate to dismiss the popover view. Here is what I'm trying.
In the popoverViewController.h
#import <UIKit/UIKit.h>
#protocol MyPopoverDelegate <NSObject>
-(void) didSelectLanguage;
#end
#interface Popover : UITableViewController{
id <MyPopoverDelegate> delegate;
NSMutableArray *languageData;
}
#property (nonatomic, assign) id <MyPopoverDelegate> delegate;
#end
.m
#synthesize delegate;
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"You selected %#", [languageData objectAtIndex:[indexPath row]]);
[self.delegate didSelectLanguage];
}
...
And in the ViewController that presents the popover
#import <UIKit/UIKit.h>
#import "popoverViewController.h"
#interface ChoicesChoices : UIViewController <UIPopoverControllerDelegate, MyPopoverDelegate>{
UIPopoverController *popover;
}
- (IBAction)facebook:(id)sender;
- (IBAction)twitter:(id)sender;
- (IBAction)sms:(id)sender;
- (IBAction)copy:(id)sender;
- (IBAction)email:(id)sender;
- (IBAction)home:(id)sender;
- (IBAction)mute:(id)sender;
- (IBAction)note:(id)sender;
#property (nonatomic, retain) UIPopoverController* popover;
#end
and .m
#synthesize popover;
...
- (void)didSelectLanguage{
[popover dismissPopoverAnimated:YES];
NSLog(#"didSelectLanguage fired");
}
When I select a row in the table of the popover, didSelectLanguage does not get called. Any ideas on what I might be doing wrong? Thanks for your help.
Make sure you are setting your delegate to the be the view controller that is presenting your popover. Something like this in ChoicesChoices.m:
- (void)presentPopover
{
// assuming ARC for all allocations
Popover *myController = [Popover new];
myController.delegate = self;
self.popover = [[UIPopoverController alloc] initWithContentViewController:myController];
[self.popover presentPopover...]; // two flavors here, FromRect: and FromBarButtonItem:, that's left up to you to choose which one is correct.
}
Make sure you set the delegate in the presenting view controller when you create the instance of your custom class.
popover.delegate = self
Also, it looks like your property is a standard popover controller instead of an instance of your custom view controller.

Xcode: Does the delegating object HAVE to send a message to the delegate object?

I'm trying to assign SecondViewController as a delegate object of FirstViewController (if I understand correctly). However FirstViewController doesn't send any messages to SecondViewController.
Am I allowed to pretend as though SecondViewController did get a message from FirstViewController and respond to the FirstViewController? (Note: My SecondViewController is in charge of a view that has a button. When I press the button on my SecondViewController's view I want it to tell the FirstViewController to update its view)
FirstViewController.h
#import <UIKit/UIKit.h>
#protocol FirstViewControllerDelegate <NSObject>
#optional
- (void) setAnotherLabel;
#end
#interface FirstViewController : UIViewController {
IBOutlet UILabel *label;
id <FirstViewControllerDelegate> delegate;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, assign) id <FirstViewControllerDelegate> delegate;
- (void) pretendLabel;
- (void) realLabel;
#end
FirstViewController.m
#import "FirstViewController.h"
#implementation FirstViewController
#synthesize label;
#synthesize delegate;
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void) setAnotherLabel;
{
label.text =#"Real";
[self.view setNeedsDisplay];
}
- (void) pretendLabel;
{
label.text =#"Pretend";
[self.view setNeedsDisplay];
}
- (void) realLabel;
{
[self setAnotherLabel];
}
- (void)viewDidLoad
{
[super viewDidLoad];
label.text=#"Load";
[self pretendLabel];
}
...
#end
SecondViewController.h
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "FirstViewController.h"
#interface SecondViewController : UIViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate, FirstViewControllerDelegate>
{
UIImage *image;
IBOutlet UIImageView *imageView;
}
- (IBAction) sendPressed:(UIButton *)sender;
- (IBAction) cancelPressed:(UIButton *)sender;
#end
SecondViewController.m
#import "SecondViewController.h"
#implementation SecondViewController
- (IBAction) sendPressed:(UIButton *)sender
{
FirstViewController *fvc = [[FirstViewController alloc] init];
[fvc setDelegate:self];
//how do I find out if I'm actually the delegate for FirstViewController at this point?
[fvc realLabel];
self.tabBarController.selectedIndex = 0;//switch over to the first view to see if it worked
}
There are a few issues with this and what appears to be a bit of confusion.
I assume that FirstViewController and SecondViewController are in separate tabs in the tab bar controller.
In the sendPressed: method, you're creating a new instance of FirstViewController - this is not the same FirstViewController that is in your tab bar controller and why calling realLabel has no effect.
The second point is that you appear to misunderstand how delegation works - there is no reason for a delegate in the code you posted.
Good read for getting to grips with delegates: http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/CocoaFundamentals/CommunicatingWithObjects/CommunicateWithObjects.html
As for a solution to your problem there are a few options:
Post a notification from SecondViewController that FirstViewController is observing (lots of information available on the net regarding notifications).
Get the specific instance of FirstViewController within the self.tabBarController.viewControllers array and call the method from there. Something like...
- (IBAction) sendPressed:(UIButton *)sender
{
for(UIViewController *controller in self.tabBarController.viewControllers)
{
if([controller isKindOfClass:[FirstViewController class]])
{
FirstViewController *firstViewController = (FirstViewController *)controller;
[firstViewController realLabel];
}
}
self.tabBarController.selectedIndex = 0;//switch over to the first view to see if it worked
}
There are more options available than this, but the above will give you a good start into researching the best approach for your need.
Hope this helps.

Implementing delegate methods for modal view controller data transfer

I have a simple project to present a modal view controller and transfer back a string based on which button in the modal VC that gets pressed. I based it all on watching the Stanford class on iTunes U. It looks like I have everything correct, but I get a couple of compiler warnings.
First I get one called passing argument 1 of 'setDelegate:' from incompatible pointer type in TransferViewController.m
Second I get four warnings called Invalid receiver type 'id <MyModalViewControllerDelegate>*' but these aren't displayed in the build results area, rather next to the offending lines in MyModalViewController.m, both lines in each of the button actions.
Here's the code...
// TransferViewController.h
#import <UIKit/UIKit.h>
#import "MyModalViewController.h";
#interface TransferViewController : UIViewController <MyModalViewControllerDelegate> {
UILabel *label;
UIButton *button;
}
#property (nonatomic, retain) IBOutlet UILabel *label;
#property (nonatomic, retain) UIButton *button;
- (IBAction)updateText;
#end
// TransferViewController.m
#import "TransferViewController.h"
#implementation TransferViewController
#synthesize label;
#synthesize button;
- (IBAction)updateText {
MyModalViewController *myModalViewController = [[MyModalViewController alloc] init];
myModalViewController.delegate = self; // I get the warning here.
[self presentModalViewController:myModalViewController animated:YES];
[myModalViewController release];
}
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog {
label.text = selectedDog;
[self dismissModalViewControllerAnimated:YES];
}
#end
// MyModalViewController.h
#import <UIKit/UIKit.h>
#protocol MyModalViewControllerDelegate;
#interface MyModalViewController : UIViewController {
UIButton *abby;
UIButton *zion;
id <MyModalViewControllerDelegate> delegate;
}
#property (assign) id <MyModalViewControllerDelegate> delegate;
- (IBAction)selectedAbby;
- (IBAction)selectedZion;
#end
#protocol MyModalViewControllerDelegate <NSObject>
#optional
- (void)myModalViewController:(MyModalViewController *)controller didFinishSelecting:(NSString *)selectedDog;
#end
// MyModalViewController.m
#import "MyModalViewController.h"
#implementation MyModalViewController
#synthesize delegate;
- (IBAction)selectedAbby {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Abby"];
}
}
- (IBAction)selectedZion {
if ([self.delegate respondsToSelector:#selector (myModalViewController:didFinishSelecting:)]) {
[self.delegate myModalViewController:self didFinishSelecting:#"Zion"];
}
}
Get rid of those *s after id <something> and before delegate.
So make this
id <MyModalViewControllerDelegate> *delegate;
this
id <MyModalViewControllerDelegate> delegate;