1. Trang chủ
  2. » Công Nghệ Thông Tin

beginning iphone 3 development exploring the iphone sdk phần 2 pptx

58 1,1K 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 58
Dung lượng 3,31 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

The MVC model divides up all functionality into three distinct categories: ■ Model: The classes that hold your application’s data ■ View: Made up of the windows, controls, and other el

Trang 1

all object-oriented frameworks pay a certain amount of homage to MVC, but few are as true

to the MVC model as Cocoa Touch

The MVC model divides up all functionality into three distinct categories:

Model: The classes that hold your application’s data

View: Made up of the windows, controls, and other elements that the user can see

and interact with

Controller: Binds the model and view together and is the application logic that

decides how to handle the user’s inputs The goal in MVC is to make the objects that implement these three types of code as dis-

tinct from one another as possible Any object you write should be readily identifiable as

belonging in one of the three categories, with little or no functionality within it that could be

classified within either of the other two An object that implements a button, for example,

shouldn’t contain code to process data when that button is tapped, and code that

imple-ments a bank account shouldn’t contain code to draw a table to display its transactions

MVC helps ensure maximum reusability A class that implements a generic button can be

used in any application A class that implements a button that does some particular

calcula-tion when it is clicked can be used only in the applicacalcula-tion for which it was originally written

When you write Cocoa Touch applications, you will primarily create your view components

using Interface Builder, although you will sometimes also modify your interface from code,

or you might subclass existing views and controls

Your model will be created by crafting Objective-C classes designed to hold your

applica-tion’s data or by building a data model using Core Data, which you’ll learn about in Chapter

11 We won’t be creating any model objects in this chapter’s application because we have no

need to store or preserve data, but we will introduce model objects as our applications get

more complex in future chapters

Your controller component will typically be composed of classes that you create and that are

specific to your application Controllers can be completely custom classes (NSObject

sub-classes), but more often, they will be subclasses of one of several existing generic controller

classes from the UIKit framework such as UIViewController, which you’ll see in a moment

By subclassing one of these existing classes, you will get a lot of functionality for free and

won’t have to spend time recoding the wheel, so to speak

As we get deeper into Cocoa Touch, you will quickly start to see how the classes of the UIKit

framework follow the principles of MVC If you keep this concept in the back of your head as

Trang 2

Creating Our Project

It’s time to create our Xcode project We’re going to use the same template that we used in

the previous chapter: View-based Application We’ll start using some of the other templates

before too long, but by starting with the simple template again, it’ll be easier for you to see

how the view and controller objects work together in an iPhone application Go ahead and

create your project, saving it under the name Button Fun If you have any trouble creating

your project, refer to the preceding chapter for the proper steps

You probably remember that the project template created some classes for us You’ll find

those same classes in your new project, although the names will be a little different because

some class names are based on the project name

Creating the View Controller

A little later in this chapter, we’re going to design a view (or user interface) for our

applica-tion using Interface Builder, just as we did in the previous chapter Before we do that, we’re

going to look at and make some changes to the source code files that were created for us

Yes, Virginia, we’re actually going to write some code in this chapter

Before we make any changes, let’s look at the files that

were created for us In the project window, expand

the Classes folder to reveal the four files within (see

Figure 3-2)

These four files implement two classes, each of which

contains a m and h file The application we are

creat-ing in this chapter has only one view, and the controller

class that is responsible for managing that one view is

called Button_FunViewController The Button_Fun

part of the name comes from our project name, and

the ViewController part of the name means this class is, well, a view controller Click

Button_FunViewController.h in the Groups & Files pane, and take a look at the contents of

Figure 3-2 The class files that

were created for us by the project template

Trang 3

Not much to it, is there? This is a subclass of UIViewController, which is one of those

generic controller classes we mentioned earlier It is part of the UIKit and gives us a bunch

of functionality for free Xcode doesn’t know what our application-specific functionality is

going to be, but it does know we’re going to have some, so it has created this class to hold

that functionality

Take a look back at Figure 3-1 Our program consists of two buttons and a text label that

reflects which button was tapped We’ll create all three of these elements in Interface

Builder Since we’re also going to be writing code, there must be some way for our code to

interact with the elements we create in Interface Builder, right?

Absolutely right Our controller class can refer to objects in the nib by using a special kind

of instance variable called an outlet Think of an outlet as a pointer that points to an object

within the nib For example, suppose you created a text label in Interface Builder and wanted

to change the label’s text from within your code By declaring an outlet and connecting that

outlet to the label object, you could use the outlet from within your code to change the text

displayed by the label You’ll see how to do just that in a bit

Going in the opposite direction, interface objects in our nib file can be set up to trigger

spe-cial methods in our controller class These spespe-cial methods are known as action methods

For example, you can tell Interface Builder that when the user touches up (pulls a finger off

the screen) within a button, a specific action method within your code should be called

As we’ve already said, Button Fun will feature two buttons and a label

In our code, we’ll create an outlet that points to the label, and this outlet will allow us to

change the text of that label We’ll also create a method named buttonPressed: that will

fire whenever one of the two buttons is tapped buttonPressed: will set the label’s text to

let the user know which button was tapped

We’ll use Interface Builder to create the buttons and label, and then we’ll do some

clicking and dragging to connect the label to our label outlet and our buttons to our

buttonPressed: action

But before we get to our code, here’s a bit more detail on outlets and actions

Outlets

Outlets are instance variables that are declared using the keyword IBOutlet A declaration

of an outlet in your controller’s header file might look like this:

@property (nonatomic, retain) IBOutlet UIButton *myButton;

Trang 4

The IBOutlet keyword is defined like this:

#ifndef IBOutlet

#define IBOutlet

#endif

Confused? IBOutlet does absolutely nothing as far as the compiler is concerned Its sole

purpose is to act as a hint to tell Interface Builder that this is an instance variable that we’re

going to connect to an object in a nib Any instance variable that you create and want to

connect to an object in a nib file must be preceded by the IBOutlet keyword When you

open Interface Builder, it will scan your project header files for occurrences of this keyword

and will allow you to make connections from your code to the nib based on these (and only

these) variables In a few minutes, you’ll see how to actually make the connection between

an outlet and a user interface object in Interface Builder

Outlet changes

In the first version of the book, we placed the IBOutlet keyword before the instance variable declaration,

like this:

IBOutlet UIButton *myButton;

Since that time, Apple’s sample code has been moving toward placing the IBOutlet keyword in the

prop-erty declaration, like this:

@property (nonatomic, retain) IBOutlet UIButton *myButton;

Both mechanisms are supported, and for the most part, there is no difference in the way things work based

on where you put the keyword There is one exception to that, however If you declare a property with a

different name than its underlying instance variable (which can be done in the @synthesize directive),

then you have to put the IBOutlet keyword in the property declaration, and not before the instance

vari-able declaration, in order for it to work correctly If you are a bit fuzzy on the property concept, we’ll talk you

through it in just a bit.

Although both approaches work, we’ve followed Apple’s lead and have moved the IBOutlet keyword to

the property declaration in all of our code.

You can read more about the new Objective-C properties in the second edition of Learn Objective-C on the Mac,

by Mark Dalrymple and Scott Knaster (Apress 2008), and in The Objective-C 2.0 Programming Language

avail-able from Apple’s developer web site:

http://developer.apple.com/documentation/Cocoa/Conceptual/ObjectiveC/

ObjC.pdf

Trang 5

Actions are methods that are part of your controller class They are also declared with a

spe-cial keyword, IBAction, which tells Interface Builder that this method is an action and can

be triggered by a control Typically, the declaration for an action method will look like this:

- (IBAction)doSomething:(id)sender;

The actual name of the method can be anything you want, but it must have a return type of

IBAction, which is the same as declaring a return type of void This is another way of saying

that action methods do not return a value Usually, the action method will take one

argu-ment, and it’s typically defined as id and given a name of sender The control that triggers

your action will use the sender argument to pass a reference to itself So, for example, if your

action method was called as the result of a button tap, the argument sender would contain

a reference to the specific button that was tapped

As you’ll see in a bit, our program will use that sender argument to set the label to the text

“left” or “right,” depending on which button was tapped If you don’t need to know which

control called your method, you can also define action methods without a sender

param-eter This would look like so:

- (IBAction)doSomething;

It won’t hurt anything if you declare an action method with a sender argument and then

ignore sender You will likely see a lot of sample code that does just that, because

histori-cally action methods in Cocoa had to accept sender whether they used it or not

Adding Actions and Outlets to the View Controller

Now that you know what outlets and actions are, let’s go ahead and add one of each to

our controller class We need an outlet so we can change the label’s text Since we won’t be

changing the buttons, we don’t need an outlet for them

We’ll also declare a single action method that will be called by both buttons While many

action methods are specific to a single control, it’s possible to use a single action to handle

input from multiple controls, which is what we’re going to do here Our action will grab the

button’s name from its sender argument and use the label outlet to embed that button

name in the label’s text You’ll see how this is done in a moment

Trang 6

Because Xcode creates files for us to use that already contain some of the code we need, we will

often be inserting code into an existing file When you see code listings like the one for Button_

FunViewController.h, any code that is in a normal typeface is existing code that should already

be in the file Code that is listed in bold is new code that you need to type.

Go ahead and add the following code to Button_FunViewController.h:

If you have worked with Objective-C 2.0, you’re probably familiar with the @property

declaration, but if you aren’t, that line of code might look a little intimidating Fear not:

Objective-C properties are really quite simple Let’s take a quick detour to talk about them,

since they are relatively new and we will use them extensively in this book Even if you are

already a master of the property, please do read on, because there is a bit of Cocoa Touch–

specific information that you’ll definitely find useful

Objective-c Properties

Before the property was added to Objective-C, programmers traditionally defined pairs of

methods to set and retrieve the values for each of a class’s instance variables These methods

are called accessors and mutators (or, if you prefer, getters and setters) and might look

something like this:

Trang 7

Although this approach is still perfectly valid, the @property declaration allows you to say

goodbye to the tedious process of creating accessor and mutator methods, if you want The

@property declarations we just typed, combined with another declaration in the

imple-mentation file (@synthesize), which you’ll see in a moment, will tell the compiler to create

the getter and setter methods at compile time You do still have to declare the underlying

instance variables as we did here, but you do not need to define the accessor or mutator

In our declaration, the @property keyword is followed by some optional attributes, wrapped

in parentheses These further define how the accessors and mutators will be created by the

compiler The two you see here will be used often when defining properties in iPhone

appli-cations:

@property (nonatomic, retain) UILabel *statusText;

The first of these attributes, retain, tells the compiler to send a retain message to any object

that we assign to this property This will keep the instance variable underlying our property

from being flushed from memory while we’re still using it This is necessary because the

default behavior (assign) is intended for use with garbage collection, a feature of

Objective-C 2.0 that isn’t currently available on iPhone As a result, if you define a property that is an

object (as opposed to a raw datatype like int), you should generally specify retain in the

optional attributes When declaring a property for an int, float, or other raw datatype, you

do not need to specify any optional attributes

The second of our optional attributes, nonatomic, changes the way that the accessor and

mutator methods are generated Without getting too technical, let’s just say that, by default,

these methods are created with some additional code that is helpful when writing

multi-threaded programs That additional overhead, though small, is unnecessary when declaring

a pointer to a user interface object, so we declare nonatomic to save a bit of overhead There

will be times where you don’t want to specify nonatomic for a property, but as a general

rule, most of the time you will specify nonatomic when writing iPhone applications

Objective-C 2.0 has another nice feature that we’ll be using along with properties It

intro-duced the use of dot notation to the language Traditionally, to use an accessor method,

you would send a message to the object, like this:

myVar = [someObject foo];

This approach still works just fine But when you’ve defined a property, you also have the

option of using dot notation, similar to that used in Java, C++, and C#, like so:

myVar = someObject.foo;

Trang 8

someObject.foo = myVar;

is functionally identical to the following:

[someObject setFoo:myVar];

Declaring the action Method

After the property declaration, we added another line of code:

- (IBAction)buttonPressed:(id)sender;

This is our action method declaration By placing this declaration here, we are informing

other classes, and Interface Builder, that our class has an action method called

button-Pressed:

Adding Actions and Outlets to the Implementation File

We are done with our controller class header file for the time being, so save it and

single-click the class’s implementation file, Button_FunViewController.m The file should look like

this:

#import "Button_FunViewController.h"

@implementation Button_FunViewController

/*

// The designated initializer Override to perform setup

// that is required before the view is loaded.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:

// Implement loadView to create a view hierarchy programmatically,

// without using a nib.

- (void)loadView {

}

*/

/*

Trang 9

// typically from a nib.

// Return YES for supported orientations

return (interfaceOrientation == UIInterfaceOrientationPortrait);

// Release any retained subviews of the main view.

// e.g self.myOutlet = nil;

Apple has anticipated some of the methods that we are likely to override and has included

method stubs in the implementation file Some of them are commented out and can be

either uncommented or deleted as appropriate The ones that aren’t commented out are

either used by the template or are so commonly used that they were included to save us

time We won’t need any of the commented-out methods for this application, so go ahead

and delete them, which will shorten up the code and make it easier to follow as we insert

new code into this file

Once you’ve deleted the commented-out methods, add the following code When you’re

Trang 10

#import "Button_FunViewController.h"

@implementation Button_FunViewController

@synthesize statusText;

- (IBAction)buttonPressed:(id)sender {

NSString *title = [sender titleForState:UIControlStateNormal];

NSString *newText = [[NSString alloc] initWithFormat:

@"%@ button pressed.", title];

statusText.text = newText;

[newText release];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning]; // Releases the view if it

// doesn't have a superview

// Release anything that's not essential, such as cached data

}

- (void)viewDidUnload {

// Release any retained subviews of the main view.

// e.g self.myOutlet = nil;

This is how we tell the compiler to automatically create the accessor and mutator

meth-ods for us By virtue of this line of code, there are now two “invisible” methmeth-ods in our class:

statusText and setStatusText: We didn’t write them, but they are there nonetheless,

waiting for us to use them

The next bit of newly added code is the implementation of our action method that will get

called when either button is tapped:

-(IBAction)buttonPressed: (id)sender {

NSString *title = [sender titleForState:UIControlStateNormal];

NSString *newText = [[NSString alloc] initWithFormat:

@"%@ button pressed.", title];

Trang 11

statusText.text = newText;

[newText release];

}

Remember that the parameter passed into an action method is the control or object that

invoked it So, in our application, sender will always point to the button that was tapped

This is a very handy mechanism, because it allows us to have one action method handle the

input from multiple controls, which is exactly what we’re doing here: both buttons call this

method, and we tell them apart by looking at sender The first line of code in this method

grabs the tapped button’s title from sender

NSString *title = [sender titleForState:UIControlStateNormal];

nOte

We had to provide a control state when we requested the button’s title The four possible states are

normal, which represents the control when it’s active but not currently being used; highlighted, which

represents the control when it is in the process of being tapped or otherwise used; disabled, which is the

state of a button that is not enabled and can’t be used; and selected, which is a state that only certain

controls have and which indicates that the control is currently selected UIControlStateNormal

represents a control’s normal state and is the one you will use the vast majority of the time If values for

the other states are not specified, those states will have the same value as the normal state.

The next thing we do is create a new string based on that title:

NSString *newText = [[NSString alloc] initWithFormat:

@"%@ button pressed.", title];

This new string will append the text “button pressed.” to the name of the button So if we

tapped a button with a title of “Left,” this new string would equal “Left button pressed.”

Finally, we set the text of our label to this new string:

statusText.text = newText;

We’re using dot notation here to set the label’s text, but we could have also used

[statusText setText:newText]; instead Finally, we release the string:

[newText release];

The importance of releasing objects when you’re done with them cannot be overstated

Trang 12

NSString *newText = [NSString stringWithFormat:

@"%@ button pressed.", title];

This code would work exactly the same as the code we used Class methods like this one are

called convenience or factory methods, and they return an autoreleased object

Follow-ing the general memory rule that “if you didn’t allocate it or retain it, don’t release it,” these

autoreleased objects don’t have to be released unless you specifically retain them, and using

them often results in code that’s a little shorter and more readable

But, there is a cost associated with these convenience methods because they use the

autore-lease pool The memory allocated for an autoreautore-leased object will stay allocated for some

period of time after we’re done with it On Mac OS X, with swap files and relatively large

amounts of physical memory, the cost of using autoreleased objects is nominal, but on

iPhone, these objects can have a detrimental effect on your application’s memory footprint

It is OK to use autorelease, but try to use it only when you really need to, not just to save

typ-ing a line or two of code

Next, we added a single line of code to the existing viewDidUnload: method:

self.statusText = nil;

Don’t worry too much about this line of code for now; we’ll explain why this line of code is

needed in the next chapter For now, just remember that you need to set any outlets your

class has to nil in viewDidUnload

tiP

If you’re a bit fuzzy on objective-C memory management, you really should review the memory

management “contract” at http://developer.apple.com/documentation/Cocoa/

Conceptual/MemoryMgmt/Articles/mmRules.html Even a small number of memory leaks

can wreak havoc in an iPhone application.

The last thing we did was to release the outlet in our dealloc method:

[statusText release];

Releasing this item might seem strange You might be thinking, since we didn’t instantiate

it, we shouldn’t be responsible for releasing it If you have worked with older versions of

Cocoa and Objective-C, you’re probably thinking this is just plain wrong However, because

we implemented properties for each of these outlets and specified retain in that property’s

attributes, releasing it is correct and necessary Interface Builder will use our generated

mutator method when assigning the outlets, and that mutator will retain the object that is

Trang 13

Before moving on, make sure you’ve saved this file, and then go ahead and build the project

by pressing ⌘ B to make sure you didn’t make any mistakes while typing If it doesn’t

com-pile, go back and compare your code to the code in this book

This one line of code will function exactly the same as the four lines of code that make up our

button-Pressed: method For sake of clarity, we won’t generally nest Objective-C messages in the code examples

in this book, with the exception of calls to alloc and init, which, by longstanding convention, are almost

always nested.

Using the Application Delegate

The other two files under the Classes folder implement our application delegate Cocoa

Touch makes extensive use of delegates, which are classes that take responsibility for doing

certain things on behalf of another object The application delegate lets us do things at

cer-tain predefined times on behalf of the UIApplication class Every iPhone application has

one and only one instance of UIApplication, which is responsible for the application’s run

loop and handles application-level functionality such as routing input to the appropriate

controller class

UIApplication is a standard part of the UIKit, and it does its job mostly behind the scenes,

so you don’t have to worry about it for the most part At certain well-defined times during an

application’s execution, however, UIApplication will call specific delegate methods, if there

is a delegate and if it implements that method For example, if you have code that needs to

fire just before your program quits, you would implement the method

applicationWill-Terminate: in your application delegate and put your termination code there This type of

delegation allows our application to implement common application-wide behavior

with-out having to subclass UIApplication or, indeed, to even know anything about its inner

workings

Click Button_FunAppDelegate.h in the Groups & Files pane, and look at the application

del-egate’s header file It should look like this:

Trang 14

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain) IBOutlet Button_FunViewController

*viewController;

@end

We don’t need to make any changes to this file, and after implementing our controller class,

most everything here should look familiar to you One thing worth pointing out is this line of

code:

@interface Button_FunAppDelegate : NSObject <UIApplicationDelegate> {

Do you see that value between the angle brackets? This indicates that this class conforms

to a protocol called UIApplicationDelegate Hold down the option key, and move your

cursor so that it is over the word UIApplicationDelegate Your cursor should turn into

crosshairs; when it does, double-click This will open the documentation browser and show

you the documentation for the UIApplicationDelegate protocol (see Figure 3-3) This

same trick works with class, protocol, and category names, as well as method names

dis-played in the editor pane Just option–double-click a word, and it will search for that word in

the documentation browser

Knowing how to quickly look up things in the documentation is definitely worthwhile, but

looking at the definition of this protocol is perhaps more important Here’s where you’ll find

what methods the application delegate can implement and when those methods will get

called It’s probably worth your time to read over the descriptions of these methods

nOte

If you’ve worked with Objective-C before but not with Objective-C 2.0, you should be aware that protocols

can now specify optional methods UIApplicationDelegate contains many optional methods, and

you do not need to implement any of the optional methods in your application delegate unless you have a

reason.

Trang 15

Figure 3-3 Looking at the UIApplicationDelegate documentation using the documentation browser

Click Button_FunAppDelegate.m, and look at the implementation of the application delegate

It should look like this:

Trang 16

// Override point for customization after app launch

Right in the middle of the file, you can see that our application delegate has implemented

one of the protocol’s methods: applicationDidFinishLaunching:, which, as you can

probably guess, fires as soon as the application has finished all the setup work and is ready

to start interacting with the user

Our delegate version of applicationDidFinishLaunching: adds our view controller’s view

as a subview to the application’s main window and makes the window visible, which is how

the view we are going to design gets shown to the user You don’t need to do anything to

make this happen; it’s all part of the code generated by the template we used to build this

project

We just wanted to give you a bit of background on application delegates and see how this

all ties together

Editing MainWindow.xib

So far, we’ve looked at the four files in our project’s Classes tab (two .m files, two .h files)

In previous chapters, we’ve had experience with two of the three files in our project’s

Resources tab We looked at the equivalent of Button_Fun-Info.plist when we added our

icon to the project, and we looked at the equivalent of Button_FunViewController.xib

when we added our “Hello, World!” label

There’s one other file in the Resources tab that we want to talk about The file MainWindow.

xib is what causes your application’s delegate, main window, and view controller instances

to get created at runtime Remember, this file is provided as part of the project template

You don’t need to change or do anything here This is just a chance to see what’s going on

behind the scenes, to get a glimpse of the big picture

Trang 17

Expand the Resources folder in Xcode’s Groups

& Files pane, and double-click MainWindow.xib

Once Interface Builder opens, take a look at the

nib’s main window—the one labeled

MainWin-dow.xib, which should look like Figure 3-4.

You should recognize the first two icons in this

window from Chapter 2 As a reminder, every icon

in a nib window after the first two represents an

object that will get instantiated when the nib file

loads Let’s take a look at the third, fourth, and

fifth icons

nOte

Long names get truncated in the nib file’s main window in the default view, as you can see in Figure 3-4

If you hold your cursor over one of these icons for a few seconds, a tooltip will pop up to show you the full

name of the item Note also that the names shown in the main window do not necessarily indicate the

underlying class of the object The default name for a new instance usually will clue you in to the

underly-ing class, but these names can be, and often are, changed.

The third icon is an instance of Button_FunAppDelegate The fourth icon is an instance of

Button_FunViewController And, finally, the fifth icon is our application’s one and only

window (an instance of UIWindow) These three icons indicate that once the nib file is loaded,

our application will have one instance of the application delegate, Button_FunAppDelegate;

one instance of our view controller, Button_FunViewController; and one instance of

UIWindow (the class that represents the application’s one and only window) As you can see,

Interface Builder can do much more than just create interface elements It allows you to

cre-ate instances of other classes as well This is an incredibly powerful feature Every line of code

that you don’t write is a line of code you don’t have to debug or maintain Right here, we’re

creating three object instances at launch time without having to write a single line of code

OK, that’s all there is to see here, folks; move along Be sure to close this nib file on the way

out And if you are prompted to save, just say “no,” because you shouldn’t have changed

anything

Figure 3-4 Our application’s

MainWindow.xib as it appears in Interface Builder

Trang 18

Editing Button_FunViewController.xib

Now that you have a handle on the files that make up our project and the concepts that

bring them all together, let’s turn our attention to Interface Builder and the process of

con-structing our interface

Creating the View in Interface Builder

In Xcode, double-click Button_FunViewController.xib in the Groups & Files pane The nib file

should open in Interface Builder Make sure the library is visible If it’s not, you can show it

by selecting library from the tools menu You also need to make sure that the nib’s View

window is open If it’s not, double-click the icon called View in the nib’s main window (see

Figure 3-5)

Figure 3-5 Button_FunViewController.xib open in Interface Builder

Trang 19

Now we’re ready to design our interface Drag a label from the library over to the view

win-dow, just as you did in the previous chapter Place the label toward the bottom of the view,

so the label lines up with the left and bottom blue guidelines (see Figure 3-6) Next, expand

the label so the right side lines up with the guideline on the right side of the window

Figure 3-6 Using the blue guidelines to place objects

nOte

The little blue guidelines are there to help you stick to the Apple Human Interface Guidelines (usually

referred to as “the HIG”) Yep, just like it does for Mac OS X, Apple provides the iPhone Human Interface

Guidelines for designing iPhone applications The HIG tells you how you should—and shouldn’t—design

your user interface You really should read it, because it contains valuable information that every iPhone

developer needs to know You’ll find it at http://developer.apple.com/iphone/library/

Trang 20

After you’ve placed the label at the bottom of the view, click it to select it, and press ⌘ 1 to bring up the

inspector Change the text alignment to centered by using the text alignment buttons on the inspector (see Figure 3-7)

Now, double-click the label, and delete the existing text We don’t want any text to display until a button has been tapped

Next, we’re going to drag two Round Rect Buttons from

the library (see Figure 3-8) to our view

Figure 3-8 The Round Rect Button

as it appears in the library

Place the two buttons next to each other, roughly in the middle of the view The exact placement doesn’t matter Double-click the button that you placed on the left Doing this will allow the button’s title to

be edited, so go ahead and change its text to read

“Left.” Next, double-click the button on the right, and change its text to read “Right.” When you’re done, your view should look something like the one shown in Figure 3-9

Figure 3-7 The inspector’s text

alignment buttons

Trang 21

Figure 3-9 The finished view

Connecting Everything

We now have all the pieces of our interface All that’s left is to make the various connections

that will allow these pieces to work together

The first step is to make a connection from File’s Owner to the label in the View window Why

File’s Owner?

When an instance of UIViewController or one of its subclasses is instantiated, it can be

told to initialize itself from a nib In the template we’ve used, the

Button_FunViewCon-troller class will be loaded from the nib file Button_FunViewController.xib We don’t have to

do anything to make that happen; it’s part of the project template we chose In future

chap-ters, you’ll see exactly how that process works Since the MainWindow.xib file contains an

icon that represents Button_FunViewController, an instance of Button_FunViewController

will get created automagically when our application launches When that happens, that

instance will automatically load Button_FunViewController.xib into memory and become its

file’s owner

Earlier in the chapter, we added an outlet to Button_FunViewController, which is this nib’s

owner We can now make a connection between that outlet and the label using the File’s

Trang 22

It’s OK if you don’t fully understand the nib loading process yet It’s complicated, and we’ll be talking

about it and seeing it in action in several of the later chapters For now, just remember that your

control-ler class is the file’s owner for the nib file of the same name.

connecting Outlets

Hold down the control key; click the File’s Owner icon in the main nib window; and keep the

mouse button down Drag away from the File’s Owner icon toward the View window A blue

guideline should appear Keep dragging until your cursor is over the label in the View

win-dow Even though you won’t be able to see the label, it will magically appear once you are

over it (see Figure 3-10)

Figure 3-10 Control-dragging to connect outlets

Trang 23

With the cursor still over the label, let go of the mouse button, and a small gray menu like

the one shown in Figure 3-11 should pop up

Figure 3-11 Outlet selection menu

Select statusText from the gray menu.

By control-dragging from File’s Owner to an interface object, you are telling Interface Builder

that you want to connect one of the File’s Owner’s outlets to this object when the nib file

is loaded In this case, the file’s owner is the class Button_FunViewController, and the

Button_FunViewController outlet we are interested in is statusText When we

control-dragged from File’s Owner to the label object and selected statusText from the pop-up

menu that appeared, we told Interface Builder to have Button_FunViewController’s

sta-tusText outlet point to the label, so any time we refer to statusText in our code, we will be

dealing with this label Cool, eh?

specifying actions

The only thing left to do is to identify which actions these buttons trigger and under what

circumstances they trigger them If you’re familiar with Cocoa programming for Mac OS X,

Trang 24

iPhone is different from Mac OS X, and here’s one of the places where that difference

becomes apparent On the Mac, a control can be associated with just one action, and that

action is typically triggered when that control is used There are some exceptions to this, but

by and large, a control triggers its corresponding action method when the mouse button is

released if the cursor is still inside the bounds of that control

Controls in Cocoa Touch offer a lot more possibilities,

so instead of click-dragging from the control, it’s best

to get in the habit of using the connections inspector,

which we can get to by pressing ⌘ 2 or selecting

con-nection inspector from the tools menu Click the Left

button, and then bring up the connections inspector

It should look like Figure 3-12

Under the heading Events, you’ll see a whole list of

events that can potentially trigger an action If you

like, you can associate different actions with different

events For example, you might use Touch Up Inside

to trigger one action, while Touch Drag Inside triggers

a different action Our situation is relatively simple

and straightforward When the user taps our button,

we want it to call our buttonPressed: method The

first question is which of the events in Figure 3-12 do

we use?

The answer, which may not be obvious at first, is Touch Up Inside When the user’s finger

lifts up from the screen, if the last place it touched before lifting was inside the button, the

user triggers a touch up inside Think about what happens in most of your iPhone

applica-tions if you touch the screen and change your mind You move your finger off the button

before lifting up, right? We should give our users the same ability If our user’s finger is still

on the button when it’s lifted off the screen, then we can safely assume that the button tap

is intended

Now that we know the event we want to trigger our action, how do we associate the event

with a specific action method?

See that little circle in the inspector to the right of Touch Up Inside? Click in that circle and

drag away with the mouse button still pressed; there’s no need to hold down the control

key this time You should get a gray connection line, just as you did when we were

connect-ing outlets earlier Drag this line over to the File’s Owner icon, and when the little gray menu

pops up, select buttonPressed: Remember, the File’s Owner icon represents the class whose

nib we are editing In this case, File’s Owner represents our application’s sole instance of

Figure 3-12 The connections

inspector showing our button’s available events

Trang 25

Owner icon, we are telling Interface Builder to call the selected method when the

specified event occurs So when the user touches up inside the button, the Button_

FunViewController class’s buttonPressed: method will be called

Do this same sequence with the other button and then save Now, any time the user taps

one of these buttons, our buttonPressed: method will get called

Trying It Out

Save the nib file; then head back to Xcode and take your application for a spin Select Build

and Run from the Build menu Your code should compile, and your application should come

up in the iPhone Simulator When you tap the left button, the text “Left button pressed.”

should appear, as it does in Figure 3-1 If you then tap the right button, the label will change

to say “Right button pressed.”

Bring It on Home

This chapter’s simple application introduced you to MVC, creating and connecting outlets

and actions, implementing view controllers, and using application delegates You learned

how to trigger action methods when a button is tapped and saw how to change the text of

a label at runtime Although a simple application, the basic concepts we used to build it are

the same concepts that underlie the use of all controls on the iPhone, not just buttons In

fact, the way we used buttons and labels in this chapter is pretty much the way that we will

implement and interact with most of the standard controls on the iPhone

It’s very important that you understand everything we did in this chapter and why we did

it If you don’t, go back and redo the parts that you don’t fully understand This is important

stuff! If you don’t make sure you understand everything now, you will only get more

con-fused as we get into creating more complex interfaces later on in this book

In the next chapter, we’ll take a look at some of the other standard iPhone controls You’ll

also learn how to use alerts to notify the user of important happenings and how to indicate

that the user needs to make a choice before proceeding by using action sheets When you

feel you’re ready to proceed, give yourself a pat on the back for being such an awesome

student, and head on over to the next chapter

Trang 26

Chapter 4

i

More User

Interface Fun

n Chapter 3, we discussed the Model-View-Controller concept and built an

application that brought that idea to life You learned about outlets and

actions and used them to tie a button control to a text label In this chapter,

we’re going to build an application that will take your knowledge of controls

to a whole new level

We’ll implement an image view, a slider, two different text fields, a segmented

control, a couple of switches, and an iPhone button that looks more like, well,

an iPhone button You’ll learn how to use the view hierarchy to group multiple

items under a common parent view and make manipulating the interface at

runtime easier You’ll see how to set and retrieve the values of various

con-trols, both by using outlets and by using the sender argument of our action

methods After that, we’ll look at using action sheets to force the user to make

a choice and alerts to give the user important feedback We’ll also learn about

control states and the use of stretchable images to make buttons look the way

they should

Because this chapter’s application uses so many different user interface items,

we’re going to work a little differently than we did in the previous two

chap-ters We’re going to break our application into pieces, implementing one piece

at a time and bouncing back and forth between Xcode, Interface Builder, and

the iPhone simulator and testing each piece before we move on to the next

Breaking the process of building a complex interface into smaller chunks will

make it much less intimidating and will make it more closely resemble the

actual process you’ll go through when building your own applications This

code-compile-debug cycle makes up a large part of a software developer’s

typical day

Trang 27

A Screen Full of Controls

As we mentioned, the application we’re going to build in

this chapter is a bit more complex than was the case in

Chapter 3 We’re still going to use only a single view and

controller, but as you can see in Figure 4-1, there’s quite a bit

more going on in this one view

The logo at the top of the iPhone screen is an image view,

and in this application, it does nothing more than display a

static image Below the logo, there are two text fields, one

that allows the entry of alphanumeric text and one that

allows only numbers Below the text fields is a slider As the

user changes the slider, the value of the label next to it will

change so that it always reflects the slider’s value

Below the slider is a segmented control and two switches

The segmented control will toggle between two different

types of controls in the space below it When the

applica-tion first launches, there will be two switches below the

segmented control Changing the value of either switch will

cause the other one to change its value to match Now, this

isn’t something you would likely do in a real application,

but it will let us show you how to change the value of a control programmatically and how

Cocoa Touch animates certain actions for you without you having to do any work

Figure 4-2 shows what happens when the user taps the segmented control The switches

disappear and are replaced by a button

When the Do Something button is pressed, an action sheet will pop up and ask the user if

they really meant to tap the button (see Figure 4-3) This is the standard way of responding

to input that is potentially dangerous or that could have significant repercussions and gives

the user a chance to stop potential badness from happening

If Yes, I’m Sure! is selected, the application will put up an alert, letting the user know that

everything is OK (see Figure 4-4)

Figure 4-1 The Control Fun

application, featuring text fields, labels, a slider, and several other stock iPhone controls

Trang 28

Figure 4-2 Tapping the

seg-mented controller on the left

side cause a pair of switches

to be displayed Tapping the

right side causes a button to be

displayed.

Figure 4-3 Our application

uses an action sheet to solicit a response from the user.

Figure 4-4 Alerts are used to

notify the user when important things happen We use one here to confirm that everything went OK.

Active, Static, and Passive Controls

User interface controls come in three basic forms: active, static (or inactive), and passive The

buttons that we used in the previous chapter are classic examples of active controls You

push them, and something happens—usually, a piece of code fires Although many of the

controls that you will use will directly trigger action methods, not all controls will

The label that you used in the previous chapter is a good example of a static control You

added it to your interface and even changed it programmatically, but the user could not

do anything with it Labels and images are both controls that are often used in this manner,

though both are subclasses of UIControl and can be made to fire code if you need them to

do so

Some controls can work in a passive manner, simply holding on to a value that the user has

entered until you’re ready for it These controls don’t trigger action methods, but the user

can interact with them and change their values

Trang 29

A classic example of a passive control is a text field on a web page Although there can be

validation code that fires when you tab out of a field, the vast majority of web page text

fields are simply containers for data that get submitted to the server when you click the

sub-mit button The text fields themselves don’t actually trigger any code to fire, but when the

submit button is clicked, the text field’s data goes along for the ride

On an iPhone, many of the available controls can be used in all three ways, and most can

function in more than one, depending on your needs All iPhone controls are subclasses of

UIControl and, because of that, are capable of triggering action methods Most controls

can also be used passively, and all of them can be made inactive when they are created or

changed from active to inactive, and vice versa, at runtime For example, using one control

could trigger another inactive control to become active However, some controls, such as

buttons, really don’t serve much purpose unless they are used in an active manner to trigger

code

As you might expect, there are some behavioral differences between controls on the iPhone

and those on your Mac Here are a few examples Because of the multitouch interface, all

iPhone controls can trigger multiple actions depending on how they are touched: your user

might trigger a different action with a finger swipe across the control than with just a touch

You could also have one action fire when the user presses down on a button and a separate

action fire when the finger is lifted off the button Conversely, you could also have a single

control call multiple action methods on a single event You could have two different action

methods fire on the touch up inside event, meaning that both methods would get called

when the user’s finger is lifted after touching that button

Another major difference between the iPhone and the Mac stems from the fact that the

iPhone has no physical keyboard The iPhone keyboard is actually just a view filled with a

series of button controls Your code will likely never directly interact with the iPhone

key-board, but as you’ll see later in the chapter, sometimes you have to write code to make the

keyboard behave in exactly the manner you want

Creating the Application

Fire up Xcode if it’s not already open, and create a new project called Control Fun We’re

going to use the View-based Application template option again, so create your project just

as you did in the previous two chapters

Importing the Image

Now that you’ve created your project, let’s go get the image we’ll use in our image view

Ngày đăng: 09/08/2014, 14:20

TỪ KHÓA LIÊN QUAN