MVC on iPhone: “The Model” (Part 1)
- Transfer
CocoaTouch has been built from the start with an eye on the MVC paradigm . Almost all templates, views and their controllers for the user are ready. The key classes are UIView and UIViewController . In many cases, the " UIView " method is applicable on its own - with the addition of user interface elements to the general " UIView " in the IB editor . To create your own functions, add subclasses to the " UIViewController ". The IBOutlet qualifiers allow you to associate user interface elements with a view, providing access to them.
? I practically did not find information about him. In programming lessons, they prefer not to work with the model by typing code directly in the controllers.
Having achieved, as it seemed to me, good results with the implementation, I propose them here for discussion and evaluation. I will explain briefly. I am creating a " Singleton " class that extends " NSObject " for my model. Then, by monitoring the keys / variables, I learn about updates. This is a lot like the “ ModelLocator ” from “ Cairngorm ”, if someone had to work with it in “ Flex ”.
First, create a project with a couple of views. One of them will allow the user to change the value. This value is set for the model, which, in turn, triggers the change in another view. For this purpose the " Utility Application " template is quite suitable . So, we create a project on its basis and assign it the name " MVC ". (In principle, the name can be anything, but for the convenience of working with the lesson, it is better to duplicate it.)
The result should be something like this:

As you can see, we have the " Main View " and " Flipside View " objects , and for both there are controllers and nib- files. Run the project and read the code first. I will concentrate mainly on the points regarding the model, believing that the rest is already clear.
Now add a label and a text box to the views. Label - for " Main View ", field, respectively, for " Flipside View ". First, let's take a look at variables like outlet .
The file " MainViewController.h " should look like the one shown below.
In the same way, in the file " FlipsideViewController.h " we add the variable " textField ", as well as " IBAction ", which will let us know about changing the text in the field
and FlipsideViewController.m:
Now you can open " MainView.xib " and add " UILabel " to the view . From the File's Owner object (class " MainViewController "), drag a connector line to the label, linking it to the corresponding variable.


Do the same in the " FlipsideView.xib " file by adding the " UITextField " element and associating it with the " textField " variable for " File's Owner " (" FlipsideViewController "). Attach the IBAction " textChanged " to the " editingChanged " event of the text field.


By this time, it has probably already become clear that our goal is the user to enter some text in the field, which then appears on the label. But these are two separate views with their controllers? How to establish interaction between them? Using the model, of course. After saving both nib files , close the IB editor and return to Xcode .
It is time to create a model. Add a new file to the project - a subclass for " NSObject ". Name the class " Model ". Not that I was an ardent fan of " Singleton " templates . I am well aware of their shortcomings and believe that sometimes they are used unjustifiably, but as a pragmatist, I am sure that in this case this is an acceptable option.
Those who are intolerable to the idea of singleton can create a model in the " RootViewController " and pass an instance to each of the view controllers. That should work. I’ll choose another way using the ingenious SynthesizeSingleton file from [ CocoaWithLove.com ]. By the way, there you can express your attitude towards singletons.
Now add the file " SynthesizeSingleton.h " to the project and call the macro " SYNTHESIZE_SINGLETON_FOR_CLASS " in the implementation of the class that you want to make a singleton.
I noticed that working with this macro always triggers a warning, unless you declare the static method " sharedModel " in the interface file.
Our model will also need one single property, text and custom accessors. Here is the " Model.h " file :
And here is the " Model.m ":
When changing text in the text field, the " textChanged " method in the " FlipsideViewController " controller will be called . Here you can assign a new value to the text property of the model.
Be sure to import " Model.h " into " FlipsideViewController.m ".
The main idea is enough to tell when the model will change. This can be done by observing the key / value. Accordingly, you need to configure the observer for a specific property of the object so that he immediately calls the method if it changes. Here we need to find out when the text property for the singleton model class changes. We will use the " initWithNibName " method of the " MainViewController " class for this purpose . Here's what it looks like:
First, we get a link to the " Model " singleton , and then call the " addObserver: forKeyPath: options: context " method . The browser is " self ", the class is " MainViewController". "KeyPath " - the property to be monitored, a text string of characters. These options allow us to specify exactly which data will change. Here is the new value for the property. If desired, you can request the initial, obsolete or previous value, as well as any combination thereof. As a context, you can add zero.
The change monitored by the browser should introduce a special method that will only be called in this case. Here is his signature:
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context
" keyPath " - name of the property as a string, " object " - object that owns this property (in this case, models), " change " - a dictionary that stores the indicated old, new, previous and initial values, " contex t" - any context appropriate to the case (we have zero).
If the goal is to observe several properties of the model at once, you will need a selection operator with " keyPath", which reports exactly which property has changed. For now, we are just looking at the text, therefore we already know it. You can get a new property value by sending a request to change the dictionary parameters. Another option is to directly get to the model text property, but we will use the data below:
Here we ask the object of change its “new” value (which will be all its contents, since this is the option we have given). We assign the result " label.text " - and you 're done.
That's it. We have a model that stores data and reports events when they change. Note: in the Cocoa MVC paradigm, views usually do not directly follow the model. View controllers do this, and they tell views what to do. This approach does not have the status of law, but is generally accepted.
I wanted to figure out another concept - using NSNotification"instead of observing the key / value. I’m not sure if this is really a worthy alternative, and what advantages and disadvantages it may have.
The above example has no practical value - this is just an illustration. In a real application, much could be done another, for example, does not record every editing event, moving to it only after closing the “ Flipside ” view . But the purpose of our lesson was not at all in this, but in the visual implementation of the model (at least ONE of the ways).
Thank you and success!
Source co the lesson you can download it here .
? I practically did not find information about him. In programming lessons, they prefer not to work with the model by typing code directly in the controllers.
Having achieved, as it seemed to me, good results with the implementation, I propose them here for discussion and evaluation. I will explain briefly. I am creating a " Singleton " class that extends " NSObject " for my model. Then, by monitoring the keys / variables, I learn about updates. This is a lot like the “ ModelLocator ” from “ Cairngorm ”, if someone had to work with it in “ Flex ”.
First, create a project with a couple of views. One of them will allow the user to change the value. This value is set for the model, which, in turn, triggers the change in another view. For this purpose the " Utility Application " template is quite suitable . So, we create a project on its basis and assign it the name " MVC ". (In principle, the name can be anything, but for the convenience of working with the lesson, it is better to duplicate it.)
The result should be something like this:

As you can see, we have the " Main View " and " Flipside View " objects , and for both there are controllers and nib- files. Run the project and read the code first. I will concentrate mainly on the points regarding the model, believing that the rest is already clear.
Now add a label and a text box to the views. Label - for " Main View ", field, respectively, for " Flipside View ". First, let's take a look at variables like outlet .
The file " MainViewController.h " should look like the one shown below.
#import
@interface MainViewController : UIViewController {
UILabel *label;
}
@property (nonatomic, retain) IBOutlet UILabel *label;
@end
[/cc]
а файл "MainViewController.m" — так:
[cc lang="objc"]
#import "MainViewController.h"
#import "MainView.h"
@implementation MainViewController
@synthesize label;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Пользовательская инициализация
}
return self;
}
/*
// Внедряем viewDidLoad для дополнительной настройки после загрузки изображения, как правило, из nib-файла.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
/*
// Отменяем, разрешая другие ориентации помимо заданной по умолчанию книжной.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Освобождаем представление, если у него нет superview
// Освобождаем все лишнее, например данные из кэша
}
- (void)dealloc {
[label release];
[super dealloc];
}
@end
* This source code was highlighted with Source Code Highlighter.
In the same way, in the file " FlipsideViewController.h " we add the variable " textField ", as well as " IBAction ", which will let us know about changing the text in the field
#import
@interface FlipsideViewController : UIViewController {
UITextField *textField;
}
@property (nonatomic, retain) IBOutlet UITextField *textField;
- (IBAction)textChanged:(id)sender;
@end
* This source code was highlighted with Source Code Highlighter.
and FlipsideViewController.m:
#import "FlipsideViewController.h"
@implementation FlipsideViewController
@synthesize textField;
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor viewFlipsideBackgroundColor];
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview
// Release anything that's not essential, such as cached data
}
- (IBAction)textChanged:(id)sender
{
}
- (void)dealloc {
[textField release];
[super dealloc];
}
@end
* This source code was highlighted with Source Code Highlighter.
Now you can open " MainView.xib " and add " UILabel " to the view . From the File's Owner object (class " MainViewController "), drag a connector line to the label, linking it to the corresponding variable.


Do the same in the " FlipsideView.xib " file by adding the " UITextField " element and associating it with the " textField " variable for " File's Owner " (" FlipsideViewController "). Attach the IBAction " textChanged " to the " editingChanged " event of the text field.


By this time, it has probably already become clear that our goal is the user to enter some text in the field, which then appears on the label. But these are two separate views with their controllers? How to establish interaction between them? Using the model, of course. After saving both nib files , close the IB editor and return to Xcode .
Model.
It is time to create a model. Add a new file to the project - a subclass for " NSObject ". Name the class " Model ". Not that I was an ardent fan of " Singleton " templates . I am well aware of their shortcomings and believe that sometimes they are used unjustifiably, but as a pragmatist, I am sure that in this case this is an acceptable option.
Those who are intolerable to the idea of singleton can create a model in the " RootViewController " and pass an instance to each of the view controllers. That should work. I’ll choose another way using the ingenious SynthesizeSingleton file from [ CocoaWithLove.com ]. By the way, there you can express your attitude towards singletons.
Now add the file " SynthesizeSingleton.h " to the project and call the macro " SYNTHESIZE_SINGLETON_FOR_CLASS " in the implementation of the class that you want to make a singleton.
I noticed that working with this macro always triggers a warning, unless you declare the static method " sharedModel " in the interface file.
Our model will also need one single property, text and custom accessors. Here is the " Model.h " file :
#import
@interface Model : NSObject {
NSString *text;
}
@property (nonatomic, retain) NSString *text;
+ (Model *)sharedModel;
@end
* This source code was highlighted with Source Code Highlighter.
And here is the " Model.m ":
#import "Model.h"
#import "SynthesizeSingleton.h"
@implementation Model
SYNTHESIZE_SINGLETON_FOR_CLASS(Model);
@synthesize text;
- (id) init
{
self = [super init];
if (self != nil) {
text = @"";
}
return self;
}
- (void) dealloc
{
[text release];
[super dealloc];
}
@end
* This source code was highlighted with Source Code Highlighter.
Setting data for the model.
When changing text in the text field, the " textChanged " method in the " FlipsideViewController " controller will be called . Here you can assign a new value to the text property of the model.
- (IBAction)textChanged:(id)sender
{
Model *model = [Model sharedModel];
model.text = textField.text;
}
* This source code was highlighted with Source Code Highlighter.
Be sure to import " Model.h " into " FlipsideViewController.m ".
Commit changes.
The main idea is enough to tell when the model will change. This can be done by observing the key / value. Accordingly, you need to configure the observer for a specific property of the object so that he immediately calls the method if it changes. Here we need to find out when the text property for the singleton model class changes. We will use the " initWithNibName " method of the " MainViewController " class for this purpose . Here's what it looks like:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) {
// Пользовательская инициализация
Model *model = [Model sharedModel];
[model addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
* This source code was highlighted with Source Code Highlighter.
First, we get a link to the " Model " singleton , and then call the " addObserver: forKeyPath: options: context " method . The browser is " self ", the class is " MainViewController". "KeyPath " - the property to be monitored, a text string of characters. These options allow us to specify exactly which data will change. Here is the new value for the property. If desired, you can request the initial, obsolete or previous value, as well as any combination thereof. As a context, you can add zero.
The change monitored by the browser should introduce a special method that will only be called in this case. Here is his signature:
- (void) observeValueForKeyPath: (NSString *) keyPath ofObject: (id) object change: (NSDictionary *) change context: (void *) context
" keyPath " - name of the property as a string, " object " - object that owns this property (in this case, models), " change " - a dictionary that stores the indicated old, new, previous and initial values, " contex t" - any context appropriate to the case (we have zero).
If the goal is to observe several properties of the model at once, you will need a selection operator with " keyPath", which reports exactly which property has changed. For now, we are just looking at the text, therefore we already know it. You can get a new property value by sending a request to change the dictionary parameters. Another option is to directly get to the model text property, but we will use the data below:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
label.text = [change valueForKey:@"new"];
}
* This source code was highlighted with Source Code Highlighter.
Here we ask the object of change its “new” value (which will be all its contents, since this is the option we have given). We assign the result " label.text " - and you 're done.
That's it. We have a model that stores data and reports events when they change. Note: in the Cocoa MVC paradigm, views usually do not directly follow the model. View controllers do this, and they tell views what to do. This approach does not have the status of law, but is generally accepted.
I wanted to figure out another concept - using NSNotification"instead of observing the key / value. I’m not sure if this is really a worthy alternative, and what advantages and disadvantages it may have.
The above example has no practical value - this is just an illustration. In a real application, much could be done another, for example, does not record every editing event, moving to it only after closing the “ Flipside ” view . But the purpose of our lesson was not at all in this, but in the visual implementation of the model (at least ONE of the ways).
Thank you and success!
Source co the lesson you can download it here .