Your data model consists of the objects that store your app’s information. Data model objects should:
n Represent the data in your app
n Encapsulate the storage of that data
n Avoid assumptions about how the data is displayed or changed
The data of your app is whatever values, information, or concepts your app uses. In your MyStuff app, your data model was simply the names, locations, and images of the things you own. A chess app would have a slightlymore complex data model; there would be an object that represented the chess board, objects for each player, objects for each piece, objects that recorded the moves, and so on. An astronomical catalog app might requiredozens of classes and hundreds of thousands of objects to keep track of the visible stars.
The first job of your data model classes is to represent the data for your app, while hiding (encapsulating) how that data is stored. It should present the rest of your app with a simple interface so the other classes can get theinformation they need, without needing to know exactly how that data is represented or stored.
Even for “simple” apps, like MyStuff, encapsulation is important for the future of your app. For example, the image property of MyWhatsit stored a UIImage object with the picture of that item. Simple, right?
But images can take up a lot of memory, and if your app is going to inventory hundreds, instead of
dozens, of items, your app can’t keep all of those images in memory—it will run out of memory and crash.
You could address this problem by changing your data model so images that you’re not currently displaying—after all, you can’t display them all at once—are written to flash memory as individual image files. The next time anobject requests the image property of a MyWhatsit object, your data model can determine if it has that image in memory or whether it needs to retrieve it from flash storage.
The key concept is that all of these decisions are encapsulated in your data model. The other classes that use your MyWhatsit object just request the image property; they don’t know how, or where, that information is stored, and they shouldn’t care. Review the food truck analogy in the “Encapsulation” section of Chapter 6, if that isn’t clear.
The other really important aspect of the data model is what it is not. The data model is at the bottom of the MVC design and it shouldn’t contain any properties or logic that are not directly related to your app’s data or how thatdata is maintained.
Specifically, it shouldn’t know anything about, or make any assumptions about, the view or controller objects it works with. It shouldn’t contain references to view objects, have methods that present the data in the user interface, or directly handle user actions. In this respect, the data model is the purest of the three MVC roles; it’s all about the data, and nothing else.
View Objects
View objects sit in the middle of the MVC design. A good view object:
n Presents some aspect of the data model to the user
n Understands the data it displays, and how to display it, but nothing more
n May interpret user interface events and send actions to controller objects
A view object’s primary purpose is to display the value(s) in your data model. View objects must, by necessity, understand at least some aspects of your data model, but know nothing about controller objects.
How much does a view object know about the data model? That depends on the complexity of what’s being displayed. In general, it should know just enough to do its job, and no more. A view that displays a string only needs to knowthe string value to display. A view that draws an animated picture of the night sky needs a lot of information: the list of visible stars, their magnitude and color, the coordinates
of the observer, the current time, the azimuth, elevation, the angle of view, and so on. To find examples, you have to look no further than the Cocoa Touch framework, which is full of view objects that display everything from the simpleststring (UILabel) to entire documents (UIWebView).
It’s common for view objects, especially complex ones, to maintain a reference to the data model objects they display. Such a view object not only understands how to display the data, but also knows what data to display.
View objects may also interpret user interface events (like a “swipe” or a “pinch” gesture) and translate those into action messages (-nextPage: or -zoomOut:), which it sends to a controller object. A view object should not act onthose actions; it should simply pass them to a controller.
Controller Objects
Controllers are at the top of the MVC design and are the “business end” of your app. Controller objects are supervisors that oversee, and often coordinate, the data model and view objects. Controller objects:
n Understand, and often create, the data model objects
n Configure, and often create, the view objects
n Perform the actions received from view objects
n Make changes to the data model
n Coordinate communications between the data model and view objects
n May take responsibility for keeping the view objects updated
It’s almost easier to explain what a controller is not, than what it is. It is not your data model; a controller object does not store, manage, or convert your app’s data.1 It is not a view object; it does not draw the interface orinterpret low-level events. It is, essentially, everything else.
Controllers can be involved in the initialization of your data model and view objects, often creating the data model objects and loading your view objects from an Interface Builder file.
Controller objects contain all of the business logic of your app. They perform the commands initiated by the user, respond to high-level events, and instigate changes to the data model. In complex apps, there are often multiplecontroller objects, each responsible for a particular feature or interface.
Your controller objects are also either the recipient or source of most of the messages within your app. How they are involved depends on your design, which brings us to the topic of inter-object communications.
MVC Communications
In its simplest form, the communications between MVC objects forms a loop (see Figure 8-1):
n Data model objects notify view objects of changes
n View objects send actions to controller objects
n Controller objects modify the data model
Figure 8-1. Simple MVC communications
In this arrangement, the data model is responsible for notifying any observers of changes. The view objects are responsible for observing and displaying those changes and sending actions to the controller objects. Thecontroller objects perform the actions, often making changes to the data model, and the whole cycle starts again.
Counter-intuitively, this simplified arrangement only happens in fairly sophisticated apps. Most of the time, the data model is not set up to post notification and the view objects don’t observe changes directly. Instead, thecontroller object steps in and takes responsibility for notifying the view objects when the data model changes, as shown in Figure 8-2.
Figure 8-2. Typical MVC communications
Now that you have the basics of the MVC design pattern, let’s put together another iOS app. Instead of focusing on a particular iOS technology, like the motion events or the camera, I want you to pay attention to the roles ofyour objects, their design, and how they change as your app evolves.
Color Model
You’re going to develop a new app called ColorModel. It’s an app that lets you choose a color using the hue-saturation-brightness color model. Its initial design is simple, as shown in Figure 8-3. The interface consists of threesliders, one for each of the HSB values, and a view where the chosen color appears.
Figure 8-3. Initial design of ColorModel
Start by launching Xcode. Create and configure a new project:
n Use the Single View Application template
n Name the project ColorModel
n Set the class prefix to CM
n Set devices to iPhone
n Create the project
n In the General tab of the ColorModel target, uncheck the Landscape Left and
Landscape Right orientations, so only Portrait orientation is checked
Creating Your Data Model
The first step (after design) of almost any app is to develop your data model. The data model in this app is remarkably simple; it’s a single object that maintains the values for hue, saturation, and brightness. It also translates those values into a color object suitable for display and other uses. Start by adding a new Objective-C source file to your project. Select the ColorModel group (the folder, not the project) in the project navigator and choose theFile ➤ New ➤ File . . . command
(or right/control+click on the group and choose New File . . .). From the iOS category, select the
Objective-C class template, name it CMColor, and make it a subclass of NSObject. You will now have an empty data model class, as shown in Figure 8-4.
Figure 8-4. Empty CMColor class
Create your data model’s public interface by adding the following properties to the @interface
section of the CMColor.h file:
@property (nonatomic) float hue;
@property (nonatomic) float saturation;
@property (nonatomic) float brightness;
@property (readonly,nonatomic) UIColor *color;
The first three properties are floating point values, one each for the color’s hue, saturation, and brightness. The hue is in degrees and can range between 0° and 360°. The other two are expressed as a percentage and can rangebetween 0% and 100%.
The last property is readonly—which just means clients of this object can’t change it. It contains a
UIColor object that represents the color of the current hue/saturation/brightness triplet. The color
property is a synthetic property: a value calculated from the values of the other three properties.
Implement this by replacing the default getter method with your own in CMColor.m:
- (UIColor*)color
{
return [UIColor colorWithHue:self.hue/360 saturation:self.saturation/100 brightness:self.brightness/100
alpha:1];
}
The conversion from the hue-saturation-brightness values into a UIColor object (which uses the
red-green-blue model) is thoughtfully provided by the UIColor class. I’m glad. There are formulas for converting between various color models, but it requires a lot more math than I want to explain.
The values that UIColor uses to express hue, saturation, and brightness are, however, different than the one you choose—OK, I choose—for the data model. In your data model, hue is a value between 0 and 360. UIColor expectsa value between 0 and 1. Likewise, UIColor saturation and brightness values are also between 0 and 1. To convert between our model and the one used by UIColor, you must scale the values by dividing them by their range. This is the kind of detail that data models encapsulate (hide) from the rest of your app.
With your data model complete, it’s time to move on to the view objects.
Creating View Objects
Select your Main.storyboard Interface Builder file. In the objects library, find the plain View object and drag one into your interface. Resize and position it so it occupies the top of the display, inset from the left, top, and rightusing the positioning guides. Using either the resizing handles or the size inspector, set its height to 80 pixels, as shown in Figure 8-5. This will be the view where the user’s chosen color appears.
Figure 8-5. Adding a simple view object
Control/Right-click on the new view object, drag down, release, and choose the Height constraint to fix the height of the view at 80 pixels.
Find the Label object in the library and drag one into your interface. Position it immediately below the lower-left corner of the view object. Set its title to H. Locate the Slider object in the library and drag one into your interface, positioning it just below the color view and immediately to the right of the label you just added, as shown in Figure 8-6.
Figure 8-6. Adding the first label and slider
Select the slider and grab the right-center resizing handle. Resize it so its right edge is aligned with the view. You need two more label/slider pairs, so let’s quickly duplicate the ones you just created. Select both the label andslider views (by holding down the shift key, or by dragging out a selection rectangle that selects both). Now press the option key. While holding down the option key, click
and drag the pair down. The option key turns the drag into a copy operation. Position the pair immediately below the first two, as shown in Figure 8-7, and release the mouse.
Figure 8-7. Duplicating the label and slider
Repeat the copy again so you have three labels and three slider controls. Control/Right-click on the top slider, drag down to the middle slider, release, and choose Equal Widths from the constraints menu. Repeat, dragging tothe bottom slider, as shown in
Figure 8-8. This adds constraints to keep the three slider controls the same width.
Figure 8-8. Constraining the widths of the sliders
Retitle the second and third labels to S and B. You now have all of the view objects you need. Flesh out the constraints by choosing Add Missing Constraints in View Controller from the Resolve Auto Layout Issues control.
In your data mode, the hue value ranges from 0° to 360° and saturation and brightness range from 0% to 100%. Change the value range of the three sliders to match. Select the top (hue) slider and use the attributes inspectorto change its Maximum value from 1 to 360, as shown in Figure 8-9.Change the maximum value of the other two sliders to 100.
Figure 8-9. Establishing value range of slider control
Writing Your Controller
The Xcode project template already provides you with a controller class; you just need to fill it out. Select your CMViewController.h interface file. Your controller will need a reference to your data model object, along with outlets andactions to connect with your interface. Start by adding an #import statement above the @interface directive so your controller knows about your CMColor class:
#import "CMColor.h"
Inside the @interface, add two properties:
@property (strong,nonatomic) CMColor *colorModel;
@property (weak,nonatomic) IBOutlet UIView *colorView;
The first is your controller’s connection with your data model. The second is an outlet that you’ll connect to your color view. This will let your controller update the color displayed in the view.
Finally, your controller will need three actions, one for each slider control, that will adjust one value in the data model:
- (IBAction)changeHue:(UISlider*)sender;
- (IBAction)changeSaturation:(UISlider*)sender;
- (IBAction)changeBrightness:(UISlider*)sender;
Switch to the CMViewController.m implementation file, and add these three methods:
- (IBAction)changeHue:(UISlider*)sender
{
self.colorModel.hue = sender.value; self.colorView.backgroundColor = self.colorModel.color;
}
- (IBAction)changeSaturation:(UISlider*)sender
{
self.colorModel.saturation = sender.value; self.colorView.backgroundColor = self.colorModel.color;
}
- (IBAction)changeBrightness:(UISlider*)sender
{
self.colorModel.brightness = sender.value; self.colorView.backgroundColor = self.colorModel.color;
}
Each action message will be received from one of the slider controls whenever it changes. Each method simply modifies the corresponding value in the data model with the new value of the slider.
It then updates the color view to reflect the new color in the data model. In this implementation, your controller is taking responsibility for updating the view whenever the data model changes
(see Figure 8-2).
The last detail is to create the data model when the controller is loaded. Find the -viewDidLoad
method and add the one bold line:
- (void)viewDidLoad
{
[super viewDidLoad];
self.colorModel = [CMColor new];
}









No comments:
Post a Comment