Archive for 4th October 2009

Passing values and messages between views on iPhone

I am working on an iPhone application with some regular views and a table view, and I need to have the table view communicate with the main view. Seemingly a simple enough problem, but as I searched and searched, I could not find any concrete examples of how to do this, just a bunch of vague references about how views need to talk to each other and a larger number of folks suggesting the use of singletons.

Now, I have been known to use a global variable or two in my day, but in this instance, I figured I would dig a bit deeper.

As it turns out, the means to accomplish this for my task was easier than I thought. All that I did was to store a reference to the primary view controller in my secondary view controller, and that gave me access to any of the member variables in that class. In addition, this also gives me the ability to send messages to methods in that class.

So, in my primary view controller header file, we have the following:

#import <UIKit/UIKit.h>
 
@interface PassingValuesBetweenViewsViewController : UIViewController {
	IBOutlet UILabel *label;
	int selectedValue;
}
 
@property (nonatomic, retain) UILabel *label;
@property (nonatomic) int selectedValue;
 
- (IBAction)buttonWasTapped;
 
- (void)tableItemWasSelected:(int)itemSelected;
 
@end

And in the primary view controller implementation file:

#import "PassingValuesBetweenViewsViewController.h"
#import "SecondaryViewController.h"
 
@implementation PassingValuesBetweenViewsViewController
 
@synthesize label;
@synthesize selectedValue;
 
- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
 
	// Release any cached data, images, etc that aren't in use.
}
 
- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
}
 
- (void)dealloc {
    [super dealloc];
}
 
- (IBAction)buttonWasTapped
{
	SecondaryViewController *svc = [SecondaryViewController alloc];
	svc.vc = self;
	[self.view addSubview:svc.view];
}
 
- (void)tableItemWasSelected:(int)itemSelected
{
	NSLog(@"tableItemWasSelected called with %d", itemSelected);
	NSLog(@"The current value of selectedValue is %d", selectedValue);
	label.text = [NSString stringWithFormat: @"You selected #%d", itemSelected];
}
 
@end

The selectedValue member is an integer that gets updated in the table view with the selected cell, and the tableItemWasSelected methods is called from the table view when a cell is selected. In the buttonWasTapped method, the secondary view controller gets a reference to the primary view controller after it is allocated, and this enables the magic to happen.

Here is the secondary view controller header file:

#import <UIKit/UIKit.h>
 
#import "PassingValuesBetweenViewsViewController.h"
 
@interface SecondaryViewController : UITableViewController {
	PassingValuesBetweenViewsViewController *vc;
}
 
@property (nonatomic, retain) PassingValuesBetweenViewsViewController *vc;
 
@end

And the secondary view controller implementation file:

#import "SecondaryViewController.h"
 
@implementation SecondaryViewController
 
@synthesize vc;
 
- (void)didReceiveMemoryWarning {
	// Releases the view if it doesn't have a superview.
    [super didReceiveMemoryWarning];
 
	// Release any cached data, images, etc that aren't in use.
}
 
- (void)viewDidUnload {
	// Release any retained subviews of the main view.
	// e.g. self.myOutlet = nil;
}
 
#pragma mark Table view methods
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
 
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 50;
}
 
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Set up the cell...
	cell.textLabel.text = [NSString stringWithFormat: @"Cell #%d", indexPath.row];
    return cell;
}
 
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
	vc.selectedValue = indexPath.row;
	[vc tableItemWasSelected:indexPath.row];
	[self.view removeFromSuperview];
}
 
- (void)dealloc {
    [super dealloc];
}
 
@end

As you can see here, the primary view controller is references from the didSelectRowAtIndexPath method, both in setting a member variable through standard dot notation, and in sending a message with a parameter to the primary view controller.

I have posted a copy of this code if you would like to download the entire project and work with it:

PassingValuesBetweenViews.zip

Please keep in mind this is not finished or production quality code.