Posts Tagged data sharing

Sharing Information Between Views

One of the most common questions in the Getting Started section of Apple’s Developer Forums is, “How do I edit information in one view and update another using it?” Sometimes it’s expressed instead as how to compare or save information in different views but it’s really the same question. There are two answers: the expedient one and the good one. Before I get to either of those, however, I’d like to set up a sample situation.

The Problem:
I’m going to work with a project that’s created using the standard iPhone Tab Bar Application template, using Xcode 4.0.2. It defaults to having two view controllers and my plan is to have the first one be a data display screen while the second is a data editing screen. (Note: My latest build of this works on Xcode 4.5.2 and iOS 6.0 with conversion to ARC.)

To prepare for this, I edit the supplied .xib files so that FirstView.xib has only a UILabel element inside its view and SecondView.xib has only a UITextField element inside its view. I then modify the related code files for the view controllers to define and synthesize properties that match the label and the text field. Finally, within the .xib files, I make the connections between the File’s Owner properties that were just created in code and the visual elements.

Usually, this is the state the person has reached when they ask the question. If they haven’t made it this far, the questions are more likely to be about how to use Interface Builder.

The Expedient Solution:
This addresses the problem as a lack of understanding about objects. The point that’s being missed is that objects need to have references to other objects while the program is running in order to communicate. Since I want my view controllers to communicate with each other, one of them must have a reference to the other. I could have my editor update the display whenever data changed but, for a variety of reasons, I prefer the idea of the display being in control of its own content.

Within the code for the FirstViewController, I create a property that references a SecondViewController object and synthesize it. Instances of both controllers exist inside MainWindow.xib so I connect the new property of the first controller to the SecondViewController instance.

The last step is to write code so that the FirstViewController can obtain the most recent information whenever it is about to be displayed…and that is now trivial.

Relevant controller code:

//  SecondViewController.h

#import <UIKit/UIKit.h>

@interface SecondViewController : UIViewController <UITextFieldDelegate> {
}

@property (nonatomic, retain) IBOutlet UITextField *dataEditor;

- (NSString *)editedText;

@end

// SecondViewController.m

#import “SecondViewController.h”

@implementation SecondViewController

@synthesize dataEditor;

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    return YES;
}

- (NSString *)editedText {
    if (dataEditor == nil) {
        return @”Not initialized”;
    }
    return dataEditor.text;
}

- (void)dealloc {
    [dataEditor release];
    [super dealloc];
}

@end

// FirstViewController.h

#import <UIKit/UIKit.h>

@class SecondViewController;

@interface FirstViewController : UIViewController {
}

@property (nonatomic, retain) IBOutlet UILabel *dataDisplay;
@property (nonatomic, retain) IBOutlet SecondViewController *dataSource;

@end

// FirstViewController.m

#import “FirstViewController.h”
#import “SecondViewController.h”

@implementation FirstViewController

@synthesize dataDisplay, dataSource;

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    dataDisplay.text = [dataSource editedText];
}

- (void)dealloc {
    [dataDisplay release];
    [dataSource release];
    [super dealloc];
}

@end

The Better Solution:
Here, in addition to thinking about objects, I use a simple implementation of the Model-View-Controller (MVC) object pattern. In the previous solution, the second controller acted as the data model for the application. Programs become much easier to expand and modify when that role is given to objects specifically created for the purpose.

I create a new class called SharedModel and make it a subclass of NSObject. I create a property for it that will hold a reference to a NSString object. (In a real application, the SharedModel could be expanded to serve any amount of data, even remote sites.)

I remove all references to SecondViewController from the first controller and insert properties called ‘dataSource’, that each specify a reference to the SharedModel, into both controllers.

The following is not necessarily the best way to handle the creation of the SharedModel but, for illustration purposes, it works. In MainWindow.xib, I add a NSObject and set its class to SharedModel. I then connect the dataSource property of each view controller to the new SharedModel object in the .xib file.

The last steps are: to add one line of code in the second controller that updates the SharedModel when editing ends, to remove the code from the second controller that reported the edited text to the caller, and to modify the update line in FirstViewController so that it uses the data model instead of the second controller directly.

Relevant controller and model code:

// SharedModel.h

#import <Foundation/Foundation.h>

@interface SharedModel : NSObject {
}

@property (nonatomic, retain) NSString *editedText;

@end

// SharedModel.m

#import “SharedModel.h”

@implementation SharedModel

@synthesize editedText;

- (id)init {
    self = [super init];
    if (self) {
        self.editedText = @”Not updated”;
    }
    return self;
}

- (void)dealloc {
    [editedText release];
    [super dealloc];
}

@end

// SecondViewController.h

#import <UIKit/UIKit.h>

@class SharedModel;

@interface SecondViewController : UIViewController {
}

@property (nonatomic, retain) IBOutlet UITextField *dataEditor;
@property (nonatomic, retain) IBOutlet SharedModel *dataSource;

@end

// SecondViewController.m
#import “SecondViewController.h”
#import “SharedModel.h”

@implementation SecondViewController

@synthesize dataEditor, dataSource;

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    [textField resignFirstResponder];
    dataSource.editedText = textField.text;
    return YES;
}

- (void)dealloc{
    [dataSource release];
    [dataEditor release];
    [super dealloc];
}

@end

// FirstViewController.h

#import <UIKit/UIKit.h>

@class SharedModel;

@interface FirstViewController : UIViewController {
}

@property (nonatomic, retain) IBOutlet UILabel *dataDisplay;
@property (nonatomic, retain) IBOutlet SharedModel *dataSource;

@end

// FirstViewController.m

#import “FirstViewController.h”
#import “SharedModel.h”

@implementation FirstViewController

@synthesize dataDisplay, dataSource;

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    dataDisplay.text = dataSource.editedText;
}

- (void)dealloc {
    [dataDisplay release];
    [dataSource release];
    [super dealloc];
}

@end

, , ,

1 Comment