page 2.1

misweb Online Gradebook System mobile access Student Instructions

A new feature in misweb provides you with convenient access to selected course technology through your mobile web browser (smartphone, laptop, or other mobile device). You can access your Online Grades page by typing a rather long URL (one time), then save a bookmark to that URL. Successive accesses will require little or no typing.

In your mobile device’s web browser, go to either of the following two URLs (substituting your netid for the word netid and your password for the word password): https://misweb.cbi.msstate.edu/rpearson?mobile/bis2990-01/netid https://misweb.cbi.msstate.edu/rpearson?mobile/bis2990-01/netid/password

Be sure to type a ? before the word “mobile”, not a slash.

This will take you to a login page, similar to the one shown to the right. When the login page loads in your browser, you can save a bookmark to that page for fast access in the future. (On an iPhone, iPod Touch, or iPad, tap the middle icon at the bottom of the screen (a rectangle, with an arrow pointing to the right), and select the “Add to Home Screen” option. This will create an icon on your Home Screen that you can use in the future to quickly check your grades in this class. The sample Home Screen just below shows an example of the icon for BIS 2990.)

On the login page, if you used the first URL above, you will need to type your password into the password textfield; if you used the second URL, the entire form will be pre-filled, and you will simply tap the “Log in” button.

Upon logging in, you will see a concise version of the Online Gradebook System, with larger buttons, larger text, and no superfluous text (as shown to the right).

A QR code on the front cover of this course packet will also take you directly to our class’s grades login page on your smartphone.

page 2.2

misweb registration

Each COB student has the opportunity to maintain a student profile page as part of the COB misweb web site. This system uses misweb as a central starting point that any student, faculty member, prospective employer, or other interested party can use to contact you.

Register for the COB misweb Student Information System To register for misweb, use your web browser to go to the URL:

http://misweb.cbi.msstate.edu/editor

Enter your MSU Banner net id and net password in the textfields and click the Log in button.

If things go well, you will see a form that has several input fields. Enter any information that is needed to complete all fields. Please try to type correctly!

Click the Continue button. That’s all there is to it! You are now registered for the misweb system. You can edit your own data for your personal student profile page, keeping it up to date throughout your college career.

Once you have completed this registration process, you will have an account in the COB misweb Student Information System. Your account is password-protected. NOTE: If you do not want to use your Banner net password when you access misweb, you have the option of creating an additional special misweb-only password. Use the option in your misweb Edit Routine to create your misweb-only password.

To access misweb, go to the following URL:

http://misweb.cbi.msstate.edu

If you have any questions about this system, please email:

[email protected]

page 2.3

Checking your misweb online grades

To check your grades, go to the main misweb page, at:

http://misweb.cbi.msstate.edu

Go to your teacher's faculty profile page. Scroll down to the Courses Taught section. Click on any link for Online Grades.

On the next screen, select your class from the list of Online Gradebooks, enter your net id and password in the textfields, and click the Log in button.

Assuming that you log in correctly, you will see one or more buttons for your class. These buttons (if available) link to: - your grades - the private course web site - an online class calendar - the class email archive - the class message board

The button page also has a link at the very top that you can use to log out of misweb. For (your own) security reasons, you should always log out rather than just moving to a different web page, since logging out physically terminates your misweb session.

For your homework assignment, you must do the following:

1. Register for misweb. 2. Enter your basic information. You must enter your name and email address; everything else is optional. 3. Check your grades in this class. 4. While you are checking your grades, familiarize yourself with the available buttons, such as Private Course Web Site, EMail Archive, and Class Message Board. 5. Post a message on the Class Message Board under the Topic:

Characteristics of a Good Student

If the Topic has not been created yet, create it. If it has been created, add your message to the Topic. Try to add something meaningful.

Pay attention to what you are doing when you post a message on the Class Message Board. If the Topic already exists, DO NOT CREATE A NEW TOPIC. Instead, add a message to the existing Topic.

To add a message to an existing topic, you must actually read at least one existing message on the topic. When you read a message, you will see a button to Post a New Message.

page 2.4

6. When you access the class message board, you will see an option at the bottom to set your “Message Board notification” level. Select “detail”, so you get an automatic email as soon as anyone posts a message to the class message board. This is a required part of this assignment.

The messages posted on the class message board could be an invaluable tool when you are working on your homework assignments.

You do not need to turn in any type of paper or report for this assignment (or any other assignment in this class). Everything will be graded electronically.

page 2.5

Joining the MSU iOS Developer Team

Introductory information This course covers programming for the Apple iPhone, iPod Touch, or iPad devices. All of these devices use the same basic operating system, which is called iOS. The class could just as well be called iOS Programming, or iOS Development.

You must have access to an Intel-based Apple Mac computer to do this programming. You do not actually need to have an iPhone, iPod Touch, or iPad device. You can program on a Mac, and test your programs (in Apple terms, your apps, or applications) in a software-implemented iPhone Simulator on the Mac. The simulator looks just like an iPhone, and your program operates much like (although not exactly like) it would on an iPhone. When your program is fully developed, you can then deploy it to your iPhone, iPod Touch, or iPad (hereinafter referred to as device), if you have one.

Our programming will be done with a free Apple-supplied iPhone SDK (Software Development Kit). Although the SDK is free, you must be a registered iPhone Developer to download it. Registration is also free. With a free registration, you can develop apps, and test them in the simulator – you may not, however, deploy your apps to your device. To do that, you must be a paid iPhone Developer – with one exception: you will be a member of the MSU iPhone Developer Team, which is part of the Apple University Developer Program. You will not need to pay $99 to transfer your apps to your device. You won’t have to pay anything. Your only restriction is that you will not be able to upload and sell your developed apps through the iTunes Store. To do that, you have to be a true paid iPhone Developer. That costs $99 per year for an individual, or $299 for an entity with multiple developers.

Preparing for programming

1. Welcome to the MSU iOS Developer Team! There are several steps that must be completed before you can start programming. This is a rather long, multi-stage process, that includes several email messages to which various people must respond (you respond to some, and your Team Admin responds to others). Definitely start this process early, because it could take several days to complete.

2. You need to register to be an iPhone Developer. In any web browser, go to http://developer.apple.com/iphone.

page 2.6

This is the iOS Dev Center, with all sorts of downloads and resources (including the actual iPhone SDK). Some (most) items are restricted to registered developers. Toward the top right, click the link to Register (the layout of Apple’s web page could, of course, change at any time; at the time of this writing, there is a link that says Register).

3. Most of the registration process is straightforward. You will be asked several questions, including (at this time; the questions could change at any time) what type of programs you intend to write, who your primary market is, and things like that. Answer appropriately.

4. You will need to click the I Agree button at the end to continue. Upon completion, you may close your browser. Apple will send you another email message.

5. (Complete this step only if you will be programming on your own Mac computer. If you are going to be programming on a lab computer, you do not need to do this step.) If you are going to be developing apps on your own Mac, you can (at any time, including now) go back to http://developer.apple.com/iphone, log in, and download the Software Development Kit (SDK). Any registered developers – even non-paid developers – can download this SDK, for free. Simply log in, find the link to download the SDK, download it, and install it. This will install Xcode, the IDE (Integrated Development Environment) in which you will program.

After installation, Xcode will be available on your Mac hard drive under Developer, Applications. You should drag Xcode’s icon onto your dock, for quick access.

page 2.7

6. (Complete this step only if you will deploy your apps to your iPhone, iPod Touch, or iPad.) If you plan to install your programs on a device, your Team Agent (not you) must enter your device’s 40-hex digit Unique Device Identifier (UDID) into the program portal. There is a link on our course web site to a form that you can submit to send your UDID to the Team Agent. (You will be able to deploy your apps to your registered UDID, without uploading those apps to the iTunes store. If you plan to deploy your apps to your device, it is important that you get this step right.)

You can find your device’s UDID in iTunes (Mac or Windows). Your device must be connected to your computer for either of these programs to detect the device’s UDID.

iTunes typically shows your device’s serial number. If you click on the phrase Serial Number, however, it will change to Identifier (UDID), and display your 40-character UDID. Copy that UDID into the form that is available via the course web site (from the iTunes menu, select Edit, Copy). It is important that you supply the correct UDID to your Team Agent, and that the Team Agent supply the correct UDID to Apple.

Submit your UDID to your Team Agent, who will then register your device. To submit your UDID, go to our course web site (on misweb), where you will find a link entitled “Submit your Unique Device Identifier to the Team Agent”. This will be a web-based form, into which you should paste your UDID. At some point, your Team Admin will collect all the submitted UDIDs, and copy/paste them (one-by-one, unfortunately, into the developer center).

page 2.8

7. (If you are going to be programming on a lab computer, or if you are not going to be deploying your apps to a device, you do not need to do this step.) If you are going to program on your own Mac and transfer your apps to your device, you will need a signing certificate. If you are going to test your programs only in the simulator, and do not plan to transfer them to a device, you do not need a signing certificate, and you will not need to complete this step.

You need a signing certificate so your device knows that it can trust the apps that you install (as well as so your teammates’ devices know that they can trust your apps). You will create your own personal Development Certificate. You are allowed to have only one Development Certificate. - In the Applications folder, open Utilities, then Keychain Access. - Using the menu bar at the very top of the screen, select Keychain Access, then Certificate Assistant, and Request a Certificate from a Certificate Authority. - In the Certificate Information window, enter your email address in the Common Name field, enter your name in the CA Email Address field, leave it blank in the Request is group, select the Saved to disk option click Continue - In the Save As pop-up, you can change the name of the saved file to include your name; that might help you identify your file if you are performing this step on a shared computer – do not change the .certSigningRequest extension on the filename - The Certificate Assistant will save a Certificate Signing Request (CSR) on your desktop. You will upload that file to the developer web site later.

You can not do the remaining steps until your Team Admin sends you an invitation to join the MSU Developer Team.

page 2.9

8. At some point, the Team Admin will extend invitations to each Team Member to join the MSU iPhone Developer Team. You will get your individual email, to which you should respond. When you get your email invitation, it will include two different parts: one if you are already a registered Apple Developer and want to accept the invitation to be part of the MSU Team, and another if you are not yet a registered Apple Developer.

The first link in that email is for registered developers. You are one (assuming that you completed steps 2-4). Click that link. This will open a login page in your browser. Use your Apple Developer login and password. This will take you to a page where you will accept the invitation to join the MSU team. You will need to agree to the iPhone Developer Program University Student Agreement.

Finally, you will go to the iPhone Developer Program Portal.

9. You are now logged into the MSU Program Portal. Along the left side, you will see areas such as: Team Certificates Devices App IDs Provisioning Distribution

10. Click on Team. You will see the members of our MSU Developer Team.

page 2.10

11. (Complete all remaining steps only if you will be programming on your own Mac and deploying your apps to a device.) Click on Certificates. Use the Choose File button (Safari browser) or Browse button (Firefox or IE) to upload your .certSigningRequest file that you created earlier.

Submit this file. You will see on the next screen that your certificate request is Pending Approval. Your Team Admin will be sent an email telling him that you have submitted a CSR request. The Team Admin will need to physically log into the team portal, and approve your request. When that step is done, you will get an email notification, and you can continue.

12. When you receive your email that your CSR has been approved, you can return to the program portal, click on Certificates, and download your certificate (Click the Download button). This will download a file named developer_identity.cer.

13. (This step apparently should be done in either Safari or IE, as Firefox apparently automatically installs the certificate and does not give you an option to save it to disk.) Just below the table that lists certificates, there is a paragraph that says something like:

If you do not have the WWDR intermediate certificate installed, click here to download now.

Click on the link. This will download a file named AppleWWDRCA.cer.

14. Double-click each of these downloaded files (developer_identity.cer and AppleWWDRCA.cer) to open Keychain Access (the Keychain Access program is in Applications/Utilities). (Note: On a Mac, the two downloaded files will be in /Users/yourname/Downloads.) page 2.11

You can look in Keychain Access to see if the second certificate is already installed. In the left side, under Keychains, click on login. Under Category, click on Certificates. In the right side, see if there is already a Certificate for Apple Worldwide Developer Relations Certificate. If there is you do not need to install the second file that you downloaded.

As the figure shows, there are two iPhone certificates installed on this computer: one for development purposes, and one for distribution purposes. The first is satisfactory for development on your own device; the second is required to upload apps to the iTunes store.

15. For safety purposes, or if you plan to develop on multiple computers, you might want to save your private key somewhere safe.

To export your private key and certificate for safe keeping, open Keychain Access and select the Keys category in the lower left box.

In the right side, Control-click the line that includes your name and says Private Key. Select the Export option from the pop-up menu. If your name is Bear Pearson, you might want to save the file as BearPearsonPrivateKey, saving it to the Desktop. Save the file in the default Personal Information Exchange (.p12) format.

You will have to supply a desired password. This password could be required later, such as if you need to import this private key onto another computer.

page 2.12

16. Back in your browser, in the left side of the program portal (on the Apple Developer web site), click on App IDs. The information there tells you (not all that clearly) that you will need this App ID in Xcode when you send a developed app to your device (and not before then). The entire team has one App ID, which technically consists of a 10-character code (selected by Apple), followed by an identifier that your Team Agent selected. In reality, the 10-digit code may not be required, and you will need to know only:

edu.msstate.bis4990.*

We will use this value later. Much later.

17. Click on Provisioning. A Provisioning Profile must be installed on a device before that device can be used for development purposes. The entire team has one Provisioning Profile, which consists of a set of iPhone Development Certificates, UDIDs, and one App ID. This Provisioning Profile will need to be constructed after all Team Members have created their Certificates, and after all Devices have been identified. (You can click on Details to see the list of Certificates, Devices, and App IDs that make up this Provisioning Profile.) page 2.13

When the time comes to install the Team Provisioning Profile on your device, return to this part of the program portal, click Provisioning, then click the Download button to download the file BIS_4990_Development.mobileprovision. The file will be stored in your Downloads directory, such as /User/BearPearson/Downloads. Find this file in your directories, and drag the file into /Users/BearPearson/Library/MobileDevice/Provisiong Profiles. Amazingly, this simple dragging operation is all that is needed to install the provisioning profile. You should be all set to develop on your device.

------

That’s all for now! Next up, Hello World, Hello Simulator.

page 2.14

Project name: HelloWorld-

Hello World, Hello Simulator

These steps demonstrate the creation of a basic static Hello World app, which has a simple button that declares “Hello World.” In the second part of this assignment, we will add dynamic functionality (for example, clicking the button will cause something to happen). The main point of this first exercise is to make sure that your environment is configured correctly, and to introduce you to your main programming tools.

Initially, these apps will be tested in the iPhone Simulator. Later, you will copy the app to your device, and run it there. At that point, you will have a complete (albeit beginning) understanding of the total iPhone development environment.

1. iPhone programming must be done on a Mac computer. In our class, up to three students may work together on each homework assignment. If nobody in your group owns a Mac, you can use one in the COB lab, the Library or the Academic Computing Center lab – in that case, be sure to have a flash drive on which you will store your project; the USB ports are on the back of the Mac display device.

When you start a lab Mac, assuming that you have inserted your jump drive in a USB port, you will see your drive on the desktop (labeled No Name, shown to the right).

2. Open Xcode. (In the Dock shown below, click the second icon from the left, with a blue background and a picture of a hammer. If your Dock does not have such an icon, double-click on the Macintosh HD icon, then the Developer folder icon, the Applications Folder icon, then the Xcode icon.) Xcode may not open any windows automatically, but you will have an Xcode menu bar at the very top of the screen, with options like: Xcode, File, Edit, View, Project, and so on.

page 2.15

3. On the XCode menu bar, select File, New, New Project. This will open the New Project Window.

4. In the left side of the New Project window, just under the iOS label, click on the Application option. The word Application should display with white letters on a blue background, indicating that that category is currently selected (highlighted).

5. In the right side of the New Project window, click on the Single View Application icon. You will create a View-Based Application, one that relies on the concept of a UIViewController object and a UIView object (a UIView object is a rectangular region on the screen – it could be the entire screen, or it could be smaller, such as a UIButton or a UILabel).

6. Click the Next button.

7. In the Product Name field, name your project, such as HelloWorld-rap10-awp1 (no spaces). Be sure to use your group members' net ids as the last part of the project name, to differentiate your project from those of other students. If you are programming as part of a group, include the net id of each student in your group as the last part of the file name.

Each of the settings in the New Project window is important: - be sure to include your team’s net ids in your Product Name; no spaces - make sure that the Company Identifier says edu.msstate.bis4990 - leave the Class Prefix blank - choose “iPhone” as your Device Family - do not check “Use Storyboard” – checking this option will only complicate your project - be sure to check “Use Automatic Reference Counting” - do not check “include Unit Tests”

8. Specify the location in which you want to save your project. On a Lab computer, be sure to save your project on your jump drive (on your own computer, you will probably save it in your Documents folder).

Make sure that the “Create local git repository” checkbox is unchecked.

9. Click the Create button.

page 2.16

10. You will now see the actual HelloWorld Project window.

In the leftmost pane (this is the Navigator pane), you will see three folders: - your project - Frameworks - Products

(The entire work area to the right of the Navigator is the Editor pane, or simply the Editor.)

When you click a folder, the folder opens and related sub-items are displayed. For instance, your project folder includes various interface files (.h extension), implementation files (.m extension), and nib files (.xib extension) that have been created for your project. In this case, there are five files listed (excluding your netid-based prefix): AppDelegate.h AppDelegate.m ViewController.h ViewController.m Controller.xib

11. Click (don’t double-click) the ViewController.xib file. This will open your nib file so you can create your user interface. (The functionality to edit the user interface used to be provided in a separate program named Interface Builder, but starting with Xcode 4 it is integrated into Xcode.) This will open the nib file in the Editor (the large pane on the right).

page 2.17

12. As you can see below, the main thing that you see is a blank gray rectangle – this represents the screen on your device, the user interface. This is the View. To the right of this, you see (in the top right), the Inspector, which is used to manipulate attributes, and many other things. In the bottom right, you see the Library, which contains the Toolbox of controls that you can drag over to the View Window.

If the Inspector and Library are not open, you can open them via the menu bar option – View, Utilities, Show Object Library.

The relatively narrow vertical area to the left of the View is the Dock. Below the View, in the bottom left corner of the Editor, you will see a right-pointing arrow – click this arrow to get a much more informative display of the Dock. You will usually want this more complete display of the Dock (shown to the right).

page 2.18

13. Drag a Round Rect Button from the Library (bottom right) onto the View. This puts a button on your iPhone (basically). This button is an instance of the UIButton class.

14. The Inspector shows various attributes of the “current object”. The Inspector has six icons, near its top. From left to right, the icons are: Show File Inspector Show Quick Help Inspector Identity Attributes Size Connections

We will primarily use the last three of these: Attributes, Size, and Connections.

15. Click on the UIButton to make it the current object (blue re-sizing handles will appear on its edges), then click the Inspector’s Attributes icon (the third from the right of the six icons in the Inspector). You can change various attribute values here, including the Title (the caption on the button). Type Click me in the Title field, and press Enter.

16. You now have a working program, although clicking the button won’t actually do anything. To test it so far, click the Run icon in Xcode’s top left corner. (Note: a Select list to the left of Build and Go should say “iPhone 5.0 Simulator” – make sure that it does.)

You may be asked whether you want to Save before building. You do. Click Save All. After a few moments, you will see the iPhone Simulator. It will automatically open your program. The Simulator really does look a lot like an iPhone. (Command left-arrow and Command right-arrow will simulate rotating your device).

17. The iPhone Simulator menu bar is at the top of the screen. Click iPhone Simulator, Quit iPhone Simulator.

18. Interface Builder is a WYSIWYG (What You See Is What You Get, pronounced WizzyWig) tool. Experiment with Interface Builder, dragging various objects from the Library onto your view. Use the Inspector’s Attributes tab to change various attributes of your objects.

This is the end to this part of the assignment. page 2.19

Project name: HelloWorld-

Dynamic Hello World

In this assignment, we will add dynamic functionality to your Hello World program. Much of our programming today is event-driven – we respond to events. When the user clicks or taps a button, that creates an event, and our program responds to the event. In iPhone programming, we respond to many different types of events: - the program starting - tapping a button - selecting a row from a table - shaking the phone - gesturing with the finger (or a french fry) - moving to a different location (as detected by the onboard GPS system), and more.

1. Open Xcode.

2. From the Xcode top menu bar, select File, Open. Browse to your HelloWorld project, then open the file that ends with a .xcodeproj extension. It will have a small blue icon to the left of the file name (not a folder icon, but a different small blue icon).

On a lab computer, you will need to navigate to your jump drive to open your previously-saved project. You will go through screens similar to the ones shown here. page 2.20

3. This will open your project from the previous assignment.

Click the ViewController.h file. This is the interface file for your HelloWorldViewController object. In the interface file, you define the instance variables and the methods that your class will have (then, in the implementation file, you will write the code for those methods). Your interface file will look something like:

#import

@interface ViewController : UIViewController { }

@end

IMPORTANT NOTE: Xcode 4 may not automatically generate the curly braces {} that are shown here. If they are not there, as shown here, type them.

The first line of this file imports classes from the UIKit framework. The second line indicates that the name of this class is ViewController, and that this class is a subclass of the UIViewController class (more on this later).

4. Insert the following line just before @end to define a method named showColor1.

- (IBAction) showColor1;

This defines a method (a method is a body of code that does something). (IBAction) links this method to Interface Builder (whose functionality is now actually integrated into Xcode), and tells Interface Builder that you will connect this method to an object that you manage in Interface Builder. Finally, showColor1 is the name of the method.

This one line of code is called a method header. It is not a complete method – remember, a method is a body of code, probably not a single line. You will define method headers in your interface file; later, we will write the body of the method in the implementation file – this is a good thing to remember, you will write only one line for a method header in the interface file (the .h file), but you will write a complete method (the body) in the implementation file (the .m file). Remember that the .h file is the interface file, and the .m file is the implementation file.

5. Your file should look something like this:

#import

@interface ViewController : UIViewController { }

- (IBAction) showColor1;

@end

6. Save your file (File, Save in the very top Xcode menu bar).

page 2.21

7. You are now going to define a connection between the method that you have defined in Xcode, and the object that you have created in Interface Builder.

8. Click ViewController.xib. This will display the various panes.

An icon in the middle pane (the Dock) appears to be a yellowish 3- dimensional stick figure of a cube. This icon represents File’s Owner – the class file that owns this nib file.

Remember, if you click the small right-pointing arrow below the View, in the Editor’s bottom left corner, you will get a more complete display in the Dock.

9. Click the File’s Owner cube to highlight it. The Inspector now shows information related to the ViewController class.

10. Click the Inspector’s Connections tab (far right icon). This tab will display two important sections (as well as additional sections that we don’t need at this point): Outlets Received Actions

Outlets and Actions connect your program code (as created in Xcode) with your user interface (as designed in Interface Builder).

An outlet is a reference to a GUI object (such as a UIButton) that program code will use to refer to the GUI object.

An action is a method that will operate on an object when an event occurs, such as when the user taps a button.

page 2.22

11. Connect the showColor1 method that you defined in Xcode to the actual object that is on your user interface. In the Actions section in the Inspector, you will see the action (showColor1) that you just created.

Make a connection between your showColor1 action and the graphical UIButton. Click on the small circle to the right of showColor1, and draw a line from the source (the small circle) to the button that is visible on your view. Once you have done this once or twice, you may even believe that making connections is fun!

When you release the mouse, you will see a list of the events to which the UIButton can respond. Select Touch Down.

12. Looking back at the Inspector, you can see that your showColor1 method is connected to a Rounded Rect Button, responding to the Touch Down event.

13. When you click on an item in the Documents Window (Dock) (and highlight it – sometimes a single click is not enough – be sure to highlight the desired item), the Inspector will show attributes or connections of that item. So far, we have been looking at connections of ViewController.

Click on the Rounded Rect Button in the Documents window to highlight it. Now the Connections tab in the Inspector shows – in a different way – that this UIButton is connected to the showColor1 method, responding to the Touch Down event.

page 2.23

14. Back in the XCode window, click on ViewController.m to edit the implementation file (the .m file). This is what you will see:

#import “ViewController.h”

@implementation ViewController

(a bunch of comments and predefined methods)

@end

We need to write (implement) the actual code for the showColor1 method, which will be called when the user clicks on the button.

15. Add the following lines of code between just before @end. This code defines a method that will change the color of the screen when this method is called.

- (IBAction) showColor1 { self.view.backgroundColor=[UIColor magentaColor]; }

A complete explanation of this one line of code covers a lot of object-oriented programming ground. This is an assignment statement, the syntax of which is:

L-value = expression;

There are two steps to execute this statement. The first step skips right over the L-value and the = sign and is: (Step 1) evaluate the expression.

In this case, the expression is:

[UIColor magentaColor] This statement calls a method – in Objective-C, you call a method by enclosing your code in square brackets. Inside the brackets, you will have a class or an object, a space, and a method. In this case, UIColor is the class, and magentaColor is the method.

You can look up the documentation of the UIColor class.

In Xcode’s top menu bar, click Help, Documentation and API Reference. You will see a search pane in the left side (in the screen capture, we have typed uiview into the search field).

page 2.24

You can specify several find options. Click the small gray downward-pointing triangle just beside the magnifying glass at the left end of the search field. Your three find options are: - match type (contains, prefix, or exact) - Doc sets - languages

We use a match type of prefix, so the search matches only items that begin with our search phrase; alternatives are contains and exact. We search only one Doc set (set of documentation), iOS 5.0 Library. Finally, we search for documentation from only two languages, Objective-C and C. You can modify your find options at any time.

Type uicolor in the search field. You will see the documentation of the UIColor class; this documentation describes all of the UIColor class’s properties and methods. One of these methods is named magentaColor. The documentation tells you that this method “returns a color object”. The following phrase, then, returns a color object that is the color magenta:

[UIColor magentaColor]

The second step in executing the assignment statement is (Step 2) assign the evaluated value to the L-value. Let’s look at that L-value. self is an Objective-C keyword that refers to “the current object.” The question arises, what is the current object? The answer goes back to a line in your interface file:

@interface ViewController : UIViewController {

As we mentioned earlier, the : in this line indicates that our class (ViewController) is a subclass of UIViewController. This establishes an is a relationship, which is truly one of the most important concepts in all of object-oriented programming. Our ViewController “is a” UIViewController – it inherits all of the functionality that is defined by the UIViewController class, all of its properties and methods.

page 2.25

All of the classes in the iPhone SDK are arranged in one big family tree, with the NSObject class at the top (the root of the tree), and all other classes descending from it. Each class in the tree inherits functionality from all of its superclasses (ancestors, in family tree terminology). Here is a very small sample of this hierarchy of SDK classes:

NSObject is the root of the tree. It has immediate subclasses like UIResponder, UIColor, NSString, and NSTimer. Each of these classes may have their own subclasses. As this tree shows, for instance, UIResponder has subclasses like UIView and UIViewController. A subclass inherits common functionality (properties and methods) from all of its superclasses, then adds its own specific functionality. UILabel, for instance, inherits common functionality from NSObject, UIResponder, and UIView, then adds the specific functionality that a UILabel object needs. An understanding of this concept is crucial to object-oriented programming.

Back to the phrase self.view.backgroundColor. The keyword self refers to our HelloWorldViewController, which is a UIViewController.

Look up the documentation of the UIViewController class; this documentation describes all of the UIViewController class’s properties and methods.

As the documentation shows, a UIViewController object has a property named view, which is “the view that the controller manages.”

page 2.26

We can refer to that view with dot notation, with the name of the object to the left of the dot, and the name of the property to the right of the dot. (You can refer to any property, of any object, with dot notation: object dot property). This explains the first part of the line of code:

self.view

self is our HelloWorldViewController object, and view is the view that that controller manages. That UIView object has a property named backgroundColor (you can look it up in the documentation of the UIView class). Thus, our L-value is:

self.view.backgroundColor

which is “the backgroundColor property of the UIView object that is managed by our HelloWorldViewController object.” We assign that the magentaColor that was returned to us, and it will change the background color of our view (the screen) to magenta.

While that is perhaps a long and detailed explanation of one line of code, once you understand all of it, you can change the background color of any object to a pre-defined color.

16. Click the Run button, which will, of course, run your program in the Simulator. Hopefully, when you click your button, the background color of your view will change to magenta.

17. Now it’s time for you to fly on your own.

In Xcode, in your interface file (ViewController.h), define another method, named showColor2 (this will require one line of code, much like one line that you wrote earlier in your interface file).

In Interface Builder, put another button on your program. Connect your showColor2 method to your second button.

Back in XCode, in your implementation file (ViewController.m), write your showColor2 method, and change the view’s background color to white (you can use [UIColor whiteColor]). This will require a second method – a body of code – much like the method you wrote earlier.

18. When you have your project working just as you want it, you will upload it to an account that has been set up for you on mislab.cbi.msstate.edu (instructions coming up).

page 2.27

19. We will use an FTP (File Transfer Protocol) to upload our assignments to the mislab server (note that the name of this server is mislab, not misweb; its complete name is mislab.cbi.msstate.edu).

You have an account on the mislab server. Your login id on mislab is your net id, and your password is the last four digits of your MSU id number.

You need an FTP program to upload your project. For the Mac, Yummy FTP is a good program. While the full version costs $28, you can get a free 30-day trial. You can also buy a perfectly functional Yummy FTP Lite for $1.99 – that version will do everything that you need to do, and it is a real app, not a time-limited trial version (personally, I would get the $1.99 version).

On a Windows computer, Filezilla is a free FTP program that works quite well.

In any FTP program, you need only three pieces of information to connect to a server: the name of the server (mislab.cbi.msstate.edu), your login id on that server (your net id), and your password on that server (the last four digits of your MSU id number).

Of course, making a connection depends on several other things: you have to actually be connected to the internet, and mislab needs to be up and running and connected as well. So while there are potential problems that could prevent your connecting, you should know whether you are connected to the internet (try going to a web page; if you can get to a web page, you're connected to the internet), and mislab is rarely down or unavailable. The more likely connection problem is mis-typing one of these three items.

When you connect to mislab, you will see two panes (depending, of course, on your actual FTP program; the screen captures show Yummy FTP): your local computer in the left pane, and your root directory on the mislab server in the right pane.

page 2.28

You need to upload your entire project directory to your mislab account. Your first task, then, is to find your project directory. Since you could have saved your project anywhere on your computer, it's hard to give exact instructions on how to locate it. If you saved it on your Desktop (in your Desktop folder), you can click on the dropdown list shown to the right, and locate your user directory (this is your user directory on your local computer, not on mislab). Both Desktop and Documents are immediately under your user directory.

When you finally find your project, uploading is a simple matter of dragging the project folder from the left pane to the right pane.

Note: If you double-click on your project folder and go down into your project folder, you will see the individual files that are in your folder. Do not upload from there – you need to upload your entire project folder, not the individual files that are in it. Click on the small left-arrow above your list of local files to move back up one directory. The two images below show contents of the project folder – this is NOT what you should upload. You need click and drag only one item – the actual project folder, as shown above. page 2.29

20. Don’t just remove your jump drive from the computer. Control-click on your jump drive. From the pop-up menu, select the Eject option. When you jump drive icon disappears from the desktop, then it is save to remove the jump drive from the computer.

21. (optional step) Hopefully, this program has added some color to your life. The big thing, of course, is that we now know how (1) define a method header in an interface file, (2) how to drag an object onto our view (in Interface Builder), (3) how to connect that to our method header, and (4) how to respond to an event by writing a body of code in our implementation file. We even know how to perform at least one action: we can change an object’s background color!

But you are a Bulldog, right? Wouldn’t you prefer maroon to magenta? Wouldn’t you prefer official MSU maroon?

The UIColor class defines several methods that you can use to get a predefined color object. You can look up documentation of this class in Xcode by selecting Help, Documentation, then searching for the word UIColor. You will find the following class methods (among others):

[UIColor redColor] [UIColor whiteColor] [UIColor blueColor]

There are quite a few of these, but there is no [UIColor maroonColor], and even if there were, it would probably be the wrong shade of maroon. There is, however, a method that you can call to create any color you want. With this method, you specify your desired color by specifying the desired intensity of red, green, and blue. Each intensity is specified as a value from 0.0 through 1.0, inclusive. To change your background color to maroon, you can use the line:

self.view.backgroundColor=[UIColor colorWithRed:0.4 green:0.0 blue:0.0 alpha:1.0];

Try it, you’ll like it.

page 2.30

Hello iPhone!

In this assignment, we will finally deploy an application to an iPhone or iPod Touch. Make sure that you work in a group that has at least one device. (Instructions for deploying to an iPad are provided at the end of the assignment.)

You will not be able to use most lab computers to deploy your app to your device (exception: you may use the Mac computers in the McCool lab). You must use a non-public Mac, one that has been properly set up (our assignment #1b) and verified to be secure. You can use a public computer to create your app (such as your ImageViewer app, from our previous assignment), save your project on a jump drive, and take it to a non-public Mac (or one in the McCool lab) for deployment. Your teacher will be happy to help you deploy, using his computer (not during class).

1. Start with any app that you have previously written.

2. Once your app is running, install it on your device. In Xcode, click on the very top item in the Navigator (the name of your project) to display Summary information about the project. Under the word Targets (middle pane), click the name of your project. You should see information similar to that shown below.

The value for Identifier is very important – it should be composed of your developer’s app id (in our case, edu.msstate.bis4990), followed by the name of your project.

You can change your Deployment Target here – this is minimum verson OS that the app will run on. If you specify a Deployment Target of 5.0, you can not deploy the app to any device running an iOS version lower than that. In general, for maximum market opportunities, you probably want to specify the minimum Deployment Target possible. However, if you specify, for example, 3.0, you can not actually use any SDK functionality that was developed in versions after SDK 3.0. For this app, we did not use any such functionality, so you can choose 3.0.

page 2.31

3. The Information window contains five tabs: Summary, Info, Build Settings, Build Phases, Build Rules

Click the Build Settings tab. Under Code Signing and Code Signing Identity, the value of Any iOS SDK must be correct. If your environment is set up correctly, your Xcode should indicate that your developer profile has been selected, much as mine does below (look at the fifth line from the bottom).

If the value of Any iOS SDK is “iPhone Developer (no profiles currently match)”, then you have not set up your environment correctly. You may have to return to assignment #1b, and go through those steps. Deployment will not work unless your environment is set up correctly.

4. Close the Information window. In the Xcode Project window, change the select list to the right of the Run button to “iOS Device”.

5. Click the Run button. This should install your app on your device.

You may get a pop-up or message telling you that you need to configure your device for development. You may have to click a button like “Use for Development” or “Install” a provisioning profile. We have never had any problem with any device that we prepared for development. You should always be cautious when modifying your device, but we will say that we have never had a problem with any device in this regard.

Note: If you get a pop-up error message when you try to install an app, the message will be very uninformative. The most likely solutions: - verify the Identifier (step 2) - verify the Code Signing values (step 3) - verify that you have the BIS 4990 Provisioning Profile installed (on your device, Settings, General, Profiles) - if you just downloaded your copy of the provisioning profile to your computer, close Xcode and re-open it – Xcode loads provisioning profiles when Xcode itself is opened

page 2.32

If these fixes do not work, you can try the following: - reboot your device – hold down the Sleep button and the Home button at the same time; after a few seconds, you will see a “Slide to Power Off” message; keep holding the buttons; when you see an Apple logo, you can release the buttons; wait for the device to completely reboot - if all else fails, try creating a new project, see if it installs; if it does, copy your files from your original project to this one (painful, but this is sometimes the fix)

6. There is one more thing that you might enjoy doing – creating an icon for your app’s representation on the Home screen. Use an image editing program to create a 57-by-57 pixel .png file.

With your Target displayed, click the Summary tab. Now drag your icon file into the App Icons section of this tab.

7. Re-deploy your app to your device. You will see your new icon on the Home screen.

Because of all the chained-together requirements to satisfy the code-signing security requirements, deploying an app to a device is one of the trickiest parts in all of iPhone programming. There are so many steps, and each one must be done correctly. The error messages are sometimes not very informative. The good news is that once you deploy your first app successfully, you probably have your environment all set up, and you will be able to deploy your next one, and the one after that, with ease.

If you have gotten through all of these steps, that is fantastic!

There is no additional project to upload. You should, however, send your teacher an email telling him that your group did deploy an app to a device. page 2.33

Deploying an app to an iPad will require a few modifications, which will be summarized here: 1. Ideally, you will actually create an iPad app, not an iPhone app. When you first create a new project, there is a dropdown list that you use to indicate either iPhone or iPad. .xib files will be created accordingly. 2. You can create an app that programmatically configures itself to accommodate both iPhone and iPad layouts, but it is somewhat complicated to do so. It is easier to create either an iPhone app, or an iPad app. 3. When you create an iPad app, Interface Builder will adapt and present you with an iPad- looking user interface. 4. If you create a Home Screen icon, it must be 72x72 pixels, not the 57x57 used on the iPhone.

page 2.34

Project name: ImageViewer1-

ImageViewer1

Pictures are fun.

In this assignment, we will write a small program that we can use to display a set of pictures. We will say from the outset, this is not how we would write a finished version of this program. The program that we will write is not very scaleable or flexible. It will work fine for displaying six pictures, but would not work well at all if we wanted to display a hundred or so (that is the part that makes this approach not scaleable). The program will be written to display six specific pictures – changing the program display a different set of six pictures would require that we actually modify the program code (this is not flexible). Our goal in this assignment, however, is not to write a polished, finished product, but to learn the basics of displaying images.

1. Get some image files. The approach used in this program works best if the images all have the same aspect ratio (this is the ratio of the width of the image to its height). Our images were all 320 pixels wide, and 320 pixels tall, which is an aspect ratio of 320:320, which reduces to 1:1. Any resolution which would reduce to 1:1 has the same aspect ratio as any other resolution that reduces to 1:1. Examples are: 640:640, 1024:1024, and 1600:1600.

2. Once you have your image files, open Xcode and create a Single View Application.

3. Open your Supporting Files folder, and drag your image files into the folder.

VERY VERY IMPORTANT: When you drag your files, be sure to check the checkbox to “Copy items into destination group’s folder (if needed)”.

page 2.35

4. We need a UIImageView object, in which we will display an image. We will need to operate on that object – we need to be able to refer to it, so we can change the displayed image. When you need to refer to an object, it needs a name. We should, then, define a variable (just like in Algebra, a variable “represents something”; in our programming, we will define a variable to represent a button, an imageView, a label, or any of the other objects that we will create). We are going to drag the UIImageView object onto our view in Interface Builder, then program with it in Xcode (in these projects, we frequently speak of Interface Builder as if it were a separate program, which it used to be, but its functionality is now incorporated in Xcode; technically, Interface Builder no longer exists).

In Xcode, the first few lines of your interface file (ViewController.h) should be something like:

#import

@interface ViewController : UIViewController { }

@end

IMPORTANT NOTE: Xcode 4 may not automatically generate the curly braces {} as shown here. If it doesn’t, type them.

Remember, an interface file is used for two things: (1) to define instance variables, and (2) to define method headers. We know how and where to define a method header (with one line of code, immediately before the @end, and after the closing curly brace). We define instance variables inside the curly braces.

We could define our variable with the following :

@interface ViewController : UIViewController { UIImageView *imageView; }

This line defines the data type of the variable – it says that this variable will represent a UIImageView object – an instance of the UIImageView class. It also defines the name of the variable. In Objective-C, any time that a variable represents an object, the variable name should be preceded with a * character. Finally, it implicitly defines the size of the variable (we say implicitly, because we did not explicitly specify a size – the size is automatic, depending on the data type). In any programming language, when you define (or declare) a variable, you define three things: its data type, its name, and its size.

While this line would work for some variables that we might define, it will not work for this one. If we want to make an Interface Builder connection to a variable that we define in Xcode, we need to use the keyword IBOutlet so Interface Builder will recognize the variable. The first few lines of your interface file should actually be something like:

@interface ImageViewerViewController : UIViewController { IBOutlet UIImageView *imageView; } page 2.36

5. In our admittedly non-scaleable approach to this problem, we will use a button for each picture. Click a button, and the corresponding picture will be displayed. We need a method to respond to each button click. We need six methods. Your interface file now contains:

@interface ImageViewerViewController : UIViewController { IBOutlet UIImageView *imageView; }

- (IBAction) showBear; - (IBAction) showDeer; - (IBAction) showGoats; - (IBAction) showRainbow; - (IBAction) showWindowView; - (IBAction) showLakeView;

@end

6. Click on ViewController.xib to open it. Drag a UIImageView object from the Library onto your view (click View, Utilities, Show Object Library if the Library is not visible). Drag six UIButton objects onto your view. Arrange them as you like.

While you can do a good job of aligning objects visually, you can be far more precise by using the Inspector’s Size tab.

You can specify a precise value for the X and Y coordinates of each object, as well as its width and height. We made each of our buttons precisely 120 pixels wide, and 40 pixels tall. We set the left edge of the left column of buttons on X-coordinate 20, and the right column on X-coordinate 180. We aligned the tops of the three rows of buttons at Y- coordinates 270, 330, and 390. We made our image view 320 pixels wide, and 240 pixels tall (for an aspect ratio of 4:3).

page 2.37

7. Make your Connections. Connect your one outlet to your UIImageView object, and connect your six actions to your six buttons.

8. Implement one of your methods (in your implementation file, the .m file):

- (IBAction) showBear { imageView.image=[UIImage imageNamed:@"BearAtLunch.jpg"]; }

This is, of course, an assignment statement. The first step is to (Step 1) evaluate the righthand side:

[UIImage imageNamed:@"BearAtLunch.jpg"]

Enclosed in square brackets, this code calls the UIImage class’s imageNamed: method. We pass one argument to that method: the name of the image file to be used. We pass that as an NSString object. This will return a UIImage object. (The documentation of UIImage class’s imageNamed: method is shown on the next page.)

In (Step 2), we assign that UIImage object to the image property of imageView, which is, of course, our UIImageView object that we created and connected in Interface Builder (using dot notation, as you would expect). (The documentation of the UIImageView class’s image property is shown on the next page.)

Run your program, and click the Bear button. It should display your Bear picture. page 2.38

9. You will run into an issue if your actual image is not the same size as your imageView. We would like the imageView to scale the image for us. We can set up the imageView to do this, in Interface Builder.

Click on your .xib file, then click on your imageView to highlight it. In the Inspector’s Attributes tab, change the value of the Mode dropdown list to Aspect Fill.

Note: You could do this programmatically if you wanted to, and would type the following line:

imageView.contentMode=UIViewContentModeScaleToFill;

10. Implement your other five methods, and you will have yourself a reasonably entertaining program. page 2.39

For the curious, one might wonder how we could make this program more flexible, and more scaleable. For flexibility, we could have the program get a directory listing of all the .jpg files in our Resources folder, then use all of them. For scaleability, we could display the list of images in a scrolling table (like the iPhone’s Contacts) rather than using a button for each image. The truth is, while this would require some slightly more sophisticated programming, it is not a long journey from here to there.

Don’t forget to upload your project to your mislab account. page 2.40

Project name: ImageViewer4-

ImageViewer4

Pictures are fun.

In this assignment, we will write a program that we can use to display an animated slideshow of a set of pictures. We can use any number of pictures, as they will be set up in an array, and we will step through the elements in the array, fading one image out and fading the next in.

1. Get some image files. The approach used in this program works best if the images all have the same aspect ratio (this is the ratio of the width of the image to its height). Our images were all 320 pixels wide, and 320 pixels tall, which is an aspect ratio of 320:320, which reduces to 1:1. Any resolution which would reduce to 1:1 has the same aspect ratio as any other resolution that reduces to 4:3. Examples are: 640:640, 1024:1024, and 1600:1600.

2. Once you have your image files, open Xcode and create a Single View Application.

3. Open your Supporting Files folder, and drag your image files into the folder.

VERY VERY IMPORTANT: When you drag your files, be sure to check the checkbox to “Copy items into destination group’s folder (if needed)”.

page 2.41

4. We need two UIImageView objects. We will need to operate on those objects – we need to be able to refer to them, so we can change the displayed images. When you need to refer to an object, it needs a name. We should, then, define a variable (just like in Algebra, a variable “represents something”; in our programming, we will define a variable to represent a button, an imageView, a label, or any of the other objects that we will create). We are going to drag the UIImageView objects onto our view in Interface Builder, then program with them in Xcode (in these projects, we frequently speak of Interface Builder as if it were a separate program, which it used to be, but its functionality is now incorporated in Xcode; technically, Interface Builder no longer exists).

In Xcode, the first few lines of your interface file (ViewController.h) should be something like:

#import

@interface ViewController : UIViewController { }

@end

IMPORTANT NOTE: Xcode 4 may not automatically generate the curly braces {} as shown here. If it doesn’t, type them.

Remember, an interface file is used for three things: (1) to define instance variables, (2) to define properties, and (3) to define method headers. We know how and where to define a method header (with one line of code, immediately before the @end, and after the closing curly brace). We define instance variables inside the curly braces.

We could define our variables with the following :

@interface ViewController : UIViewController {

UIImageView *imageView1; UIImageView *imageView2; }

@end

These lines define the data type of the variables – they say that tese variables will represent UIImageView objects – instances of the UIImageView class. They also define the name of each variable. In Objective-C, any time that a variable represents an object, the variable name must be preceded by a * character. Finally, the lines implicitly define the size of the variables (we say implicitly, because we did not explicitly specify a size – the size is automatic, depending on the data type). In any programming language, when you define (or declare) a variable, you define three things: its data type, its name, and its size.

page 2.42

While these lines would work for some variables that we might define, they will not work for these. If we want to make an Interface Builder connection to a variable that we define in Xcode, we need to use the keyword IBOutlet so Interface Builder will recognize the variable. The first few lines of your interface file should actually be something like:

@interface ViewController : UIViewController {

IBOutlet UIImageView *imageView1; IBOutlet UIImageView *imageView2; }

5. We will use an NSTimer object to implement our slideshow. The NSTimer will "fire" every 4.0 seconds, calling a method (that we will write) every time it fires. We need to write two methods: a method that the NSTimer will call – this method will transition to the next image – and a second method that will do some wrap-up work when the transition ends. We need to define each of these methods in our interface file. Our interface file is now:

#import

@interface ViewController : UIViewController {

IBOutlet UIImageView *imageView1; IBOutlet UIImageView *imageView2; }

- (void) nextImage; - (void) finishAnimation;

@end

6. Finally, we will store our image file names in an NSMutableArray. We need to define a variable for the array, and also an integer variable that we will use to keep track of the current image number (the image that is currently displayed). With all of this, our final interface file is:

#import

@interface ViewController : UIViewController {

IBOutlet UIImageView *imageView1; IBOutlet UIImageView *imageView2;

NSMutableArray *filenames;

int imageNumber;

}

- (void) nextImage; - (void) finishAnimation;

@end page 2.43

7. Click on ViewController.xib to open it. Drag two UIImageView objects from the Library onto your view (click View, Utilities, Show Object Library if the Library is not visible).

Your two imageViews should be in the exact same position – one should completely overlap the other. While you can do a good job of aligning objects visually, you can be far more precise by using the Inspector’s Size tab.

You can specify a precise value for the X and Y coordinates of each object, as well as its width and height. We made each of our images precisely 320 pixels wide and 320 pixels tall. We set the top left corner of each image to X-coordinate 0 and Y-coordinate 0.

8. Make your Connections. Connect your your imageView1 variable to your first UIImageView object (connect it to the first object listed in your view stack in the Dock; that imageView is underneath the other one) and connect imageView2 to the other UIImageView object.

9. In your viewDidLoad method (in ViewController.m), you need to instantiate (create an instance) an NSMutableArray object. You can instantiate any object by calling two methods: alloc and init (or some class-appropriate form of init).

filenames=[[NSMutableArray alloc] initWithCapacity:0]; page 2.44

10. We need to populate the array with the names of the files that we dragged into the project. Be very careful, as the file names must be exactly right, including case sensitivity, spaces... exact. Use the addObject: method to add a string (representing the file name) to the NSMutableArray.

[filenames addObject:@"BearAtLunch.jpg"]; [filenames addObject:@"DeerAtAvalancheLake.jpg"]; [filenames addObject:@"MountainGoats.jpg"]; [filenames addObject:@"RainbowOverStMaryLake.jpg"]; [filenames addObject:@"ViewAcrossSwiftcurrentLake.jpg"]; [filenames addObject:@"ViewFromManyGlacierHotel.jpg"];

11. We will start imageView1 displaying the first image in our array (Objective-C arrays are zero-based, meaning that the first element is element #0, not element #1). Use the objectAtIndex: method to retrieve an element from an array. We will take that string and use it to create a UIImage, which we can assign to imageView1.image.

imageNumber=0; NSString *imageFilename=[filenames objectAtIndex:imageNumber]; imageView1.image=[UIImage imageNamed:imageFilename];

Let's look most closely at the last line here. This is, of course, an assignment statement. The first step is to (Step 1) evaluate the righthand side:

[UIImage imageNamed:imageFilename];

Enclosed in square brackets, this code calls the UIImage class’s imageNamed: method. We pass one argument to that method: the name of the image file to be used. We pass that as an NSString object. This will return a UIImage object. (The documentation of UIImage class’s imageNamed: method is shown on the next page.)

In (Step 2), we assign that UIImage object to the image property of imageView1, which is, of course, our "underneath" UIImageView object that we created and connected in Interface Builder (using dot notation, as you would expect). (The documentation of the UIImageView class’s image property is shown on the next page.)

page 2.45

12. You will run into an issue if your actual image is not the same size as your imageView. We would like the imageView to scale the image for us. We can set up the imageView to do this, in Interface Builder.

Click on your .xib file, then click on your imageView to highlight it. In the Inspector’s Attributes tab, change the value of the Mode dropdown list to Aspect Fill.

Note: You could do this programmatically if you wanted to, and would type the following line:

imageView1.contentMode=UIViewContentModeScaleToFill; imageView2.contentMode=UIViewContentModeScaleToFill;

page 2.46

13. We will make imageView2 (which is on top of imageView1) initially transparent.

imageView2.alpha=0.0;

14. Finally, in viewDidLoad, we will create an NSTimer which will fire every 4.0 seconds, calling our nextImage method every time it fires.

[NSTimer scheduledTimerWithTimeInterval:4.0 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];

15. Our entire viewDidLoad method is:

- (void)viewDidLoad { [super viewDidLoad];

filenames=[[NSMutableArray alloc] initWithCapacity:0];

[filenames addObject:@"BearAtLunch.jpg"]; [filenames addObject:@"DeerAtAvalancheLake.jpg"]; [filenames addObject:@"MountainGoats.jpg"]; [filenames addObject:@"RainbowOverStMaryLake.jpg"]; [filenames addObject:@"ViewAcrossSwiftcurrentLake.jpg"]; [filenames addObject:@"ViewFromManyGlacierHotel.jpg"];

imageNumber=0; NSString *imageFilename=[filenames objectAtIndex:imageNumber]; imageView1.image=[UIImage imageNamed:imageFilename];

imageView2.alpha=0.0;

[NSTimer scheduledTimerWithTimeInterval:4.0 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];

}

16. As we know, our nextImage method will be called (by the NSTimer) every 4.0 seconds. This method will transition to the next image. And here is how we will make a smooth transition: imageView2 is currently transparent. We will change its image to the the next image (we won't see the next image, because imageView2 is transparent), and then we will gradually make imageView2 visible by gradually changing its alpha property to 1.0.

We defined an integer variable named imageNumber to keep track of the current image. We will increment imageNumber. It is important to do appropriate bounds checking (checking the boundaries). If we have six images in our array, the elements in the array are elements #0, #1, #2, #3, #4, and #5. There is no element #6. If we neglect to perform our bounds checking and refer to an element #6, we will get an execution error message telling us that we tried to refer to element #6, but the bounds of the array are 0 through 5.

page 2.47

We can increment imageNumber and perform our bounds checking with:

imageNumber++; if(imageNumber==filenames.count) { imageNumber=0; }

17. Retrieve the next file name from our array. Make sure imageView2's alpha is 0.0, and change its image property to the next image.

NSString *imageFilename=[filenames objectAtIndex:imageNumber];

imageView2.alpha=0.0; imageView2.image=[UIImage imageNamed:imageFilename];

18. Finally, use an animation block to gradually change imageView2's alpha to 1.0.

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(finishAnimation)]; [UIView setAnimationDuration:2.0]; imageView2.alpha=1.0; [UIView commitAnimations];

This animation block has one extra feature: it indicates the name of a method to call when this animation ends (stops) – it says that our finishAnimation method should be called at that time. (Note that we must also call the setAnimationDelegate: method, passing self as our argument, indicating the name of an object who owns the method that we are saying to call when this animation stops.)

19. Our last remaining task is to write our finishAnimation method. This method is quite simple. All we have to do is change imageView1's image property to match that of imageView2, and change imageView2's alpha back to 0.0 so it will once again be transparent.

- (void) finishAnimation { imageView1.image=imageView2.image; imageView2.alpha=0.0; }

And with this, we have a pleasing slideshow of our own images.

Don’t forget to upload your project to your mislab account. page 2.48

Project name: TempConverter1-

TempConverter1

This example demonstrates very common GUI event-driven programming. The app includes a textfield for user input, a button that the user clicks to do something, and another textfield for output. In this case, the user will enter a temperature, then click one button to convert from Fahrenheit to Centigrade, or a different button to convert from Centigrade to Fahrenheit.

1. Create a new Single View Application. These instructions will assume that you named your app TempConverter.

Be sure to enter your Company Identifier if you plan to deploy this app to your device or upload to the App Store (we doubt that you could successfully sell this app on the app store, as it will be rather basic). Since we will be using only one view (screen), we do not need Storyboard, and we do not need Unit Tests. Be sure to check the checkbox for Automatic Reference Counting.

page 2.49

2. This program consists of a UITextField object into which the user will enter data, and another UITextField object in which we will display output. Define these two objects in your interface file (ViewController.h). You will also have two buttons which the user will tap to perform temperature conversions. Define a method that will respond to clicking each button; each method will perform one type of conversion.

#import

@interface ViewController : UIViewController { IBOutlet UITextField *input; IBOutlet UITextField *output; }

- (IBAction) toFah; - (IBAction) toCent;

@end

We have already pictured our user interface in our mind. We know that we are going to have a textfield, two buttons, and another textfield. If we want to be able to refer to an object, that object must have a name. We define the variables that we will use to refer to the graphical objects in Xcode – by including the word IBOutlet, Interface Builder will recognize the variable definition as a variable that will be connected to a graphical object. The word IBOutlet really does not mean anything else – it is used by Interface Builder to recognize variables that will be connected to IB objects.

One might wonder, why did we define variables for the two textfields, but not for the two buttons? We will need to refer to the textfields during the execution of the program: we will need to retrieve the value that the user types into the first textfield, and we will need to put some output into the second textfield. To operate on these two objects in these ways, we need to have a way to refer to them. We will not, on the other hand, operate on either button in a similar way. We will not, for instance, change the caption on the button. We won’t change its color. Since we don’t need to refer to the button during the execution of the program, we don’t need a variable that would allow us to refer to it.

page 2.50

We also know (in our minds) that we will want to respond to two events: tapping one button, and tapping the other button. We know that we are going to need to write a method to respond to each of these events. Thus, in our interface file, we have defined the method header for each of these methods. Each of these methods indicates that it returns (IBAction). In reality, the method does not return anything. Similar to IBOutlet, IBAction indicates to Interface Builder that this is a method that will be connected to an IB object. (Responding to an event does not necessarily require that you be able to refer to the source of that event. We can, then, respond to a Touch Down event on a button without having a variable name for that button.)

In summary, since we have defined variables and methods with IBOutlet and IBAction, we are ready to swap over to Interface Builder, drag and drop objects onto our program, then make connections from our already-defined variables and methods to these objects.

3. Click ViewController.xib to open it in the Editor pane.

4. Drag two UIButton objects and two UITextField objects from the Library onto your view.

5. Make your connections. Click on File’s Owner to highlight it. Make sure that File’s Owner is highlighted. Now click the Connections tab (the rightmost tab in the Inspector). You should see your aleady-defined variables and methods, ready to be connected.

In the Inspector, connect your two outlets: connect input to the top UITextField, and output to the other. Make a connection, of course, by mouse-pressing on the small circle to the right of the variable name in the Inspector, then while holding the mouse down, draw a connecting line to the actual textfield. It’s a visible connection – you’ll see it. page 2.51

Note: If one of your variables doesn’t show up under Outlets, make sure that you included the word IBOutlet in the variable definition, and that you spelled the data type correctly. If one of your methods does not show up in the Actions list, make sure that you included IBAction as your return data type.

6. Connect your two actions: connect toFah to one UIButton (select the Touch Down event), and toCent to the other.

7. Click on one of your UIButton objects to highlight it. Click on the Attributes tab (the fourth tab from the left in the Inspector) to display the attributes of the UIButton.

Type F->C as the value of the Title attribute. You will see it show up as the caption of the button. You can also change the fontsize of that title. The example shows a Bold 15pt font as the title. As you look through the properties in the Attributes tab, you will see that a UIButton has many attributes that you can modify (such as Text Color).

Note: One of the nice things about making connections in Interface Builder is that it recognizes the data type of your outlets (variables that you defined in Xcode), your actions (methods that you defined in Xcode), and your graphical objects that you have dragged onto your view. When you are drawing a connection line from a UITextField outlet, for instance, you can draw that connection line only to a graphical UITextField object. When the mouse hovers over a UITextField object, you will see an indication that you are over the correct data type, and you can complete your connection. When you hover over a UIButton or some other object, however, you can not make a connection.

All in all, making these connections is really quite easy, and perhaps even fun. Fun? page 2.52

8. Click on a textfield to highlight it. Click on the Inspector’s Attributes tab to display the attributes of the textfield. Type “Input temperature” as the value of the Placeholder attribute. This will display a light gray prompt for the user, letting the user know what is expected in that textfield. The placeholder will disappear as soon as the user enters anything into the textfield.

You can change the font in a textfield, just as you did for the title on a UIButton.

9. In the Navigator pane, click on ViewController.m to edit the implementation file (the .m file). Define your two methods, just before the @end line.

- (IBAction) toFah { }

- (IBAction) toCent { }

@end

10. Run your program, just to make sure things are working so far (nothing visible will happen when you click the buttons). page 2.53

When you begin typing program code – such as in Xcode – you will almost certainly make mistakes. These mistakes result in three different types of programming errors:

1. syntax error: Syntax means “rules of a language”; a syntax error is a violation of one of these rules. An Objective-C rule of syntax is that every variable that you use in your implementation must be defined. If you forget to define a variable, but use it in your implementation, you will get a syntax error message. If you have even a single syntax error in your program, the IDE will not even begin the execution of your program. Syntax error messages are displayed in red, and you must get rid of them. Xcode will also display warnings, in yellow. A warning may, or may not, in fact lead to an error. If you define a variable, but don’t use it, you will get a yellow warning. That warning will probably not lead to an error. If you call a settitle method, when the method name is actually setTitle, you will get a warning. That warning will lead to an execution error, because there is no method named settitle. Debugging syntax errors is difficult at first, and becomes much easier with experience. Read the error message, and look at the corresponding code very, very carefully.

2. execution error (also called runtime error): This is an error that occurs during the execution of your program. If you call a settitle method, but the method name is actually setTitle, you will get an execution error when you actually try to call settitle during the execution of the program. The Dynamic Debugger a great tool to use when you are debugging execution errors.

3. logic error: These errors are the most difficult to debug, because you don’t get an error message. This is an error in the logic of your implementation. Perhaps you were supposed to increase every number in a list by 1%, but you increased them by 10%. Logic errors are typically recognized by a person who is examining output of the program. The Dynamic Debugger is a great tool to use when you are debugging logic errors.

11. Implement the logic to perform the Fahrenheit-to-Centigrade conversion.

- (IBAction) toCent { float f=[input.text floatValue]; float c=(f-32) * 5 / 9; output.text=[NSString stringWithFormat:@“%.2f”,c]; }

The first line of code (which is an assignment statement) refers to input.text. input, of course, is one of our UITextField objects. The documentation of the UITextField class tells us that every UITextField object has a property named text. The documentation tells us that the data type of that property is NSString – it is the string that the user entered into your input field. The line of code uses that NSString object’s floatValue instance method, which returns a float representation of the NSString. We then assign that float value to the primitive float f.

page 2.54

Note: There are two steps to execute an assignment statement: 1. evaluate the righthand side. 2. assign the evaluated value to the left.

The second line of code uses an arithmetic expression to calculate the centigrade equivalent of the input value.

The third line uses a class method of the NSString class – stringWithFormat: – to create a formatted string (in this case, it creates a string representation that has two digits to the right of the decimal point). It then assigns that string to output.text, thereby displaying the value in the output textfield.

12. Click the Run icon to test your program.

13. See if you can implement the toFah method yourself.

14. Test this program both in the simulator and on your device.

15. If you want, create a 57-by-57 pixel .png file, and use it as your app’s icon file.

16. There are many other things that you could do to improve your program. You can change the text color of the button that was pressed to highlight the one that was pressed. In the toCent method. you could change the text color of the F->C button to green, and the text color of the C->F button to black. In the toFah method, you would do just the opposite. So you could add the following to the toCent method:

[toCentButton setTitleColor:[UIColor greenColor] forState:UIControlStateNormal]; [toFahButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];

Note that this requires an additional change, in your interface file, because you now need to refer to these buttons programmatically (in programming code) during the execution of your program. You would need to define a variable for each button. These variable names refer to the buttons that you created in Interface Builder, so you need to define them with the IBOutlet keyword, then return to Interface Builder to make these two connections.

IBOutlet UIButton *toFahButton; IBOutlet UIButton *toCentButton;

page 2.55

17. Want to prevent the user from typing into the output textfield? You need to operate on that textfield object, by using one of its properties or methods. In this case, it is the enabled property. You want to change its value to No as soon as the program starts – as soon as your view is loaded.

- (void)viewDidLoad { [super viewDidLoad]; output.enabled=NO; }

18. Want to specify a special type of keyboard for your user since the input will be all numeric? Your input textfield has a property named keyboardType. Interestingly, if you look up the documentation of UITextField, you will not see a property named keyboardType, but that documentation lists only the properties that are defined right there in the UITextField class. A UITextField class also has all of the properties that are defined by its superclasses (such as UIControl and UIView) and protocols that UITextField implements. You will see that UITextField “conforms to” a protocol named UITextInputTraits, which does have a property named keyboardType. The point here is that a subclass has properties from a number of different sources, so be sure that you look in all appropriate places for properties and methods. Look in the class’s documentation, but also in the documentation of all of that class’s superclasses and implemented protocols.

The documentation of UITextInputTrait’s keyboardType property is:

@property(nonatomic) UIKeyboardType keyboardType

The data type of this property is UIKeyboardType. The documentation of UIKeyboardType will tell you that it is a C structure, which is similar to a class (similar, but not the same). It has possible values of: UIKeyboardTypeDefault UIKeyboardTypeASCIICapable UIKeyboardTypeNumbersAndPunctuation UIKeyboardTypeURL UIKeyboardTypeNumberPad UIKeyboardTypePhonePad UIKeyboardTypeNamePhonePad UIKeyboardTypeEmailAddress

You could put the following line in your viewDidLoad method:

input.keyboardType=UIKeyboardTypeNumbersAndPunctuation;

19. You will notice that your keyboard comes up automatically when you tap in a textfield. This is because the keyboard is the automatic “first responder” for a UITextField. But the keyboard never goes away! You want it to go away when you tap one of your temperature conversion buttons. Put the following in your toFah method as well as in your toCent method to hide the keyboard:

[input resignFirstResponder]; page 2.56

20. Another idea. Put a label just above the output textfield. When you tap the C->F button, you will change this label to say “Fahrenheit equivalent”. When you tap the F->C button, you will change it to say “Centigrade equivalent”. This will be useful information for the user.

Define a UILabel in your interface file. You will need to connect it to a UILabel object in Interface Builder, so be sure to include the IBOutlet keyword.

IBOutlet UILabel *label;

Swap over to Interface Builder, drag a UILabel object onto your view, and connect your new variable to this new UILabel (click on File’s Owner to highlight it in the Documents window, then select the Connections tab in the Inspector; draw your connection).

Back in Xcode, in your implementation file, change your label’s text property in each method. In the toFah method:

label.text=@”Fahrenheit equivalent”;

In the toCent method:

label.text=@”Centigrade equivalent”;

22. One final idea. As it is now, when the user does a conversion, you change the text color of one button to green, and change the label (in the previous step) to indicate which conversion was performed. It might be nice to do the following: as soon as the user taps in the input textfield to enter a number for the next conversion, change the text color of the button back to black, blank out the label, and clear the output textfield so there are no “remnants” of the previous conversion still on the screen.

To do this, we need to respond to tapping in the UITextField. The UITextField class has a corresponding protocol that serves as its delegate – the delegate does things on behalf of the UITextField class. You can implement that protocol to provide your own functionality that you want in response to selected events. The UITextFieldDelegate protocol will respond to all appropriate events, in pre-defined ways, but you can over-ride any of that protocol’s methods that you choose, to provide your own functionality for specific events.

There are three steps to implement a protocol: 1. Indicate that you are going to implement the protocol. Do this by putting the name of the protocol in angle brackets in your @interface directive, in your .h file. 2. Tell the appropriate object (in this case, the input textfield) that you will be its delegate. 3. Over-ride any desired methods that are defined by the protocol.

Step 1: Indicate that you are going to implement the UITextFieldDelegate protocol by putting the name of the protocol in angle brackets, in the interface file, at the end of the @interface directive:

@interface TempConverter1ViewController : UIViewController {

page 2.57

Step 2: Tell the input textfield that you will be its delegate. We need to do this once, when the app first starts. A natural place to do this would be in our viewDidLoad method. input.delegate=self;

Step 3: Over-ride any desired methods (to implement the specific functionality that you desire). If you look up the documentation of the UITextFieldDelegate protocol, you will see that it defines seven methods: textField:shouldChangeCharactersInRange:replacementString: textFieldDidBeginEditing: textFieldDidEndEditing: textFieldDidEndEditing: textFieldShouldBeginEditing: textFieldShouldEndEditing: textFieldShouldEndEditing:

The names of these methods pretty much self-describe the event to which each of them will respond. textFieldShouldBeginEditing, for instance, is called “when the user performs an action that would normally initiate an editing session” (quoted from the documentation of the method). This method will be called, for instance, when the user taps in the textfield to start typing.

The documentation of each of these methods says that “this method is optional”. There is already a default implementation of each method that does what it is supposed to do, but you can over-ride that default implementation to provide your own specific functionality.

We want to over-ride the default textFieldShouldBeginEditing method. To do this, we write our own textFieldShouldBeginEditing method. We must match the signature of the method, which consists of its name, its parameter list, and its return data type. Typically, you will copy/paste the method header from the documentation into your code.

- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField and now provide the functionality that we want for the method. When the user taps in the input textfield, we want to: - change the text color of each button to black - blank out the label that we created - blank out the value that is in the output textfield from the previous conversion

We also need to do one important thing. The default implementation of this method does one important thing: it returns a value of YES to indicate that the textfield should indeed start editing. We can return either YES to start editing, or NO if we don’t want to start editing. In some program, we may have a situation that we want to start editing only if some condition is true – we would use this capability to implement that. In this case, we want to return a YES, and start editing.

page 2.58

- (BOOL) textFieldShouldBeginEditing:(UITextField *)textField { [toFahButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [toCentButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; label.text=@””; output.text=@””;

return YES; }

Congratulations, you could now write many different types of basic programs for your device!

page 2.59

Project name: TempConverter2-

TempConverter2

This project builds on TempConverter1 (meaning, you need to complete that project before you do this one), adding a bar at the top of the screen with two Clear buttons. The two Clear buttons basically do the same thing – they reset the form to its initial state – but they do it in two different ways.

The first Clear button uses a UIAlertView object (a UIAlertView object is an instance of the UIAlertView class; it is a pop-up alert that you see in many applications). The basic steps to use a UIAlertView are: 1. create the object 2. show it

You can have one or more buttons on a UIAlertView. If you have only one, the programming is fairly simple because you don’t need to process the user’s response – you know which button the user tapped to clear the alert, because there was only one button.

If your UIAlertView has more than one button, you will need to implement the UIAlertViewDelegate protocol. Specifically, you will need to over-ride the alertView:clickedButtonAtIndex: method to determine which button the user tapped, and respond accordingly.

The other Clear button in this project will use a UIActionSheet.

page 2.60

1. Open your Temperature Converter project.

2. Define two method headers in your interface file (one method for each Clear button):

- (IBAction) alertClear; - (IBAction) actionClear;

3. Click the nib file to open it in the Editor.

4. Drag a UINavigationBar from the Library onto the top of your view. Change the value of its Title property (in the Attributes tab of the Inspector) to TempConverter2.

5. Drag a UIBarButtonItem onto the left side of your Navigation Bar. You can put only two UIBarButtonItems on a UINavigationBar – one on the left, and/or one on the right. They will go into pre-defined locations.

Change the value of the Title property of the UIBarButtonItem object to Clear.

6. Drag a UIBarButtonItem onto the right side of your Navigation Bar. Change the value of the Title property of the UIBarButtonItem object to Clear.

7. Click File’s Owner (in the Dock) to highlight it, then the Connections tab in the Inspector. Connect your two actions (alertClear and actionClear) to your left and right Clear buttons. page 2.61

Is the use of Xcode becoming more clear? You define variables and methods in your .h file, then connect them to graphical objects via connections.

8. You need to actually implement your two new methods (alertClear and actionClear). You need to instantiate (a sophisticated word meaning to create an instance) a UIAlertView object (an instance of the UIAlertview class). Instantiation in Objective-C typically involves two steps: allocate memory for an object, and initialize it. This is typically done with two methods: alloc and init (or some form of init). This could be done in three lines, all in your implementation file. In this case:

UIAlertView *alert;

This line defines a variable named alert, whose data type is UIAlertView. It currently has a value of nil, but it will eventually “point to” a UIAlertView object.

alert=[UIAlertView alloc];

This assignment statement (1) calls a class method named alloc to allocate memory for a UIAlertView object, then (2) assigns the memory address of that object as the value of the variable named alert. The variable alert no longer has a value of nil. (The alloc method is inherited from the NSObject class. Since NSObject is the superclass of all classes, every subclass – indeed, every class – has an alloc method.)

[alert initWithTitle:@"" message:@"Are you sure you want to clear the textfields?" delegate:self cancelButtonTitle:nil otherButtonTitles:@”No”,@"Yes",nil];

This line calls an instance method defined in the UIAlertView class. The method is named initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:, and it is the way that you initialize a UIAlertView object, passing appropriate values to the method’s parameters.

You pass five arguments:

title: an optional short message that will be displayed in slightly larger, bold font at the top of the alert

message: the message to be displayed in the body of the alert

delegate: If your alert will have only one button, you don’t need a delegate to process the user’s button selection, so you can pass “nil”. If you are going to have more than one button on the alert, and need to process the selected button, you need to implement the UIAlertViewDelegate protocol, and pass “self” as the delegate here.

cancelButtonTitle: the label that you want to display on the cancel button. There is nothing special about the cancel button. It’s just a button. You can pass “nil” here and pass your desired button caption in the next argument if you wish; there is no difference.

otherButtonTitles: You can have multiple buttons on the alert. In this argument, you pass a comma-separated list of titles for the remaining buttons. The list must end with “nil”.

page 2.62

While this can be done in the three lines of code that we just discussed, it is usually done in one (we usually type this as one long line, not the three physical lines that are shown here):

UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:@"Are you sure you want to clear the textfields?" delegate:self cancelButtonTitle:nil otherButtonTitles:@”No”,@"Yes",nil];

Once you instantiate your UIAlertView, call its show instance method to make it visible. The entire method, then, becomes:

- (IBAction) alertClear // display a UIAlertView for confirmation { UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:@"Are you sure you want to clear the textfields?" delegate:self cancelButtonTitle:nil otherButtonTitles:@”No”,@"Yes",nil]; [alert show]; }

Our program will not quite run yet.

9. In this case, we have more than one button on the UIAlertView. We want to respond to the buttons differently – if the user taps No, we do nothing; if the user taps Yes, we reset the form. To process a UIAlertView’s buttons, we need to implement the UIAlertViewDelegate protocol – we need to be the UIAlertView’s delegate.

To implement the protocol, specify the name of the protocol in angle brackets, on the @interface directive. Although you can subclass only one class (Objective-C supports single inheritance only), you can implement any number of protocols. Implementing a protocol, in reality, is very much like subclassing a class, in that each one establishes an “is a” relationship. Many languages support single inheritance only, and use something similar to protocols to allow multiple “is a” relationships.

We are already implementing the UITextFieldDelegate protocol from project #003a. We are going to add UIAlertViewDelegate now (this is typically typed on one long line).

@interface TempConverterViewController : UIViewController {

Our class, TempConverterViewController, subclasses UIViewController, and implements the UITextFieldDelegate and UIAlertViewDelegate protocols. Our TempConverterViewController object “is a” UIViewController, it “is a” UITextFieldDelegate, and it “is a” UIAlertViewDelegate. This demonstrates the object- oriented programming concept of polymorphism, wherein an object can “be” different things, depending on the context in which it is used.

Note that in the following line, we passed “self” to the delegate parameter. “self” is a keyword that refers to the “current object”, which is our ViewController. We can pass “self” here only because our object “is a” UIAlertViewDelegate. If we had not implemented the UIAlertViewDelegate protocol, we could not have passed “self” to this parameter.

page 2.63

UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:@"Are you sure you want to clear the textfields?" delegate:self cancelButtonTitle:nil otherButtonTitles:@”No”,@"Yes",nil];

As we did with UITextFieldDelegate, we should look at the documentation of the UIAlertViewDelegate protocol. It defines six instance methods, all of which are optional. We over-ride only the ones for which we want to provide specific functionality. In this case, we want to over-ride only one, so we can process the selected button. We want to over-ride UIAlertViewDelegate’s alertView:clickedButtonAtIndex: method. To do this, we write our own method, matching the protocol method’s signature, probably by copy/pasting the method header from the documentation into our program.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

The buttons on a UIAlertView are numbered, from left to right, starting at 0. In our case, the No button is button #0, and the Yes button is button #1. (If you specify a cancelButtonTitle, that button will be on the left, and it will be button #0; if you do not specify a cancelButtonTitle, the first OtherButtonTitle will be on the left, and it will be button #0). This method receives a parameter that tells us which button was tapped. We can use the buttonIndex variable in an if statement, then process correctly.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if(buttonIndex==1) // if user clicked button #1 (the Yes button) { input.text=@""; // clear both textfields and the label output.text=@""; label.text=@””; // then reset button colors to black [toCentButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [toFahButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; } }

You will see one other thing in code examples. For documentation purposes, you can put some comments in your code that indicate that these methods over-ride those in a protocol. You will frequently see something like:

#pragma mark - #pragma mark UIAlertView delegate methods just above this method. This is entirely optional, but possibly useful. In Xcode, you can get a dropdown list of all of the methods in your implementation file. When you have a long program, with a lot of methods, you can use this list to more quickly find the method that you need, and go to it. These pragma comments put an indication in that dropdown list that these are your protocol over-ride methods, as is shown below.

page 2.64

10. Test your alert.

11. If you have more than two or three buttons, you might want to use a UIActionSheet, which slides up from the bottom of the screen rather than popping up in the center.

The basic steps to use a UIActionSheet are much like those with a UIAlertView. One difference is that the UIActionSheet is displayed in relation to some other view, which you specify. The basic steps are: - alloc and init the UIActionSheet - show it (this time, specify a view in relation to which the action sheet is to be shown)

- (IBAction) actionClear // display a UIActionSheet for confirmation { UIActionSheet *actionSheet=[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to clear the textfields?" delegate:self cancelButtonTitle:@"No" destructiveButtonTitle:@"Yes" otherButtonTitles:nil]; [actionSheet showInView:self.view]; }

Notice that in this init method, there is no “message”, but only a “title”. This title is displayed in rather small text, on the action sheet. There is a “cancelButtonTitle” parameter, which does nothing special, and also a “destructiveButtonTitle” parameter. While you do not have to use either of these parameters, and could specify all of your button titles with the “otherButtonTitles” parameter, if you do specify a destructiveButtonTitle, that button will be displayed with a red background.

As was true with UIAlertView, if we want to process an action sheet’s buttons, we need a delegate, this time UIActionSheetDelegate. As is true in implementing any protocol, we start at the @interface directive. Adding this third delegate to our list, we have:

@interface TempConverter2ViewController : UIViewController {

And we implement the logic to do what we want to do in response to tapping “the destructive button”:

#pragma mark - #pragma mark UIActionSheet delegate methods

- (void) actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)index { if(buttonIndex==actionSheet.destructiveButtonIndex) // if user clicked destructiveButton { input.text=@""; // clear textfields and label output.text=@""; label.text=@""; // then reset button colors to black [toCentButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [toFahButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; } }

page 2.65

The numbering of a UIActionSheet’s buttons is a bit more complex than are those of a UIAlertView. UIActionSheet, then, supplies some properties that you can use in your if statements, to compare to index. Note that this delegate method receives two parameters: actionSheet and index. actionSheet is a reference to the UIActionSheet object, and index is the index number of the button that the user tapped. actionSheet has three useful properties in processing index: actionSheet.cancelButtonIndex actionSheet.destructiveButtonIndex actionSheet.firstOtherButtonIndex

You can compare index to any of these, as the example code did, to determine if the user tapped the cancelButton, the destructiveButton, or one of your otherButtons.

A UIActionSheet will slide up from the bottom of the screen – it is animated, and shows that animation, rather than immediately popping onto the screen. It provides a nice visual effect to the user. It also provides a way that you can display more than two or three buttons to the user. A cancelButton will always be displayed on the bottom of the list. A destructiveButton will alway be displayed on the top of the list, highlighted in red. You can display up to eight buttons, although that limits your title to only one line.

You can use UIAlertView and/or UIActionSheet to display quick information to the user. They are particularly useful because they so quickly grab the user’s attention. They are modal, meaning that they take total control of the user interface. The user must tap a button to dismiss the UIAlertView or UIActionSheet, and return to the main app.

page 2.66

Project name: Validator-

Validator

Regular Expressions are a tremendous data validation tool. Specifically, they allow you great power in performing pattern matching. Any time that you ask the user for free-form input, such as for an email address or a phone number, regular expressions are a great tool in gaining some assurance that the user entered valid input. It is important to understand that regular expressions are not used to ensure that the user entered the correct value, but that he or she entered something that is of the correct format. With regular expressions, you can ensure that the user entered something that looks like a valid email address or phone number, but you do not actually know that it is the user’s actual email address or phone number (doing that typically requires that you match the user’s input against some sort of list of actual values).

1. Create a Single View Application.

2. Design your user interface. The example to the right uses: - one UITextField - one UIButton - nine UILabels down the left side of the screen - four UILabels for the first four rows - five UISwitch objects for the next five rows

3. We will not refer to the nine labels along the left side, and thus will not need variables for them. In your .f file, define a variable for: - the textfield - the four labels on the right side of the first four rows - the five switches.

page 2.67

4. Define a method to be called when the user taps the small button.

Now, your interface file should look something like:

#import

@interface ViewController : UIViewController {

IBOutlet UITextField *textfield;

IBOutlet UILabel *labelLength; IBOutlet UILabel *labelCompactedLength; IBOutlet UILabel *labelWords; IBOutlet UILabel *labelCapitalizedWords;

IBOutlet UISwitch *switchSSN; IBOutlet UISwitch *switchLocalPhone; IBOutlet UISwitch *switchUSPhone; IBOutlet UISwitch *switchPassword; IBOutlet UISwitch *switchEmail;

}

- (IBAction) validate;

@end

5. Open your .xib file, and make your connections.

page 2.68

6. This project uses the third party RegexKitLite class, which is included in the project, and widely available on the world wide web. Drag the two files RegexKitLite.h and RegexKitLite.m into your project. Be sure to check the checkbox to actually copy the files – don’t just create a link to them.

7. Add an import statement just before your @interface header.

#import “RegexKitLite.h”

8. The RegexKitLite class uses International Components for Unicode library, which is already available to you, but is not linked into your project by default. We need to add this linking option.

In the Navigator panel, click on your project name to reveal the project summary information. Click the Build Settings tab. Scroll down to the Linking section. For the item Other Linker Flags, for both the Debug and Release settings, set the value to -licucore

a dash, the letter l, the letters icu, and the letters core, with no spaces

You will need to set this value any time that you use the RegexKitLite class in a project. page 2.69

9. As of the current version (4.0), RegexKitLite is not compatible with iOS 5’s automatic reference checking (ARC). We need to tell the compiler to not implement ARC on the RegexKitLite code.

Click on the Build Phases tab, then open up Compile Sources. Click on the Compiler Flags segment on the RegexKitLite.m file to change its value, then enter the following into the pop-up text area:

-fno-objc-arc

This will tell the compiler to not implement ARC on this file. You will need to set this compiler flag on this file any time that you use RegexKitLite in your app.

10. We are ready to program. Let’s write the validate method, in your implementation file. Let’s start with something rather easy, just to make sure that we can in fact respond to tapping the Go button (you did make that connection, right?).

Create a validate method that doesn’t do anything.

- (IBAction) validate { }

Run your program. When you tap in the textfield, the keyboard should appear. When you tap the Go button, nothing appears to happen (although, hopefully, your do-nothing validate method is called).

11. Let’s start by dismissing the keyboard when you tap the Go button. Add one line in your validate method.

[textfield resignFirstResponder];

12. Run your program. Now we at least have visible evidence that our method is being called, because we see the keyboard disappear.

page 2.70

13. Let’s continue by getting rid of extraneous spaces in the user’s input. In most cases, we don’t really want leading spaces (spaces before the first non-space character; that is, we don’t want the user to type their name as space, space, space, Bear Pearson). We also typically don’t want trailing spaces (spaces after the last non-space character, such as Bear Pearson, space, space, space). Finally, we typically don’t want multiple consecutive spaces. We can use Regular Expressions to handle each of these situations.

First, let’s talk some about regular expressions. They are used for pattern matching, wherein we design a pattern, then compare a string to that pattern. Regular expressions utilize special symbols – a complete table to the symbols is included at the end of this document.

Two very important symbols are ^ (which represents “beginning of the string”) and $ (which represents “end of string”). We could actually use these two to see if a string contained zero characters. Assuming the string is in a variable named check, we could:

if([check isMatchedByRegex:@"^$"])

Here, we use the isMatchedByRegex method, which comes from the RegexKitLite class. We pass that method an NSString, which represents our pattern. Here, we are (obviously) asking, “is check matched by this regular expression?”

We recommend that you express this question to yourself in more detail, and say:

Does check match the pattern: beginning of string, followed by end of string?

Clearly, if the beginning of the string is immediately followed by the end of the string, then string contains no characters.

Another useful regular expression symbol is to enclose a set of characters inside square brackets, such as [abc] or [abc123]. This creates a character set. If we want the user to type a single character, either a, b, or c, we could:

if([check isMatchedByRegex:@"^[abc]$"])

Does check match the pattern: beginning of string, followed by a single character from the character set, followed by end of string? The answer is Yes if, and only if, the user types a single character, a, b, or c.

We could ask a much different question by omitting the ^ and/or $ anchors. If we want to know if the input begins with a letter a, b, or c, but can then have anything after than, we would:

if([check isMatchedByRegex:@"^[abc]"])

page 2.71

Does check match the pattern: beginning of string, followed by a single character from the character set? We have not specified any restriction on what follows that single letter, so anything could follow the single letter.

We could check to see if the input ends with a letter a, b, or c.

if([check isMatchedByRegex:@"[abc]$"])

Does check match the pattern: a single character from the character set, followed by end of string? We have not specified any restriction on what precedes that single letter, so anything could come before the single letter.

Finally, we could check to see simply if a letter a, b, or c appears anywhere in the string.

if([check isMatchedByRegex:@"[abc]"])

Anything could come before and/or after the single letter.

The + and * symbols are quite handy, representing “one or more occurences of the preceding symbol” and “zero or more occurrences of the preceding character. You might combine this with another character set feature, which allows you to specify a range of characters. To check to see if the string consists of only lower-case letters,

if([check isMatchedByRegex:@"^[a-z]+$"])

Does check match the pattern: beginning of string, followed by one or more characters from the character set, followed by end of string? The user must type at least a single letter, and may type more than one, in any order. Note that we we had used * instead of +, the user could have typed zero or more letters, which would have allowed no input altogether.

14. Let’s use this knowledge so far to get rid of unwanted spaces. We can get rid of leading spaces with:

check=[check stringByReplacingOccurrencesOfRegex:@"^ +" withString:@""];

This will replace “beginning of string, followed by one or more spaces” with a null string, thereby removing any and all leading spaces.

Similarly we can remove any and all trailing spaces:

check=[check stringByReplacingOccurrencesOfRegex:@" +$" withString:@""];

This will replace “one or more spaces, followed by end of string” with a null string. Finally, compact all occurences of multiple consecutive spaces into a single space:

check=[check stringByReplacingOccurrencesOfRegex:@" +" withString:@" "];

page 2.72

One thing that an iPhone does automatically when you type is that if you type multiple spaces, it inserts an automatic period (this is usually quite handy). If we want to remove that inserted period, we can:

check=[check stringByReplacingOccurrencesOfRegex:@"\\. " withString:@" "];

In a regular expression, the period is a special symbol, which matches “any character”. In this case, we don’t want the period to match “any character” – we really want it to match only a period. Thus, we escape the period, by preceding it with a backslash character. In Objective-C, we need to use two backslash characters. So this pattern is matched by: a period, followed by a space. The statement replaces any match with a space only.

We could put all this together, as well as display a bit of output, with the following:

- (IBAction) validate { [textfield resignFirstResponder];

NSString *check=textfield.text;

labelLength.text=[NSString stringWithFormat:@"%d",[check length]];

check=[check stringByReplacingOccurrencesOfRegex:@"^ +" withString:@""]; check=[check stringByReplacingOccurrencesOfRegex:@" +$" withString:@""]; check=[check stringByReplacingOccurrencesOfRegex:@" +" withString:@" "]; check=[check stringByReplacingOccurrencesOfRegex:@"\\. " withString:@" "];

textfield.text=check; labelCompactedLength.text=[NSString stringWithFormat:@"%d",[check length]]; }

This will: - dismiss the keyboard - retrieve the string from the textfield - display the length of the string in the righthand label on the top row - remove leading spaces - remove trailing spaces - compact any consecutive spaces to one - eliminate any period that is immediately before a space - replace the contents of the textfield with the modified string - display the length of the compacted string in the righthand label on the second row

Whew!

15. Because of our cleanup efforts, removing all of the extraneous spaces, it will now be easy to separate the user’s input into words. Simply break the string apart, into an array, using a single space as the delimiter character. This doesn’t even require a regular expression.

NSArray *words=[check componentsSeparatedByString:@" "]; labelWords.text=[NSString stringWithFormat:@"%d",[words count]];

page 2.73

16. Now step through the array, checking to see if each word begins with an upper-case letter. First, create an integer variable that you can use to count the capitalized words. As you step through the array, if a word begins with an upper-case letter, increment that counter variable. When you finish stepping through the array elements, display that count.

NSInteger capitalized=0; for(NSInteger i=0;i<[words count];i++) { NSString *word=[words objectAtIndex:i]; if([word isMatchedByRegex:@"^[A-Z]"]) { capitalized++; } } labelCapitalizedWords.text=[NSString stringWithFormat:@"%d",capitalized];

17. Let’s see if the input appears to be a social security number. We will support two formats: either nine consecutive digits, or three digits, a dash, two digits, a dash, and four digits.

The curly braces will be very handy here. They offer three options: {m} will match exactly m instances of the previous symbol, so [0-9]{3} will match exactly three digits

{m,} will match m or more instances of the previous symbol

{m,n} will match m or more, but not more than n, instances of the previous symbol

We will set a flag variable to NO, then set it to YES if we find a match. After our validation checks, we use that flag variable to turn the corresponding UISwitch either on or off.

BOOL flag=NO; if([check isMatchedByRegex:@"^[0-9]{9}$"]) { flag=YES; } if([check isMatchedByRegex:@"^[0-9]{3}-[0-9]{2}-[0-9]{4}$"]) { flag=YES; } [switchSSN setOn:flag animated:NO];

18. We perform a very similar check for a validly formatted local phone number. We will allow seven consecutive digits, or three digits, a dash, and four digits.

flag=NO; if([check isMatchedByRegex:@"^[0-9]{7}$"]) { flag=YES; } if([check isMatchedByRegex:@"^[0-9]{3}-[0-9]{4}$"]) { flag=YES; } [switchLocalPhone setOn:flag animated:NO];

page 2.74

19. As you would expect, we perform a similar check for a ten-digit U.S. phone number.

flag=NO; if([check isMatchedByRegex:@"^[0-9]{10}$"]) { flag=YES; } if([check isMatchedByRegex:@"^[0-9]{3}-[0-9]{3}-[0-9]{4}$"]) { flag=YES; } [switchUSPhone setOn:flag animated:NO];

20. In the previous examples, it was easier to assume that the phone number was invalid (we initialized the flag to NO), then set the flag to YES if we found a valid match. In checking for a secure password, we know things that make the password invalid, so we will use somewhat opposite logic. In this example, our restrictions are that the password: - must be at least 6 characters long - must contain at least one upper-case letter - must contains at least one digit

flag=YES; if([check length]<6) { flag=NO; } if(![check isMatchedByRegex:@"[A-Z]"]) { flag=NO; } if(![check isMatchedByRegex:@"[0-9]"]) { flag=NO; } [switchPassword setOn:flag animated:NO];

Note the ! in the relational expressions. This is the negation operator, and changes the operation of this statement to “is not matched by regex”. In this case, if there is NOT a letter A-Z anywhere in the string (note that neither anchor is used in this pattern; we are checking to see if there is an upper-case letter anywhere in the string), we change the value of the flag to NO. We do the same if the string does not contain a digit.

21. Finally (and by far the most complex), we check to see if the input appears to be a validly formatted email address. The regular expression is complex. Each component of an email address consists of one or more characters from the following character set: a-zA-Z0-9_- (that is, lower-case letters, upper-case letters, digits, underscore, and dash). To match a single component of an email address, then, we could use:

^[a-zA-Z0-9_\\-]+

To the left of the @ in the email address, we might find zero or more similar components, each following a period. We can validate everything to the left of the @ sign with:

^[a-zA-Z0-9_\\-]+(\\.[a-zA-Z0-9_\\-]+)*

This matches one component, followed by 0 or more “period followed by component” sets.

page 2.75

This is, of course, followed by an @ sign, then followed by similar components. To the right of the @ sign, we will have a component, followed by one or more components (not zero or more, as was true to the left of the @ sign). The entire set of code, then, is: flag=NO; if([check isMatchedByRegex: @"^[a-zA-Z0-9_\\-]+(\\.[a-zA-Z0-9_\\-]+)*@[a-zA-Z0-9_\\-]+(\\.[a-zA-Z0-9_\\-]+)+$"]) { flag=YES; } [switchEmail setOn:flag animated:NO];

The more you work with regular expressions, the more clear they will become. You will find all sorts of places to use them, and they can save you many hours of otherwise tedious character manipulation programming.

page 2.76

Special characters in regular expressions

^ Matches beginning of the string.

/^A/ matches: beginning of string, followed by an A (then followed by anything else, including nothing) $ Matches end of the string.

/t$/ matches: (anything, followed by) t, followed by end of string. * Matches the preceding character zero or more times.

/bo*/ matches b, followed by zero or more o's (the letter o). The "bo*" may be anywhere in the string, since the regular expression is not anchored by either a ^ or $.

/^bo*/ would match a "bo*" string at the beginning of the string only.

/bo*$/ would match a "bo*" string at the end of the string only.

/^bo*$/ would match only: beginning of string, followed by a single b, followed by zero or more o's, followed by end of string. + Matches the preceding character one or more times (similar to *, except that + requires at least one occurrence of the preceding character).

/a+/ matches the 'a' in "candy" and all the a's in "caaaaaaandy." ? Matches the preceding character 0 or 1 time (only).

/e?le?/ matches the 'el' in "angel" and the 'le' in "angle." \ Used to "escape" a character that is normally treated specially in a regular expression.

For example, + is a special character that means one or more occurrences of the preceding character.

/ro+/ matches r, followed by one or more o's.

If you actually want to match an r, followed by an o, followed by a plus sign, you need to "escape" the plus sign so that it does not have special meaning in the regular expression:

/ro\+/ . Matches any single character except the newline character. page 2.77

[xyz] A character set. Matches any one of the enclosed characters. You can specify a range of characters by using a hyphen.

[abcd] is the same as [a-d].

/^[A-Z]+$/ matches: beginning of string, followed by one or more upper-case alphabetic characters, followed by end of string.

/^[A-Z][a-z]+$/ matches: beginning of string, followed by one upper-case alphabetic character, followed by one or more lower-case alphabetic characters, followed by end of string. [^xyz] A negated or complemented character set; matches anything that is not enclosed in the brackets. You can specify a range of characters by using a hyphen.

[^abc] will match any character other than a, b, or c. x|y Matches either 'x' or 'y'.

/green|red/ matches 'green' in "green apple" and 'red' in "red apple." {n} Where n is a positive integer. Matches exactly n occurrences of the preceding character.

/a{2}/ matches the two a's in "caandy," and the first two a's in "caaandy", but not the single a in "candy" {n,} Where n is a positive integer. Matches n or more occurrences of the preceding character.

/a{2,}/ matches all of the a's in "caandy" and in "caaaaaaandy", but does not match "candy" {n,m} Where n and m are positive integers. Matches at least n and at most m occurrences of the preceding character.

For example, /a{1,3}/ matches nothing in "cndy", the 'a' in "candy," the first two a's in "caandy," and the first three a's in "caaaaaaandy" Notice that when matching "caaaaaaandy", the match is "aaa", even though the original string had more a's in it. \d Matches a digit character. Equivalent to [0-9].

/\d/ or /[0-9]/ matches '2' in "B2 is the suite number."

/^\d\d\d-\d\d-\d\d\d\d$/ would match a social security number pattern (anchored by beginning and end of string)

/\d\d\d-\d\d-\d\d\d\d/ would match a social security number pattern anywhere in a string (since the regular expression is not anchored on either end) \D Matches any non-digit character. Equivalent to [^0-9]. \w Matches any alphanumeric character including the underscore. Equivalent to [A-Za-z0-9_].

/\w/ matches a in "apple", 5 in "$5.28", and 3 in "3D." page 2.78

\W Matches any non-word character. Equivalent to [^A-Za-z0-9_].

/\W/ matches % in "50%." \b Matches a word boundary, such as a space or a newline character.

/\byes/ matches the ' yes' in "possibly yesterday." \B Matches a non-word boundary. \s Matches a single white space character (form feed, line feed, carriage return, tab, vertical tab, or space character).

For example, /\s\w*/ matches ' bar' in "foo bar" \S Matches a single character other than white space. \f Matches a form-feed character. \n Matches a line-feed character. \r Matches a carriage return character. \t Matches a tab character. \v Matches a vertical tab character. [\b] Matches a backspace character. \cX Where X is a control character. Matches a control character in a string.

/\cM/ matches control-M in a string. \o# Where # is an octal value. Allows you to embed ASCII codes into regular expressions.

\x# Where # is a hexadecimal value. Allows you to embed ASCII codes into regular expressions.

(x) Matches 'x' and remembers the match.

For example, /(foo)/ matches and remembers 'foo' in "foo bar." The matched substring can be recalled from the resulting array's elements [1], ..., [n], or from the predefined RegExp object's properties $1, ..., $9. \n Where n is a positive integer. A back reference to the last substring matching the n parenthetical in the regular expression (counting left parentheses).

For example, /apple(,)\sorange\1/ matches 'apple, orange,' in "apple, orange, cherry, peach."

Note: If the number of left parentheses is less than the number specified in \n, the \n is taken as an octal escape.

page 2.79

Project name: Stopwatch-

Persistent Stopwatch

This project implements a persistent stopwatch, one that appears to continue running even when the application terminates (or even when the device completely loses batter power!). This project writes a numeric start time into a file stored in the Documents folder. Any data stored in such a file is persistent, in that it is not lost when the application terminates. When the app re-starts, it can read that data, and resume operations.

1. Create a Single View Application. Name your application Stopwatch.

2. We will use three UIButton objects, as shown in the screen capture. The top button will be used to display the “running clock”. Since we will change the text on this button, we will need to be able to refer to it. We will also need to be able to refer to the middle button, since we will change its text from Start to Stop. We will not need to refer to the Reset button, as it will always say Reset. Thus, we need to define two variables in our interface file:

IBOutlet UIButton *clock; IBOutlet UIButton *button;

3. We will respond to two events: tapping the Start/Stop button, and tapping the Reset button. Thus, we will define two method headers in our interface file:

- (IBAction) tap; - (IBAction) reset; 4. Click on ViewController.xib to open it in the Editor pane. Make your connections for your two Outlets and your two Actions.

5. As has already been mentioned, we will implement the persistent part of the stopwatch by writing some data into a Documents file. Drag the two files UtilityFileIO.h and UtilityFileIO.m into the Classes folder of this project. Be sure to check the checkbox to actually copy the files – don’t just create links to them in the other project.

6. Import UtilityFileIO’s header file into your interface file:

#import "UtilityFileIO.h"

page 2.80

7. Now, let’s discuss how we can implement a running clock. Well, we can’t really, but we can make it appear that we can. The key is the NSTimer class. An NSTimer object waits for a specified time interval to pass, then calls a pre-specified method when that time interval passes. We can create an NSTimer object to, for instance, call a method named showClock every .01 seconds. As a matter of fact, we can do that with one straightforward line of code:

[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES];

We pass this class method of the NSTimer class five arguments: - the desired time interval - an object whose method we will call (typically we will pass self, and call a method in the current class). - the name of the method to be called - nil - an indication as to whether the specified method is to be called only once (pass NO) or repeatedly (pass YES)

One other note here: if you want to ability to stop the timer at some point, you will need a way to refer to it. In our stopwatch, we certainly do want to stop the timer, so we will define a variable in our interface file, then use that variable in our implementation file. In our interface file:

NSTimer *timer;

and in our implementation file:

- (IBAction) tap { timer=[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES]; }

8. The showClock method will be called every .01 seconds. First, let’s see if we can create a simple sequential counter that will count 1, 2, 3... every .01 seconds. In your interface file, define an integer variable.

NSInteger counter;

9. Create a showClock method that increments this variable, then displays it on your top button.

- (void) showClock { counter++; [clockButton setTitle:[NSString stringWithFormat:@”%d”,counter] forState:UIControlStateNormal]; }

Run your program in the Simulator. When you click the Start button, you should see your counter counting. Rapidly.

page 2.81

10. Let’s implement some logic so that you can stop the counter. We will need a Boolean variable that will indicate to us whether the counter is running or not. We will change its value to YES and NO as needed. Define the variable in your interface file.

BOOL clockIsRunning;

11. In your viewDidLoad method, initialize the value of this variable to NO, indicating (to our code) that the clock is initially not running.

clockIsRunning=NO;

12. We need a fairly big change in our tap method. At this time, when the user taps the Start/Stop button, we start the clock running. But what we really want to do is start OR stop the clock, depending on whether it is already running or not. The logic is:

If the clock is running, stop it; if the clock is not running, start it.

- (IBAction) tap { if(clockIsRunning) { [timer invalidate]; clockIsRunning=NO; } else { clockIsRunning=YES; timer=[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES]; } }

Run your program in the Simulator. You can both start and stop your clock now.

page 2.82

13. We should change the label on the Start/Stop button so that it is clear that sometimes it starts the clock, and sometimes it stops it. We will do that in our tap method.

- (IBAction) tap { if(clockIsRunning) { [timer invalidate]; clockIsRunning=NO; [button setTitle:@”Start” forState:UIControlStateNormal]; } else { clockIsRunning=YES; timer=[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES]; [button setTitle:@”Stop” forState:UIControlStateNormal]; } }

14. We could go ahead and implement the reset method, which will reset the counter to 0 (and change the title on the top button as well). Let’s allow a reset only while the clock is not running.

- (IBAction) reset { if(!clockIsRunning) { [clock setTitle:@”0” forState:UIControlStateNormal]; } }

15. With the “clock” starting, stopping, and resetting correctly, we need to change from a counter to a stopwatch. We don’t need the variable counter any more, but we do need a double variable to store our start time (as will be explained shortly). Our entire interface file should be:

#import

#import "UtilityFileIO.h"

@interface StopwatchViewController : UIViewController {

IBOutlet UIButton *clock; IBOutlet UIButton *button;

NSTimer *timer;

BOOL clockIsRunning; double startTime;

}

- (IBAction) tap; - (IBAction) reset;

@end

page 2.83

16. We need to make some changes in our implementation file to switch over to a stopwatch rather than a counter. We need to make a couple of changes in our tap method.

Time arithmetic is complex. It is hard to subtract 10:05:49am from 3:02:05pm, for instance. In programming, we almost always represent time not as a sequence of hour:minute:second, but as a number. In many languages, we represent the time as the number of seconds since some specific point in time – that point in time, in many languages, is midnight, January 1, 1970, aka the epoch. If you can represent two times as numbers like this, you can simply subtract those two numbers to get elapsed time.

When the user taps the Start button, we need to get the current time, then start our timer. This is where we will use the double that we defined in the interface file – to represent the time as a straightforward (albeit large) number.

To get the value for startTime, we will use the NSDate class, calling its date class method, which returns an NSDate object. We will use that object, calling its timeIntervalSince1970 method, to assign a value to startTime. This value is the number of seconds since midnight, January 1, 1970, with millisecond accuracy.

- (IBAction) tap { if(clockIsRunning) { [timer invalidate]; clockIsRunning=NO; [button setTitle:@"Start" forState:UIControlStateNormal]; } else { clockIsRunning=YES; startTime=[[NSDate date] timeIntervalSince1970]; timer=[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES]; [clock setTitle:@"0.00" forState:UIControlStateNormal]; [button setTitle:@"Stop" forState:UIControlStateNormal]; } }

17. We need to modify the showClock method. Every time that this method is called, it should get the current time of day, subtract startTime from it (that will give us elapsed time), and display the elapsed time on the top button.

- (void) showClock { double nowTime=[[NSDate date] timeIntervalSince1970]; double elapsed=(nowTime-startTime); [clock setTitle:[NSString stringWithFormat:@"%.2f",elapsed] forState:UIControlStateNormal]; }

You now have a stopwatch that you can stop and start (you will need to delete the first line from your reset method, since we no longer have a variable named counter).

- (IBAction) reset { if(!clockIsRunning) { [clock setTitle:@"0.00" forState:UIControlStateNormal]; } } page 2.84

18. As our program now stands, the stopwatch works, but it will always show the elapsed time in seconds. We may want it to show seconds only up to 60 seconds, then minutes and seconds up through 60 minutes (3600 seconds), followed by hours, minutes, and seconds. A few if statements and some division can take care of this.

One remaining problem will be in the desired formatting of the numbers. When we display minutes and seconds, we always want seconds shown as two digits. A stopwatch that shows 5:3.12 for 5 minutes, 3.12 seconds, seem odd without the leading zero before the 3. We can format the seconds to two characters, but it will be space three rather than zero three. In the code below, we format the number, with the correct number of characters, knowing that some of those characters will be spaces when we want zeroes. We then use the NSString class’s stringByReplacingOccurrenceOfString:withString: method to replace the spaces with zero characters.

- (void) showClock { double nowTime=[[NSDate date] timeIntervalSince1970]; double elapsed=(nowTime-startTime); if(elapsed<60) { [clock setTitle:[NSString stringWithFormat:@"%.2f",elapsed] forState:UIControlStateNormal]; } else if(elapsed<3600) { NSInteger minutes=elapsed/60; double seconds=elapsed-minutes*60; NSString *time=[NSString stringWithFormat:@"%d:%5.2f",minutes,seconds]; time=[time stringByReplacingOccurrencesOfString:@" " withString:@"0"]; [clock setTitle:time forState:UIControlStateNormal]; } else { NSInteger hours=elapsed/3600; elapsed=elapsed-(hours*3600); NSInteger minutes=elapsed/60; double seconds=elapsed-minutes*60; NSString *time=[NSString stringWithFormat:@"%d:%2d:%5.2f",hours,minutes,seconds]; time=[time stringByReplacingOccurrencesOfString:@" " withString:@"0"]; [clock setTitle:time forState:UIControlStateNormal]; } }

19. We need to add the persistence to the stopwatch. We can get this started with one line, in the tap method. Immediately after you get the value for startTime, write it into a file in the Documents folder:

[UtilityFileIO writeDouble:startTime toDocFile:@"Start.txt"];

page 2.85

20. In viewDidLoad, check to see if this file exists. If it does, read its contents into startTime, and immediately start the stopwatch.

- (void)viewDidLoad { [super viewDidLoad];

clockIsRunning=NO;

if([UtilityFileIO docFileExists:@"Start.txt"]) { startTime=[UtilityFileIO readDocFileIntoDouble:@"Start.txt"]; clockIsRunning=YES; timer=[NSTimer scheduledTimerWithTimeInterval:.01 target:self selector:@selector(showClock) userInfo:nil repeats:YES]; [clock setTitle:@"0.00" forState:UIControlStateNormal]; [button setTitle:@"Stop" forState:UIControlStateNormal]; } }

21. We don’t want this file to be too persistent, however, which it is right now. When we tap the Reset button, we want to delete this file. Do that in your reset method.

[UtilityFileIO removeDocFile:@"Start.txt"];

22. You now have a working, persistent stopwatch. We do have one issue here, and that is with some redundant code. We have almost identical blocks of code in our viewDidLoad method and in the else clause of the if statement in our tap method. We like to avoid redundant code, because if we ever have to make a change in the code, we have to remember to make the change in every repetitive block of that code. For maintenance purposes, we try to minimize redundant code.

You will see an additional startTimer method in the accompanying Xcode project. This method has most of the logic from the redundant code: the if statements in viewDidLoad and tap then call this method. Now, if we have to modify this code, we have to do it in only one place.

There are all sorts of stopwatches, and all sorts of stopwatch functionality. Hopefully this project gives you enough information to develop the stopwatch of your dreams.

page 2.86

Project name: MovingImage1-

MovingImage1

This assignment shows how to display an image. It also presents some basic animation, moving the image about the screen.

1. Create a Single View Application.

2. In your interface file, define a UIImageView object. Also, define a method header for a move method.

#import

@interface ViewController : UIViewController {

IBOutlet UIImageView *imageView;

}

- (void) move;

@end

3. Assuming that you have an image file, drag it into your Resources folder. Be sure to check the checkbox to actually copy the physical file into the destination folder.

4. Click your .xib file to open it in the Editor pane. Once there, drag a UIImageView object onto your view. Use the Inspector to connect your imageView outlet to your UIImageView object.

5. We sized our UIImageView object (in IB) to 160 by 160 pixels, perfect for our 160 by 160 image.

page 2.87

6. Click on the UIImageView to highlight it, then select the Attributes tab in the Inspector.

A UIImageView object has a property named image, which is the UIImage object that is managed by the UIImageView. Use the dropdown list to select the filename for your UIImage (the dropdown list will include all of the images that are in your project).

7. Run your program. It should already display the image. Yippee!

8. Let’s make the image move about the screen, in a random fashion.

First, our image has a white background. Let’s set the background color of our view to white also, so the rectangular shape of the image itself won’t be so visible. In our viewDidLoad method, we can set the background color of our view:

self.view.backgroundColor=[UIColor whiteColor];

We are going to pick random numbers, so the image will start in one corner of the screen, and move to a different corner. Then it will start in a corner, and move to a corner. Over and over and over, starting in a random corner each time.

Programming languages use algorithms to generate pseudorandom numbers (the numbers are not truly random, because they are generated by a fixed algorithm; but they appear to be random, thus they are called pseudorandom). If you follow the steps in one of these algorithms multiple times, you will get the exact same sequence of pseudorandom numbers each time – not very random. Thus, we seed the algorithm to a different starting point, then start generating random numbers. It is important that we seed (start) the algorithm once and only once, and that we do it before generating any random numbers. A good place to do this is viewDidLoad. We try to pick a somewhat random number for our seed. We frequently do that by getting a numeric representation of the current date and time, so if we start the program at two different times, we get two different seeds. page 2.88

The NSDate class has a class method named date that returns a NSDate object that represents the current date and time. We can call that object’s timeIntervalSince1970 instance method to get a straightforward numeric representation of that date and time, represented as the number of seconds since midnight, January 1, 1970, which many languages consider to be the epoch, or beginning of time.

The following statement is a common way to seed the random number generator. srand([[NSDate date] timeIntervalSince1970]);

Objective-C supports multi-threaded programming. We can spawn a separate thread of execution to perform some task. It will appear that the computer is doing two things at once. For instance, we might have a thread that operates a running clock, another thread that moves an image around the screen, and another thread that counts from 1 to 100. We don’t want these three separate to activities to occur sequentially, but (seemingly) at the same time.

The iPhone CPU can not actually do two things at the same time, but it can execute one thread for a few nanoseconds, then the second thread, then the third, then the first, the second, and the third, then the first, the second, and the third, and so on. This is multi- threaded programming, and this is what we will do.

The NSTimer class has a method that we can use to spawn a separate thread of execution. We pass to this method - a time interval - a reference to an object - a method to be called when the time interval passes - userInfo (pass nil) - a NO or a YES

If you pass NO for the last argument, the timer will call the specified method only one time, after the time interval passes the first time. If you pass YES, the timer will call the specified method repeatedly, each time that the time interval passes.

The object that you pass as the second argument is the object on which the method operates. Typically, the method is defined in the current class, and we pass self as the second argument.

In the following line of code, we set a timer that will call our move method repeatedly, every 2.0 seconds. (You can type this entire statement on one line in Xcode.)

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(move) userInfo:nil repeats:YES];

page 2.89

One slight problem here is that the move method will be called – the first time – after the first 2.0 second delay. We probably want the first move immediately, then another one after 2.0 seconds, then another, then another. We can call the move method ourselves:

[self move];

Putting all this together, the viewDidLoad method is:

- (void)viewDidLoad { [super viewDidLoad];

self.view.backgroundColor=[UIColor whiteColor];

srand([[NSDate date] timeIntervalSince1970]);

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(move) userInfo:nil repeats:YES]; [self move]; }

9. In the move method, we will pick a random number 0, 1, 2, or 3, and then start the image in the top left, top right, bottom right, or bottom left corner. We will move the image (in a visible, animated fashion) to the opposite corner.

The rand() function will pick a random integer between 0 and about 2 billion. We can use modulus arithmetic to scale this down to our desired range. The modulus operator means “divide by and take the remainder”.

The following line calls the rand() function to get a random number, then divides that number by 4 and takes the remainder. When you divide by 4 and take the remainder, the remainder will always be 0, 1, 2, or 3. It can’t be anything else.

NSInteger ran=rand()%4;

10. Any UIView object has a property named frame which indicates the location and size of the object. In our case, the image will always be 160 pixels wide, and 160 tall. We want to vary the left and top pixel positions.

The top left corner of the view is 0,0 (x,y). The top right is 319,0. The bottom right is 319,459. The bottom left is 0,459. We can actually position the image outside these boundaries, specifying negative values for x and y, which is exactly what we will do. The following select statement sets values for fromLeft, fromTop, toLeft, and toTop, and indicates the corner where the image will start the animation (it will be outside the boundaries of the view), and the corner where it will end the animation (again, outside the boundaries of the view). page 2.90

float fromLeft; float fromTop; float toLeft; float toTop;

switch(ran) { case 0: { fromLeft=-160; fromTop=-160; toLeft=320; toTop=460; break; } case 1: { fromLeft=320; fromTop=-160; toLeft=-160; toTop=460; break; } case 2: { fromLeft=320; fromTop=460; toLeft=-160; toTop=-160; break; } case 3: { fromLeft=-160; fromTop=460; toLeft=320; toTop=-160; } }

11. Now we can construct a CGRect to use as the frame for the initial position of the image.

imageView.frame=CGRectMake(fromLeft,fromTop,160,160);

12. Almost there. Now, we need to create an animation block. Inside this block, we will change some characteristic of our image – specifically, we will change its frame. We will specify a time duration for the animation – how long the animated effect should take. When we start the animation, it will perform our transition, smoothly over the specified time. It is all quite amazing.

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:1.9];

imageView.frame=CGRectMake(toLeft,toTop,160,160);

[UIView commitAnimations];

page 2.91

13. Putting this all together, the move method is:

- (void) move {

NSInteger ran=rand()%4;

float fromLeft; float fromTop; float toLeft; float toTop;

switch(ran) { case 0: { fromLeft=-160; fromTop=-160; toLeft=320; toTop=460; break; } case 1: { fromLeft=320; fromTop=-160; toLeft=-160; toTop=460; break; } case 2: { fromLeft=320; fromTop=460; toLeft=-160; toTop=-160; break; } case 3: { fromLeft=-160; fromTop=460; toLeft=320; toTop=-160; } }

imageView.frame=CGRectMake(fromLeft,fromTop,160,160);

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:1.9];

imageView.frame=CGRectMake(toLeft,toTop,160,160);

[UIView commitAnimations];

}

page 2.92

14. Run this program, and make sure it works. It is a pretty cool program already, complete with random animation.

With some imagination, you can come up with all sorts of animations, chaining different animations together to create your total desired effect.

page 2.93

Project name: MovingImage2-

MovingImage2

This assignment shows how to display an image. It also presents some basic animation, moving the image about the screen. We will make the image move from one random corner of the screen to the center, then from the center to a random corner of the screen – a two-part animation.

1. Create a Single View Application.

2. In your interface file, define a UIImageView object. Also, define a method header for a moveOn method and a moveOff method.

#import

@interface ViewController : UIViewController {

IBOutlet UIImageView *imageView;

}

- (void) moveOn; - (void) moveOff;

@end

3. Assuming that you have an image file, drag it into your Resources folder. Be sure to check the checkbox to actually copy the physical file into the destination folder.

4. Click your .xib file to open it in the Editor pane. Once there, drag a UIImageView object onto your view. Use the Inspector to connect your imageView outlet to your UIImageView object.

5. We sized our UIImageView object (in IB) to 160 by 160 pixels, perfect for our 160 by 160 image.

page 2.94

6. Click on the UIImageView to highlight it, then select the Attributes tab in the Inspector.

A UIImageView object has a property named image, which is the UIImage object that is managed by the UIImageView. Use the dropdown list to select the filename for your UIImage (the dropdown list will include all of the images that are in your project).

7. Run your program. It should already display the image. Yippee!

8. Let’s make the image move about the screen, in a random fashion.

First, our image has a white background. Let’s set the background color of our view to white also, so the rectangular shape of the image itself won’t be so visible. In our viewDidLoad method, we can set the background color of our view:

self.view.backgroundColor=[UIColor whiteColor];

We are going to pick random numbers, so the image will start in one corner of the screen, and move to a different corner. Then it will start in a corner, and move to a corner. Over and over and over, starting in a random corner each time.

Programming languages use algorithms to generate pseudorandom numbers (the numbers are not truly random, because they are generated by a fixed algorithm; but they appear to be random, thus they are called pseudorandom). If you follow the steps in one of these algorithms multiple times, you will get the exact same sequence of pseudorandom numbers each time – not very random. Thus, we seed the algorithm to a different starting point, then start generating random numbers. It is important that we seed (start) the algorithm once and only once, and that we do it before generating any random numbers. A good place to do this is viewDidLoad. We try to pick a somewhat random number for our seed. We frequently do that by getting a numeric representation of the current date and time, so if we start the program at two different times, we get two different seeds. page 2.95

The NSDate class has a class method named date that returns a NSDate object that represents the current date and time. We can call that object’s timeIntervalSince1970 instance method to get a straightforward numeric representation of that date and time, represented as the number of seconds since midnight, January 1, 1970, which many languages consider to be the epoch, or beginning of time.

The following statement is a common way to seed the random number generator. srand([[NSDate date] timeIntervalSince1970]);

Objective-C supports multi-threaded programming. We can spawn a separate thread of execution to perform some task. It will appear that the computer is doing two things at once. For instance, we might have a thread that operates a running clock, another thread that moves an image around the screen, and another thread that counts from 1 to 100. We don’t want these three separate to activities to occur sequentially, but (seemingly) at the same time.

The iPhone CPU can not actually do two things at the same time, but it can execute one thread for a few nanoseconds, then the second thread, then the third, then the first, the second, and the third, then the first, the second, and the third, and so on. This is multi- threaded programming, and this is what we will do.

The NSTimer class has a method that we can use to spawn a separate thread of execution. We pass to this method - a time interval - a reference to an object - a method to be called when the time interval passes - userInfo (pass nil) - a NO or a YES

If you pass NO for the last argument, the timer will call the specified method only one time, after the time interval passes the first time. If you pass YES, the timer will call the specified method repeatedly, each time that the time interval passes.

The object that you pass as the second argument is the object on which the method operates. Typically, the method is defined in the current class, and we pass self as the second argument.

In the following line of code, we set a timer that will call our moveOn method repeatedly, every 2.0 seconds. (You can type this entire statement on one line in Xcode.)

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(moveOn) userInfo:nil repeats:YES];

page 2.96

One slight problem here is that the moveOn method will be called – the first time – after the first 2.0 second delay. We probably want the first move immediately, then another one after 2.0 seconds, then another, then another. We can call the moveOn method ourselves:

[self moveOn];

Putting all this together, the viewDidLoad method is:

- (void)viewDidLoad { [super viewDidLoad];

self.view.backgroundColor=[UIColor whiteColor];

srand([[NSDate date] timeIntervalSince1970]);

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(moveOn) userInfo:nil repeats:YES]; [self moveOn]; }

9. In the moveOn method, we will pick a random number 0, 1, 2, or 3, and then start the image in the top left, top right, bottom right, or bottom left corner. We will move the image (in a visible, animated fashion) to the center of the view.

The rand() function will pick a random integer between 0 and about 2 billion. We can use modulus arithmetic to scale this down to our desired range. The modulus operator means “divide by and take the remainder”.

The following line calls the rand() function to get a random number, then divides that number by 4 and takes the remainder. When you divide by 4 and take the remainder, the remainder will always be 0, 1, 2, or 3. It can’t be anything else.

NSInteger ran=rand()%4;

10. Any UIView object has a property named frame which indicates the location and size of the object. In our case, the image will always be 160 pixels wide, and 160 tall. We want to vary the left and top pixel positions.

The top left corner of the view is 0,0 (x,y). The top right is 319,0. The bottom right is 319,459. The bottom left is 0,459. We can actually position the image outside these boundaries, specifying negative values for x and y, which is exactly what we will do. The following switch statement sets values for fromX and fromY, indicating the corner where the image will start the animation (it will be outside the boundaries of the view).

Our program will be much more flexible if we don't count on hard-code values for the dimensions of our device and of our image. We can use variables (properties) to say this better: The top left corner of the view is 0,0 (x,y). The top right is self.view.frame.size.width-1,0. The bottom right is self.view.frame.size.width-1, self.view.frame.size.height-1. The bottom left is 0, self.view.frame.size.height-1.

page 2.97

float fromLeft; float fromTop;

switch(ran) { case 0: { fromLeft=-imageView.frame.size.width; fromTop=-imageView.frame.size.height; break; } case 1: { fromLeft=self.view.frame.size.width; fromTop=-imageView.frame.size.height; break; } case 2: { fromLeft=self.view.frame.size.width; fromTop=self.view.frame.size.height; break; } case 3: { fromLeft=-imageView.frame.size.width; fromTop=self.view.frame.size.height; } }

11. We want to move the image so that it is centered on the screen. We can calculate that based on the width/height of the screen and the width/height of the image.

float toLeft=(self.view.frame.size.width-imageView.frame.size.width)/2.0; float toTop=(self.view.frame.size.height-imageView.frame.size.height)/2.0;

12. Now we can construct a CGRect to use as the frame for the initial position of the image.

imageView.frame=CGRectMake(fromLeft,fromTop, imageView.frame.size.width,imageView.frame.size.height);

13. Almost there. Now, we need to create an animation block. Inside this block, we will change some characteristic of our image – specifically, we will change its frame. We will specify a time duration for the animation – how long the animated effect should take. When we start the animation, it will perform our transition, smoothly over the specified time. It is all quite amazing.

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.9];

imageView.frame=CGRectMake(toLeft,toTop, imageView.frame.size.width,imageView.frame.size.height);

[UIView commitAnimations];

page 2.98

14. In this example, we need to do one additional thing to achieve our two-part animation. Remember, we want to move from one random corner to the center of the screen, then from the center of the screen to a random corner. We need to start the second part of the two-part animation when the first part ends. Thus, inside our animation block we will specify a method (selector) to be called when our first animation ends (stops). Much like we do with other delegates, we will also specify an object who owns that method to be called: self.

[UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(moveOff)];

15. Putting this all together, the moveOn method is:

- (void) moveOn {

NSInteger ran=rand()%4;

float fromLeft; float fromTop;

switch(ran) { case 0: { fromLeft=-imageView.frame.size.width; fromTop=-imageView.frame.size.height; break; } case 1: { fromLeft=self.view.frame.size.width; fromTop=-imageView.frame.size.height; break; } case 2: { fromLeft=self.view.frame.size.width; fromTop=self.view.frame.size.height; break; } case 3: { fromLeft=-imageView.frame.size.width; fromTop=self.view.frame.size.height; } }

float toLeft=(self.view.frame.size.width-imageView.frame.size.width)/2.0; float toTop=(self.view.frame.size.height-imageView.frame.size.height)/2.0;

imageView.frame=CGRectMake(fromLeft,fromTop, imageView.frame.size.width,imageView.frame.size.height);

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.9]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(moveOff)];

imageView.frame=CGRectMake(toLeft,toTop, imageView.frame.size.width,imageView.frame.size.height); [UIView commitAnimations];

}

page 2.99

16. The moveOff method is very similar.

- (void) moveOff {

NSInteger ran=rand()%4;

float toLeft; float toTop;

switch(ran) { case 0: { toLeft=self.view.frame.size.width; toTop=self.view.frame.size.height; break; } case 1: { toLeft=-imageView.frame.size.width; toTop=self.view.frame.size.height; break; } case 2: { toLeft=-imageView.frame.size.width; toTop=-imageView.frame.size.height; break; } case 3: { toLeft=self.view.frame.size.width; toTop=-imageView.frame.size.height; } }

[UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.9];

imageView.frame=CGRectMake(toLeft,toTop, imageView.frame.size.width,imageView.frame.size.height);

[UIView commitAnimations];

}

With some imagination, you can come up with all sorts of animations, chaining different animations together to create your total desired effect.

page 2.100

Project name: TrackingImage-

TrackingImage

This project shows how to respond to user gestures, such as a finger moving about the screen, and tapping. In this project, an image is displayed in the center of the screen. The user can move the image around the screen with his or her finger (or a french fry), and return the image to the center of the screen by double-tapping.

Responding to user gestures is the responsibility of the UIResponder class. If you look at the documentation of UIResponder, you will see that it defines methods such as:

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event

These four methods constitute the life cycle of a touch (an instance of the UITouch class).

Both UIView and UIViewController subclass UIResponder. Therefore, a UIView is a UIResponder, and may over-ride any or all of these methods. A UIViewController also is a UIResponder, and also may over-ride any of all of these methods.

1. Create a Single-View Application. As has been true in so many projects, most of our work will be done in a subclass of UIViewController. In this project, we will take advantage of the fact that UIViewController subclasses UIResponder. We will over-ride the four touch- related methods from UIResponder to provide our own specific functionality for those methods.

2. Define a UIImageView object in your interface file.

3. Drag an image file into your Resources folder.

4. In Interface Builder, drag a UIImageView object onto your view. Size it so that it is appropriate for your image file. Place the imageView in the center of the screen. Connect your previously created outlet to this UIImageView object.

page 2.101

5. All of the logic will be in the methods that we inherit from UIResponder.

The touchesBegan:withEvent: method will be called any time that a finger touches the screen. It is called one time for each initial touch. If you touch the screen with a finger, and start moving that finger around the screen, and then touch the screen with another finger, this method will be called that second time, when the second finger hits the screen.

In this program, we will not handle any multi-touch events. If we detect two simultaneous touches, we will simply stop tracking altogether. The first thing that we will do in touchesBegan:withEvent:, then, is check for multiple simultaneous touches.

touchesBegan:withEvent: recieves as its first parameter an NSSet object that is a set of all current touches (the elements in the NSSet are UITouch objects). An NSSet is similar to an NSArray, in that it is a collection of elements, but the elements in an NSSet are unordered – there is no element #0, element #1 – they are all just elements. Like an NSArray, we can easily determine how many elements are in the NSSet, however. In this case, if there are two or more elements in the NSSet, we have detected multiple simultaneous touches, and we will stop tracking.

It is going to be handy to have a Boolean variable that determines if we are tracking or not. Define this variable in your interface file.

BOOL tracking;

6. In touchesBegan:withEvent:

if(touches.count>1) { tracking=NO; return; }

If we detect multi-touch activity, we will set our tracking flag to NO, and exit this method.

7. Of course, if we get past this if statement, we are working with a single touch. At this point, we should check to see if the user touched inside the image boundaries or not. We will track the finger only if it starts by touching inside the image boundaries (we could, of course, program any logic we want – this is just what we chose for this project).

The elements in an NSSet are unordered. We are not concerned about order in this NSSet, because we have already verified that there is only one element in the NSSet. We can use the anyObject method to get that element from the NSSet. This element will be a UITouch object.

We want to find where the touch occurred. We can actually find that location within any UIView – not only the entire screen, but we could check to see if the touch occurred anywhere within the boundaries of any subclass of UIView. The locationInView: method tells us where the touch occurred in relation to the top left corner of the view that we pass to the method. We will pass imageView, and get the location in relationship to the top left corner of the imageView. page 2.102

now is used to store the location of the touch. now is a CGPoint, which has an x and a y. If either of these is negative, the touch was to the left of or above the image. If they are both greater than or equal to 0, and less than the width and height of the image, then the touch occurred within the boundaries of the imageView. All we have to do is set the value of our tracking flag.

UITouch *touch=[touches anyObject]; CGPoint now=[touch locationInView:imageView];

if(now.x>=0 && now.x<=imageView.frame.size.width && now.y>=0 && now.y<=imageView.frame.size.height) { tracking=YES; } else { tracking=NO; }

We have not moved the image. That is the job of other methods. We have simply detected if the touch began within the boundaries of the image, and set a variable to YES or NO.

8. The touchesMoved:withEvent: method is called quite frequently when the location of a touch changes. It also receives an NSSet of UITouch objects. First, we will make sure we are still dealing with a single touch.

if(touches.count>1) { tracking=NO; return; }

9. Here’s the big part. Assuming we get past this if statement and we are dealing with a single touch, we need to move the image. We don’t know how far, however.

In addition to locationInView:, a UITouch object has a previousLocationInView: method. We can take the current location, subtract from it the previous location, and that will tell us how far to move the image. We will add those movement amounts to the current location of the image.

One quick thing. We should check our tracking flag. If the initial touch was detected outside the image boundaries, we set tracking to NO, and we don’t need to do anything as the finger moves. We will respond – in this method – only if tracking is YES. if(tracking==YES) { UITouch *touch=[touches anyObject]; CGPoint now=[touch locationInView:imageView]; CGPoint prev=[touch previousLocationInView:imageView];

NSInteger left=imageView.frame.origin.x+(now.x-prev.x); NSInteger top=imageView.frame.origin.y+(now.y-prev.y);

imageView.frame=CGRectMake(left,top,imageView.frame.size.width,imageView.frame.size.height); } page 2.103

10. The touchesEnded:withEvent: method is called one time, when the finger is removed from the screen. We may need to move the image, as there may have been some movement since the last time touchesMoved:withEvent: was called. We can copy the exact code from that method into this one. We do, however, need to add one line: since the touch has ended, we need to set our “flag variable” to NO. if(touches.count>1) { tracking=NO; return; } if(tracking==YES) { UITouch *touch=[touches anyObject]; CGPoint now=[touch locationInView:imageView]; CGPoint prev=[touch previousLocationInView:imageView];

NSInteger left=imageView.frame.origin.x+(now.x-prev.x); NSInteger top=imageView.frame.origin.y+(now.y-prev.y);

imageView.frame=CGRectMake(left,top,imageView.frame.size.width,imageView.frame.size.height); } tracking=NO;

11. Finally, touchesCancelled:withEvent: is called if a touch is interrupted, such as by an incoming phone call. We will simply change our tracking flag to NO. tracking=NO;

Test your program. It should be quite cool.

12. Let’s add one more feature. If the user double-taps on the image, we will return the image to its original location.

First, define a CGRect variable in your interface file:

CGRect start;

In viewDidLoad, assign it the imageView’s original frame:

start=imageView.frame;

We have still chosen to respond only to touches that are within the boundaries of the image. In touchesBegan:withEvent:, we will modify our code, and use the UITouch object’s tapCount property. If its value is 2, we have detected a double-tap – return the image to its original location, and stop tracking:

page 2.104

- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if(touches.count>1) { tracking=NO; return; }

UITouch *touch=[touches anyObject];

CGPoint now=[touch locationInView:imageView]; if(now.x>=0 && now.x<=imageView.frame.size.width && now.y>=0 && now.y<=imageView.frame.size.height) {

if(touch.tapCount==2) { imageView.frame=start; tracking=NO; } else { tracking=YES; } } else { tracking=NO; } }

And with this, we have a pretty cool program.

page 2.105

Project name: Contacts-

Contacts

The UITableView class provides perhaps the most frequently used object that is used to display data in an iPhone application. The class (and its supporting classes and protocols, such as UITableViewCell, UITableViewDataSource, and UITableViewDelegate) provides tremendous power and flexibility. This project will demonstrate a basic table display.

The example to the right shows a list of friends and their phone numbers. This is an infinitely scrollable list, and has the well-known bouncing effect when you get the the top or bottom of the list. Tapping on a table cell brings up a prompt that you can use to actually call the listed number.

1. In Xcode, create a Single View Application.

2. In your interface file, indicate that you plan to implement the UITableViewDataSource and UITableViewDelegate protocols. Remember, there are three steps to implementing a protocol. This first step is to indicate that you are going to implement the protocol – you do this first step by listing the name of the protocol in angle brackets on the @interface directive.

Sefine a UITableView object, that you will create in Interface Builder.

We will create an NSMutableArray to hold our contact information. Define a variable for that NSMutableArray.

#import #import

@interface ViewController:UIViewController { IBOutlet UITableView *table;

NSMutableArray *contacts; }

@end page 2.106

3. Click on your nib file to swap over to open it in the editor, drag a UITableView object onto your view, and connect your table variable to that object. We want a Plain table – a table that contains only one section.

4. We need to instantiate and populate our NSMutableArray in our viewDidLoad method. You can instantiate any object by calling the alloc method and some appropriate form of an init method, such as:

contacts=[[NSMutableArray alloc] initWithCapacity:0];

We can populate our NSMutableArray with its addObject: method. Here, we add a combined name and telephone number as one single string, with the two items separated by a semi-colon character. This semi-colon serves as a delimiter, marking the end of one field and the beginning of the next. You can use any character as a delimiter character, as long as that character does not itself appear in your data. (Later, we will want to break this string apart into its sub-fields. We will use the delimiter character to accomplish this.)

[contacts addObject:@"Andy Taylor;662-555-1212"]; [contacts addObject:@"Barney Fife;662-325-1995"]; [contacts addObject:@"Thelma Lou;662-123-4567"]; [contacts addObject:@"Otis Campbell;601-555-2121"]; [contacts addObject:@"Gomer Pyle;601-512-0505"]; [contacts addObject:@"Goober Pyle;601-512-0506"]; [contacts addObject:@"Opie Taylor;662-555-1313"]; [contacts addObject:@"Beatrice Taylor;662-555-1414"]; [contacts addObject:@"Helen Crump;601-899-8787"]; [contacts addObject:@"Ernest T. Bass;504-888-4567"]; [contacts addObject:@"Briscoe Darling;505-888-1256"]; [contacts addObject:@"Howard Sprague;601-567-0987"]; [contacts addObject:@"Floyd Lawson;662-234-3545"]; [contacts addObject:@"Rafe Hollister;662-989-4728"]; page 2.107

5. Step 2 of implementing a delegate protocol is to tell the object (in this case, the table) that you will be its delegate. In this case, we are implementing both a dataSource protocol and a delegate protocol. A UITableView object has two properties that come into play here: dataSource and delegate.

table.dataSource=self; table.delegate=self;

6. Our entire viewDidLoad method is now:

- (void)viewDidLoad { [super viewDidLoad];

contacts=[[NSMutableArray alloc] initWithCapacity:0];

[contacts addObject:@"Andy Taylor;662-555-1212"]; [contacts addObject:@"Barney Fife;662-325-1995"]; [contacts addObject:@"Thelma Lou;662-123-4567"]; [contacts addObject:@"Otis Campbell;601-555-2121"]; [contacts addObject:@"Gomer Pyle;601-512-0505"]; [contacts addObject:@"Goober Pyle;601-512-0506"]; [contacts addObject:@"Opie Taylor;662-555-1313"]; [contacts addObject:@"Beatrice Taylor;662-555-1414"]; [contacts addObject:@"Helen Crump;601-899-8787"]; [contacts addObject:@"Ernest T. Bass;504-888-4567"]; [contacts addObject:@"Briscoe Darling;505-888-1256"]; [contacts addObject:@"Howard Sprague;601-567-0987"]; [contacts addObject:@"Floyd Lawson;662-234-3545"]; [contacts addObject:@"Rafe Hollister;662-989-4728"];

[table.layer setCornerRadius:8.0f]; [table.layer setMasksToBounds:YES];

table.dataSource=self; table.delegate=self; }

7. Finished with viewDidLoad, we need to actually implement those protocols that we said we were going to implement: UITableViewDataSource and UITableViewDelegate. There are three methods that we must implement when we say that we are going to implement UITableViewDataSource. The three methods that we will need to implement are:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

numberOfSectionsInTableView: returns an NSInteger, which indicates the number of sections that are in our table. We want our table to have one section. Our job is very easy then; simply return a 1.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } page 2.108

tableView:numberOfRowsInSection: receives an NSInteger that represents a section number in the table, then returns an NSInteger that indicates the number of rows that are in that section. Our table has only one section, so our job is – once again – relatively easy. We want the one and only section of our table to have a row for each of our contacts. Our NSMutableArray has an element for each contact, so we can use the NSMutableArray's count property to indicate how many rows we want in the table.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return contacts.count; }

8. tableView:cellForRowAtIndexPath: is the most complicated method that we need to implement, as it returns an actual UITableViewCell object to be displayed.

iOS's implementation of UITableView is quite amazing. It would seem that a table – if it had a large number of rows – would consume large amounts of memory. Through a brilliant stroke of design, however, this is not the case. As the user flicks and scrolls up and down a UITableView, iOS queues and de-queues table cells for re-use. When a table cell scrolls out of view, it is queued for re-use. When a new table cell scrolls into view, first iOS checks to see if there is an available table cell queued for re-use; if there is, it uses it; if not, we will create a new table cell.

In this method, then, we first try to re-use a tableCell. If there is no such cell available, cell will be nil, and we will alloc and init one, using the desired predefined tableCellStyle (UITableViewCellStyleSubtitle).

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellStyle=@"simple"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellStyle]; }

NSString *record=[contacts objectAtIndex:indexPath.row]; NSArray *fields=[record componentsSeparatedByString:@";"]; cell.textLabel.text=[fields objectAtIndex:0]; cell.detailTextLabel.text=[fields objectAtIndex:1];

return cell; }

(This is all more complex if we have multiple "styles" of table cells. Cells of a specific style are queued, and de-queued only for another cell of that same style. Since we have only one style of tableCell in this app, we avoid that complication.)

Once we have a cell (either via re-use, or alloc/init), we retrieve the current element from our contacts array, break it apart based on the semi-colon delimiter, then use its sub-fields to set the text on cell.textLabel (the first row of the cell) and cell.detailTextLabel (the second row). page 2.109

9. You can run your app now and it should have a functioning, scrollable table.

10. We want to be able to respond to tapping a table cell. This is the work of UITableViewDelegate, which we have said that we will implement.

Step 3 of implementing a protocol is to over-ride any desired methods defined by the protocol. We need to over-ride one UITableViewDelegate method: tableView:didSelectRowAtIndexPath:

Like tableView:cellForRowAtIndexPath:, this method receives an NSIndexPath object which indicates the current cell. The NSIndexPath has two properties, section and row. Since our table has only one section, we know which section they selected. We need to know the row. indexPath.row will tell us.

Thus, we retrieve the appropriate element from our contacts array, (once again) break that element apart based on the delimiter, and get subfield #1 (which is the phone number).

We use that phoneNumber to constuct a string that we can use as a URL:

telprompt://phoneNumber

We use that constructed string to create an NSURL object, then use that to open an external application. This will prompt us with the phone number and two options: Cancel or Call. (This will work only on a device that can actually make phone calls, which the Simulator can not.)

Finally, we deselect the tapped row. One requirement of the Apple Human Interface Guidelines (HIG) is that table cells should not display state – a cell may be highlight for a brief moment, but we are supposed to de-highlight it. It should not stay in a highlighted state.

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSString *record=[contacts objectAtIndex:indexPath.row]; NSArray *fields=[record componentsSeparatedByString:@";"]; NSString *phoneNumber=[fields objectAtIndex:1];

NSString *link=[NSString stringWithFormat:@"telprompt://%@",phoneNumber]; NSURL *url=[NSURL URLWithString:link]; [[UIApplication sharedApplication] openURL:url];

[table deselectRowAtIndexPath:indexPath animated:YES]; }

Hopefully you can deploy this app to a device. It will be fun to be able to call your closest friends so easily. This (the hopefully part) gives us another idea.

page 2.110

11. Our app as it is now has an un-nerving characteristic. If the app is deployed to an iPad or iPad Touch (or the Simulator) – to a device that can not make a phone call – nothing appears to happen when we tap the table cell. Surely we can do better than that. Surely we can give the user some indication that something is happening.

The UIDevice class has a class method named currentDevice that will give us a UIDevice object representing the current device. That object will have a property named model that is an NSString representation of the model of that device. (The object will have other interesting properties also, such as batteryLevel and name.) We can use this string to see if the app is running on an iPhone. If it is, we will display the phone number prompt; otherwise we will display a UIAlertView.

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSString *record=[contacts objectAtIndex:indexPath.row]; NSArray *fields=[record componentsSeparatedByString:@";"]; NSString *name=[fields objectAtIndex:0]; NSString *phoneNumber=[fields objectAtIndex:1];

UIDevice *thisDevice=[UIDevice currentDevice]; if([thisDevice.model isEqualToString:@"iPhone"]) { NSString *link=[NSString stringWithFormat:@"telprompt://%@",phoneNumber]; NSURL *url=[NSURL URLWithString:link]; [[UIApplication sharedApplication] openURL:url]; } else { NSString *message=[NSString stringWithFormat: @"If this were an iPhone, you could call %@ at %@",name,phoneNumber]; UIAlertView *alert=[[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok",nil]; [alert show]; } [table deselectRowAtIndexPath:indexPath animated:YES]; }

page 2.111

12. Speaking of the user experience, a pleasing user interface is essential for apps. Many of the iOS GUI controls automatically have rounded corners – a UITableView is not one of those. You can, however, round the corners of any UIView object – you can round the corners on a UITableView, a UIImageView, a UILabel, or any other subclass of UIView.

The functionality we need for this is provided in the QuartzCore framework. Thus, we need to add that framework to our project. In the Navigator pane, click on your project, then click on the Build Phases tab. In the Link Binary with Libraries section, click the small + sign to add a new framework. Select QuartzCore from the list.

Import QuartzCore's header file in your ViewController.h file.

#import

In your viewDidLoad method, you can round the corners on your table (and more, as shown below). The first two lines will round the corner (specifying the number of pixels for the rounded effect). The next two set a border color and the border width (in pixels). Note that the border color must be a CGColor (Core Graphics Color), not a UIColor.

[table.layer setMasksToBounds:YES]; [table.layer setCornerRadius:8.0f]; [table.layer setBorderColor:[[UIColor blackColor] CGColor]]; [table.layer setBorderWidth:2.0];

You may want to add this rounded effect to some of your UIViews in a lot of your apps. page 2.112

Project name: SECTable-

SECTable

The UITableView class provides perhaps the most frequently used object that is used to display data in an iPhone application. The class (and its supporting classes and protocols, such as UITableViewCell, UITableViewDataSource, and UITableViewDelegate) provide tremendous power and flexibitility. This project will demonstrate some slightly more sophisticated table displays.

The example to the right shows the second “group” of a grouped table – the entire table is divided into the teams of the SEC East, followed by the teams of the SEC West. Each “group” contains a header row with simple text, plus a custom- styled cell for each school – each of these custom cells contains an image, plus two rows of text. This project will describe how to create this table, plus several variations.

1. In Xcode, create a Single View Application.

2. In your interface file, indicate that you plan to implement the UITableViewDataSource and UITableViewDelegate protocols. Also, define a UITableView object, that you will create in Interface Builder.

#import

@interface TableViewViewController : UIViewController { IBOutlet UITableView *table; } @end page 2.113

3. Click on your nib file to swap over to open it in the editor, drag a UITableView object onto your view, and connect your table variable to that object.

4. We will use a set of text files to populate the table. The design that we will use here is one that affords flexibility if you want to use some other conference, or some other organization of data. We will store the data files in our application bundle.

Create a text file named ConferenceName.txt (control-click on Supporting Files, then selet New File; on the left side, scroll all the way down to Other, and create an Empty File). page 2.114

5. Type your desired conference name in the file (we typed SEC). This file will be stored in your application bundle, a handy place that you can store read-only files.

6. Create a second text file whose name is based on what you typed into the previous file. That is, we typed SEC, so we named our second file SEC.txt. Type the names of your conference’s divisions into this file – these will be the headers for the table’s groups. We typed East and West, onto two rows in the file.

Now create a text file for each division, naming them according to your conferece name and division names. We named our files SEC-East.txt and SEC-West.txt. Finally, type the names of each school, a tab, and the mascot into the correct division files. (Obviously, you could use something other than conferences and schools in your project. When you are comfortable with files and arrays, you can also use something other than the three-level structure of files that we present.)

page 2.115

7. This project displays a logo image for each school in the left side of each table cell. Clearly, then, you have to have the image files. Create a set of image files of the desired size (we used 60 by 60 pixels), and store them in a folder on your computer. We named our folder Logos-SEC.

Click on Supporting Files to highlight it, then drag your logos folder into your project.

Very important: Be sure to check the checkbox to Copy items into destination group’s folder (if needed). Also, select the option button to Create Folder References for any added folders. If you create these folder references, you will refer to a file such as Logos-SEC/MississippiStateSmall.gif in your code later. If you do not create the folder references, you will refer to simply MississippiStateSmall.gif, excluding the folder reference, despite the fact that the images will appear to be in a folder in your Xcode project. While either method will work, we like the organizational benefits of folders, and prefer to use the folder references.

Notice that the Supporting Files folder shown here contains the four text files, and a folder named Logos-SEC, which has been opened to reveal the image files within it. Note: Even if you selected the other option button during your folder drag (Recursively create groups for any added folders), your Supporting Files folder will look just like this, but you would refer to your image files (in your code) without the folder reference (you would refer simply to MississippiStateSmall.gif, not Logos-SEC/MississippiStateSmall.gif).

page 2.116

8. With all preliminaries finished (we needed quite a few resources for this project, didn’t we?), we can get down to work.

This project is designed to manage one conference, some number of divisions, and some number of schools within those divisions. We will read the conference name from ConferenceName.txt, the division names from text files, and the school names from other text files. We need an NSString to hold the conference name, an NSArray to hold the division names (again, we will read these division names from a single text file, and we can use a single method in UtilityFileIO to do that), and an NSMutableArray to hold the school names (we will need to build this array bit by bit, reading multiple different text files, so we need a mutable array that we can in fact build – we could use an NSArray for the division names because we were reading only one file, and we have a method that will read one file and return an NSArray of elements). Define these three variables in your interface file.

NSString *conferenceName; NSArray *divisions; NSMutableArray *schools;

9. Drag UtilityFileIO.h, UtilityFileIO.m, RegexKitLite.h, and RegexKitLite.m from the accompanying project into your project’s Classes folder. Be sure to check the checkbox to actually copy the files. (You need to change a couple of settings any time that you use RegexKitLite. A brief overview of using UtilityFileIO is included at the end of this project.)

10. Tell the compiler that it should look in UtilityFileIO for some methods that you will use. In your interface file:

#import “UtilityFileIO.h”

11. We need to read our conference name from ConferenceName.txt and store it in our NSString variable. Luckily, UtilityFileIO has a method that is perfect for that, so this will take only one line of code (in viewDidLoad).

conferenceName=[UtilityFileIO readBundleFileIntoString:@"ConferenceName.txt"];

12. Now that we have the conference name, we can use it to construct the name of the next file that we have to read. We will append a .txt to the end of the conference name to construct a file name, then use that file name to read a file into our divisions array (this will read, in our case, the East and the West into two elements of the divisions array).

NSString *filename=[NSString stringWithFormat:@"%@.txt",conferenceName]; divisions=[UtilityFileIO readBundleFileIntoArray:filename];

page 2.117

13. We need to alloc our NSMutable array schools, then read the remaining text files, adding elements to schools. First, alloc the NSMutable array, with an initial capacity of zero elements.

schools=[[NSMutableArray alloc] initWithCapacity:0];

We need to step through each of the elements in divisions, and read the corresponding text file, such as SEC-East.txt and SEC-West.txt. We will step through the array, take the next element from divisions, use that (and conferenceName) to construct a file name, and read that file into a new array named division. We then add that entire array (division, which is the contents of a file such as SEC-East.txt) to the schools NSMutablearray.

for(NSInteger i=0;i<[divisions count];i++) { NSString *divisionName=[divisions objectAtIndex:i]; filename=[NSString stringWithFormat:@"%@-%@.txt",conferenceName,divisionName]; NSArray *division=[UtilityFileIO readBundleFileIntoArray:filename]; [schools addObject:division]; }

This will populate our arrays as we wanted them. Note now that schools might be viewed as a two-dimensional array, with a “row” for each division, and then “columns” representing the schools within a division. In most languages, a two-dimensional array must be symmetrically rectangular, so each “row” would be required to have the same number of columns. schools does not have this restriction – each division (“row”) is a separate NSArray object, and is not restricted to have the same number of elements that the NSArray objects on other “rows” have. We could have 5 schools in one division, and 8 in another.

14. Finally, in viewDidLoad, we need to set our viewController as the table’s dataSource and delegate (this will not work if we did not indicate that we were implementing these protocols).

table.dataSource=self; table.delegate=self;

15. Finished with viewDidLoad, we need to actually implement those protocols that we said we were going to implement: UITableViewDataSource and UITableViewDelegate. There are three methods that we must implement when we say that we are going to implement UITableViewDataSource. The three methods that we will need to implement are:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

numberOfSectionsInTableView: returns an NSInteger, which indicates the number of sections that are in our table. We want a section of each of our conference’s divisions. We want a section for each element in the divisions array. We should, then, return “the number of elements in the divisions array.”

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return divisions.count; } page 2.118

tableView:numberOfRowsInSection: receives an NSInteger that represents a section number in the table, then returns an NSInteger that indicates the number of rows that are in that section. Each section in our table will list the schools that are in a particular division (actually, the section will have one header row, then a row for each school). How many schools are in each division? The schools array has an element for each division. Each element in the schools array is itself an array – an array of the schools in that division. Thus, we want to return “the number of schools in the corresponding element of the schools array”.

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { NSArray *division=[schools objectAtIndex:section]; return division.count+1; }

Here, we extract the appropriate element from schools, and assign it to a new array named division (which is, remember, itself an NSArray). We then return the number of elements in the division array.

16. tableView:cellForRowAtIndexPath: is the most complicated method that we need to implement, as it returns an actual UITableViewCell object to be displayed. In this program, the method is particularly complex, because we want to use two different styles of cells, and one of them is a custom style.

Let’s use Interface Builder to create the custom tableViewCell. First, create a subclass of UITableViewCell named schoolCell. Control-click on Classes, then select New File.

In the New File window, click Cocoa Touch to highlight it (along the left side). Click the Objective-C class icon to highlight it. page 2.119

Name your class SchoolCell. In Subclass of, select UITableViewCell.

17. Now create a corresponding nib file. Again, control-click on Classes, then select New File. This time highlight User Interface, and click on the Empty icon. page 2.120

Select the desired Device Family.

18. Click on your nib file to open it in the editor.

The Dock will show only two lines: File’s Owner, and First Responder.

19. Drag a UITableViewCell object from the Library onto the editing area. page 2.121

20. Change the Identity of the new tableView Cell to SchoolCell. (In the Inspector, click the third tab from the left, then select SchoolCell from the Class select list.)

21. Drag a UIImageView and two UILabel objects onto the tableViewCell, placing them wherever you want. You may need to use the Inspector’s Size tab to manually specify the exact location and sizes for your objects.

22. Swap back to your interface file and define variables for your imageView and two labels.

IBOutlet UIImageView *imageView; IBOutlet UILabel *school; IBOutlet UILabel *mascot;

While you're in your interface file, #import SchoolCell.h.

#import "SchoolCell.h"

23. Connect your outlets to your graphical objects. Important note: Rather than File's Owner, click on School Cell when you make your connections.

24. Now for the tough part, implementing tableView:cellForRowAtIndexPath: in our viewController (this method is required to implement the UITableViewDataSource protocol). This method is particularly complex in this program because we have two different tableViewCell styles. First, consider the first cell in each section of the table. It will list the conference name and the division name, such as SEC East.

We know the conference name (it’s store in the string variable conferenceName). This method receives an NSIndexPath, which has two components, a section and a row. The table section corresponds to a division in our conference. This cell does not need our custom style – it’s just a basic tableCell.

When this method receives an NSIndexPath, if the row component of that NSIndexPath is 0, then this is a new table section – it should be a header row. So we can use:

page 2.122

if(indexPath.row==0) { NSString *cellStyle=@"header"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStyle]; } NSString *header=[NSString stringWithFormat:@"%@ %@",conferenceName, [divisions objectAtIndex:indexPath.section]]; cell.textLabel.text=header; return cell; }

First (as always), we try to re-use a tableCell that has the identifier headerCellStyle. If there is no such cell available, cell will be nil, and we will alloc and init one, using the default tableCellStyle (as indicated by the constant UITableViewCellStyleDefault).

Once we have a cell (either via re-use, or alloc/init), we construct a string comprised of conferenceName and an element from divisions (notice the user of indexPath.section to extract the correct element from divisions), and use it as the text on the standard tableCell’s textLabel (which is a UILabel object).

25. If indexPath.row is not zero, then we have the else part of this if statement – we have a table cell that represents a school. For this cell, we will use our custom cell style. else { NSString *cellStyle=@"school"; SchoolCell *cell=(SchoolCell *)[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { NSArray *parts= [[NSBundle mainBundle] loadNibNamed:@"SchoolCell" owner:self options:nil]; cell=[parts objectAtIndex:parts.count-1]; }

26. We need to set the values for SchoolCell’s imageView, school, and mascot variables. We really don’t have a way to do that, however – we don’t have a way to refer to those variables that are in a separate class. The answer is to make those variables properties of the class (or, more precisely, of instances created from that class).

In SchoolCell’s interface file, and put two lines just before the @end directive.

@property(retain) IBOutlet UILabel *school; @property(retain) IBOutlet UILabel *mascot;

Note that we don’t need to define a property named imageView, because our superclass – UITableView – already defined imageView as a property. Our imageView over-rides that one – it’s still a property. Go over to SchoolCell’s implementation file, and put the following two lines just after the @implementation directive.

@synthesize school; @synthesize mascot;

This creates getter methods (named school and mascot) and setter methods (named setSchool and setMascot). Rather than using these methods, we will use dot notation. page 2.123

27. The schools array is going to provide the information that we need for this table cell. schools has an element for each division. We will extract the correct element based on the section of the table. The element is an array of schools within the division. We could, then, get the correct school with two lines.

NSArray *division=[schools objectAtIndex:indexPath.section]; NSString *school=[division objectAtIndex:indexPath.row-1];

Note that we subtract 1 from indexPath.row, because row #0 within a section is the header, then row #1 within the section corresponds to school #0 in the division array.

28. Each element in schools is actually a school, a tab character, and a mascot. We can break that string into its components, using the tab as a delimiter character. This will give us an array in which element #0 is the school, and element #1 is the mascot.

NSArray *fields=[school componentsSeparatedByString:@"\t"]; cell.school.text=[fields objectAtIndex:0]; cell.mascot.text=[fields objectAtIndex:1];

29. We need to make an adjustment to our school name to get our image file name. We have schools with names like Mississippi State, with an embedded space character. Our image file names match the school names, except that the image file names do not have any spaces in them. Thus, we made one adjustment, and created a “compressed” version of the school name:

NSString *compressed= [cell.school.text stringByReplacingOccurrencesOfString:@" " withString:@""];

30. Now we can use the compressed school name to create an image file name.

NSString *imageFile=[NSString stringWithFormat:@"Logos-SEC/%@Small.gif",compressed];

31. Finally, we can assign any remaining values to the properties of the custom cell, and return the cell:

cell.imageView.image=[UIImage imageNamed:imageFile]; return cell;

page 2.124

32. Putting this all together, the method is:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

{ if(indexPath.row==0) { NSString *cellStyle=@"header"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStyle]; } NSString *header=[NSString stringWithFormat:@"%@ %@",conferenceName, [divisions objectAtIndex:indexPath.section]]; cell.textLabel.text=header; return cell; } else { NSString *cellStyle=@"school"; SchoolCell *cell=(SchoolCell *)[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { NSArray *parts= [[NSBundle mainBundle] loadNibNamed:@"SchoolCell" owner:self options:nil]; cell=[parts objectAtIndex:parts.count-1]; }

NSArray *division=[schools objectAtIndex:indexPath.section]; NSString *school=[division objectAtIndex:indexPath.row-1]; NSArray *fields=[school componentsSeparatedByString:@"\t"]; cell.school.text=[fields objectAtIndex:0]; cell.mascot.text=[fields objectAtIndex:1];

NSString *compressed= [cell.school.text stringByReplacingOccurrencesOfString:@" " withString:@""]; NSString *imageFile=[NSString stringWithFormat:@"Logos-SEC/%@Small.gif",compressed]; cell.imageView.image=[UIImage imageNamed:imageFile];

return cell; } }

33. Run it!

page 2.125

34. Are your table cells all crammed together, like this one? If so, we have just a few tweaks to make.

First, the table cells need to be taller. Our 60-pixel image can not display in the default tableViewCell size. To do this, we need to implement one UITableViewDelegate method:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 64.0; }

We are simply returning 64.0 (a float), which indicates that each tableViewCell will be 64.0 pixels tall. If you wanted, you could return different heights for different cells; we just determined that 64.0 looked good in our table.

35. You may need to return to Interface Builder to make some location and size adjustments on your imageView and your labels. If you increased the font for your labels, and they are still displayed with a small font, you probably defined a small label (the actual size of the label), and the font is being adjusted automatically so the long school name fits onto the small label. Make the label bigger, and the font will be bigger.

Finally, because this is a grouped table, divided into sections, the actual table display area is not the entire normal 320 pixels – some of those 320 pixels are reserved for the gray border around the table sections. In Interface Builder, reduce the width of School Cell to 300.

page 2.126

Project name: MascotPicker-

Mascot Picker

This project implements a UIPickerView object, commonly called a picker. A picker provides a great way to display long, long lists of options and/or data to the user, and allow user selection within the picker.

This application will display two lists of data in a picker: a list of school names, and a list of mascots. We will create a very simple game in which we will display a school, and the user is supposed to pick the correct mascot.

A UIPickerView uses two protocols for much of its implementation: UIPickerViewDataSource and UIPickerViewDelegate. An understanding of implementing protocols is essential to iPhone programming.

1. Create a Single View Application.

2. Your class will implement two protocols. A protocol is similar to a class, except that you can not instantiate an instance of a protocol. You can, however, establish an “is a” relationship between your class and the protocol, however, and that is what implementing the protocol does. (The similarity between a class and a protocol is that you can establish an “is a” relationship between your class and another class – a superclass – and you can establish an “is a” relationship between your class and a protocol. The difference between a class and a protocol is that you can instantiate an instance of a class, but you can not instantiate an instance of a protocol.) page 2.127

The documentation of the UIPickerView class, first of all, indicates that it is a Class. It also indicates that it Inherits from UIView, which inherits from UIResponder, which inherits from NSObject. Thus, a UIPickerView object “is a” UIView object, it “is a” UIResponder object, and it “is a” NSObject object. A UIPickerView object inherits all of the functionality of UIView, UIResponder, and NSObject. UIPicker view is a subclass of UIView (which is UIPickerViews immediate superclass). The documentation also indicagtes that UIPickerView Conforms to several protocols (such as NSCoding).

The documentaqtion of the UIPickerViewDelegate protocol, by contrast, indicates that it is a Protocol. It does not Inherit from anything, is not a subclass to anything, and has no superclasses.

When you are looking through the documentation, one of the first things that you want to know is whether you are looking at the documentation of a class, in which case you can instantiate an instance of that class, or a protocol, in which case you can implement the protocol.

3. Implementing a protocol requires three steps (these steps are worth remembering, as they are required to implement any protocol): Step 1: indicate the protocol to be implemented by enclosing its name in angle brackets at the end of your @interface directive (in your .h file) Step 2: tell the appropriate object that you will be its delegate (or, in some cases, its dataSource); you typically do this by either setting the value of a property of that object (that’s what we do with UIPickerViewDataSource and UIPickerViewDelegate), or by passing something (usually self) as an argument to a method (this is what we do with UIAlertViewDelegate) Step 3: over-ride any desired methods from the protocol

Step 1 is quite easy: include the name of the protocol (or multiple, comma-separated protocols) in angle brackets in your @interface header.

@interface ViewController : UIViewController

We will get to Steps 2 and 3 later.

4. Define a UIPickerView object in your interface file.

IBOutlet UIPickerView *picker;

page 2.128

5. Define two methods that we will implement later:

- (IBAction) pickSchool; - (IBAction) checkMascot;

6. Click on your .xib file to open it in the Editor. Drag a UIButton object, a UIPickerView object, and a UIButton object onto your view. Make your connections in the Inspector. Connect your two buttons, your picker, and both methods.

7. While it was initially easy to “implement” UIPickerViewDataSource (all we did was include the protocol name in angle brackets), that is kind of like saying we will implement a plan. At some point, someone is going to expect us to actually implement the plan. In this case, at some point, we have to provide some data for the picker to display.

We will use two arrays to display the data in our picker. We will hard-code the data in the arrays (in Project #005a, #005b, and #005c, we will see how to read that data from an external data source, such as a text file). Define three NSMutableArray objects in your interface file. (NSMutableArray is a subclass of NSArray – the big difference between the two is that you can not modify the size or contents of an NSArray after it has been created, but you can modify the size or contents of an NSMutableArray.)

NSMutableArray *data; NSMutableArray *schools; NSMutableArray *mascots;

8. In your viewDidLoad method, and populate your arrays. For ease of typing and maintaining our data, we will put sets of school/mascot in one array, then split each element of that array into school/mascot pairs, putting the schools in one array, and the mascots in another.

data=[[NSMutableArray alloc] initWithCapacity:0]; [data addObject:@"Alabama\tCrimson Tide"]; [data addObject:@"Arkansas\tRazorbacks"]; [data addObject:@"Auburn\tTigers"]; [data addObject:@"Florida\tGators"]; [data addObject:@"Georgia\tBulldogs"]; [data addObject:@"Kentucky\tWildcats"]; [data addObject:@"LSU\tTigers"]; [data addObject:@"Miss. State\tBulldogs"]; [data addObject:@"Ole Miss\tRebels"]; [data addObject:@"So. Carolina\tGamecocks"]; [data addObject:@"Tennessee\tVolunteers"]; [data addObject:@"Vanderbilt\tCommodores"];

9. The elements that we just added to data could be characterized as variable-length, delimited data. The school name Tennessee, for instance, is longer than the name LSU. As our delimiter character, which indicates the end of one piece of data (school name) and the beginning of the next piece of data (mascot), we used the tab character (\t). Note that it is imperative that a delimiter character NOT appear anywhere in the data.

Instantiate the schools and mascots arrays. Then step through the elements in data (a for loop is very handy for stepping through the elements in an array). For each element, split it apart based on the delimiter character – this will give us a school and a mascot. Add each of these to the appropriate array.

page 2.129

schools=[[NSMutableArray alloc] initWithCapacity:0]; mascots=[[NSMutableArray alloc] initWithCapacity:0]; for(NSInteger i=0;i

10. (This is Step 2 of implementing a protocol – tell the appropriate object that you will be its delegate.) We want our class to serve as the dataSource and the delegate for picker (our UIPickerView). dataSource and delegate are properties of any UIPickerView object; we will refer to those properties using dot notation, and our object (our program) as their value.

picker.dataSource=self; picker.delegate=self;

11. (This is Step 3 of implementing a protocol – over-ride any desired methods defined by the protocol.) Finished with viewDidLoad, it is now time to implement the desired methods defined by UIPickerViewDataSource and UIPickerViewDelegate. As their documentation shows, UIPickerViewDataSource defines only two methods, both of which we need to implement.

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component

With these two methods, we will indicate the number of components in the picker (this is the number of “columns”, or number of independently spinning wheels; in our example, it is 2) and the number of rows in each component.

Implementing the first method is easy. We know we have 2 components, so we will simply return a 2.

- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 2; }

In the second method, in this case we actually have the same number of rows in each component, but we could have different numbers, so we will prepare for that possibility. For component #0 (the leftmost component), the number of rows is equal to the number of elements in the schools array; for component #1, it is the number of elements in the mascots array.

- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component { if(component==0) { return schools.count; } else { return mascots.count; } } page 2.130

12. UIPickerViewDelegate defines several methods, one of which we must implement.

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component

This method returns an NSString, which is then displayed in the picker. We want to display a school name in component #0, and a mascot name in component #1.

- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component { if(component==0) { return [schools objectAtIndex:row]; } else { return [mascots objectAtIndex:row]; } }

Believe it or not, we have a working spinner. Run your program in the Simulator. You should see your schools and mascots on the spinning wheels.

13. Let’s start some game action. Go back to Interface Builder and put the title “Pick a school” on the upper button. Now, when the user taps this button, we will programmatically pick a school. As a matter of fact, we will pick a random school. And we will animate the spinning of component #0 to selet that school.

Computers use pre-defined algorithms to pick pseudo-random numbers. The numbers are not truly random, but they appear to be (depending on the quality of the algorithm). Since the steps in the algorithm are pre-defined, if you follow the exact same steps every time you run the program, you will get the exact same “random numbers”, which would not be random at all. It is essential that you seed the algorithm, telling the random number generator to start in a different place. Do this only once, probably in viewDidLoad.

srand([[NSDate date] timeIntervalSince1970]);

14. Now, write a pickSchool method. You will, of course, need to define the method header in your interface file, and connect it to your top button in Interface Builder.

We will pick a random school, then programmatically spin the picker’s component #0. The rand() function will return a pseudo-random integer. To adjust range of values that might result, we will divide by the number of rows in the component, then take the remainder. If there are 12 rows in the component, we will, then, divide the random integer by 12, and take the remainder. The remainder will always be in the range 0-11, inclusive, which is perfect as an index in our schools array.

NSInteger ran=rand()%schools.count; [picker selectRow:ran inComponent:0 animated:YES];

Try this program in the simulator. Every time you tap the button, you will see component #0 spin to a different school. Pretty cool.

page 2.131

15. At this point, this “game” is quite easy, as the schools and mascots are displayed in the same order. There are all sorts of things that we could do, but a good one would be to display the mascots in a random order. For this, we need one additional array, so define it in your interface file:

NSMutableArray *displayedMascots;

16. Now, back in viewDidLoad, we will create this array from the mascots array. We need to use the concept of random numbers without replacement. This is much like picking random numbers out of a basket of numbered balls. If you had a basket, with 10 balls, numbered 0 through 9, you would pick random numbers either with or without replacement. If you pick the numbers with replacement, when you pick a ball, you will call out the number, but then put the ball back in the basket. Obviously, you could get repeat numbers. If you pick the numbers without replacement, you will not put the ball back in the basket. Clearly, you can not pick that ball again, since it won’t be in the basket.

To pick random mascots without replacement (because we don’t want to list one twice, and not list others), we will operate much like that basket of balls, except that instead of a basket, we will have the mascots array. We will pick a random element from the array, and then remove that element from mascots.

displayedMascots=[[NSMutableArray alloc] initWithCapacity:0]; while(mascots.count>0) { NSInteger ran=rand()%mascots.count; NSString *mascot=[mascots objectAtIndex:ran]; [displayedMascots addObject:mascot]; [mascots removeObjectAtIndex:ran]; }

17. In your pickerView:numberOfRowsInComponent: method and your pickerView:titleForRow:forComponent: method, change your reference to mascots to displayedMascots instead. Test your program in the Simulator. Your mascots should be in a random order. (Be sure to make this change in both of these methods.)

18. We certainly need to check to see if the user selected the correct mascot. Let’s write a checkMascot method (again, you will need to define the method header in your interface file, and make your connection in Interface Builder).

This method can go back through the data array to find the selected school, then compare the current displayedMascot with the mascot in data.

First, we need to retrieve the current displayed school and mascot from our picker. A picker has a selectedRowInComponent: method that will tell us which row in any specific component is currently selected. We can get that index, then use it in the schools array and displayedMascots array to get the actual displayed school and mascot.

page 2.132

Now we can step through the data array, comparing the selected school to each one in data. When we find a match, take the mascot from that data element, and compare it to the selected mascot. We can determine if we have a match or not. Give an appropriate alert, then break to get out of the for loop, since there is no reason to continue going through it.

- (IBAction) checkMascot { NSInteger schoolIndex=[picker selectedRowInComponent:0]; NSInteger mascotIndex=[picker selectedRowInComponent:1];

NSString *selectedSchool=[schools objectAtIndex:schoolIndex]; NSString *selectedMascot=[displayedMascots objectAtIndex:mascotIndex];

for(NSInteger i=0;i

18. Let’s add one more feature. Let’s see how long it takes the user to get the correct mascot.

We will use an NSDate object, which we need to define in our interface file.

NSDate *startTime;

In our pickSchool method, we need to add one line (the last line in the method). This will get the current date and time, and store it in startTime.

startTime=[NSDate date];

Change our innermost if statement in checkMascot. Get that current date and time, and subtract startTime from it to get the elapsed time since startTime. Display that to the user.

if([mascot isEqualToString:selectedMascot]) { NSDate *nowTime=[NSDate date]; double elapsed=[nowTime timeIntervalSinceDate:startTime]; message=[NSString stringWithFormat:@"match in %.2f seconds",elapsed]; }

page 2.133

19. Quite frequently, we need to implement a lot of little pieces of logic to check for things that might occur while running our app. For instance, we might want to require that the user tap the Pick a School button before trying to pick the correct mascot. We can do this.

Let’s define a Boolean flag that we can use. In your interface file, define:

BOOL started;

20. We want to initialize (give the variable its initial, or first, value) this variable to a value of NO. Do this in viewDidLoad.

started=NO;

21. In our checkMascot method, we can use started to see if the player ever tapped the Pick a School button. Put the following at the very beginning of the method:

if(!started) { NSString *message=@"Please tap the Pick a School button"; UIAlertView *alert=[[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok",nil]; [alert show]; return; }

If started has a value of NO (which is its default initial value), the negation operator will change the if statement to “if not no”. Wow, this is some twisted logic, but “not no” is “yes”, so if started has a value of NO, the relational expression will evaluate to YES, and we will display this alert.

22. When the player taps the Pick a School button, we want to set the value of started to YES. Put this in your pickSchool method.

started=YES;

page 2.134

23. There are plenty of other things that you could add to this project, but we will leave most of that fun for you. We will implement only one more thing, and that is a feature that will prevent the user from simply spinning component #0 to a more desirable school.

We are picking a random number (our variable ran) and spinning component #0 to a random school (this is in the pickSchool method). We need for ran to be a bit more persistent, so we can still refer to it in a different method later. Define ran in your interface file rather than in your pickSchool method. This changes the scope of the variable, so it is available in other methods.

The UIPickerViewDelegate protocol defines a method that responds to the user selecting a row in a component. We can implement our own version of this method to accomplish what we need to do here.

We want to respond only to spinning component #0, and only if the user spins to a row other than the one we picked. We can do this with an if statement. If this does happen, we will simply spin the component back to where we wanted it, and issue an alert.

- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component { if(component==0 && row!=ran) { [picker selectRow:ran inComponent:0 animated:YES]; UIAlertView *alert=[[UIAlertView alloc] initWithTitle:nil message:@"nice try" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"ok",nil]; [alert show]; } }

And now, all of the remaining fun is left for you to enjoy on your own.

page 2.135

Project name: Navigator-

Navigator

This example shows how to use a UINavigationController to move back and forth between a main view and one or more subordinate views. The top-level view is represented by a UIViewController, which includes a UINavigationController. The UINavigationController will automatically place a blue bar at the top of each view. You can specify a Title, to be displayed in the middle of the Navigation Bar. Your additional views will be arranged in a hierarchical fashion, underneath your root ViewController. You can push views onto the navigation stack, as you drill down into deeper and deeper levels of views, and pop them off as you return to the root ViewController. The UINavigationController will automatically put “Back” buttons on the Navigation Bar.

1. In Xcode, create a Single View Application.

2. Create a ViewController for each of your subordinate pages. Control-click on your project folder, then select New File. Click on Cocoa Touch, and select UIViewController subclass.

In our example, we created six of these new classes. Name each of your new classes (we named ours Level2ViewController, Level3ViewController, Level4ViewController, RedViewController, GreenViewController, and BlueViewController). Be sure to check the checkbox to create a .xib file.

page 2.136

3. Edit AppDelegate.m. You will see the following line in the applicationDidFinishLaunchingWithOptions: method:

self.window.rootViewController = self.viewController;

Replace this line with the following two lines: UINavigationController *ontroller=[[UINavigationController alloc] initWithRootViewController:self.viewController]; self.window.rootViewController=controller;

4. Your root ViewController (your default viewController, the one that we have worked with in all of our projects) now has a NavigationController that you can use to push subordinate viewControllers onto the stack, then pop them off the stack. Those subordinate viewControllers will slide in from the right, and slide back out to the right.

Click on your ViewController.xib file and put a button on your view. In this example, when we tap the button, the subordinate viewController will slide in from the right.

5. Edit ViewController.h. Add the following line just after the existing #import statement:

#import “Level2ViewController.h”

Add the following line just before the @end line:

-(IBAction) showNextLevel;

6. Connect this method to your button (in Interface Builder), responding to TouchDown.

7. Edit ViewController.m to actually implement your new method. Add the following just before the @end line:

-(IBAction) showNextLevel { Level2ViewController *controller=[[Level2ViewController alloc] initWithNibName:@"Level2ViewController" bundle:nil]; [self.navigationController pushViewController:controller animated:YES]; }

As you see, when we tap the button that is on the root ViewController, we will call this method, which will (1) instantiate a Level2ViewController object, and (2) push it onto our navigationController stack (animated).

8. That's it! Run your program. When you tap the button, you will see the second view slide in from the right. Of course, our second view doesn't have anything, but that is left up to you. You can also create a Level3ViewController, Level4ViewController, and so on, and push those onto your navigation stack, and pop them off the stack. You could also create (for instance) RedViewController, GreenViewController, and BlueViewController, and have a second, separate button on your rootViewController for them. The subordinate views in a navigation stack are arranged in a tree-like hierarchical fashion, and you can move down and back up branches of that tree.

page 2.137

For each subordinate level, you will need to: 1. define a new class, like Level2ViewController 2. define a method that will "link to" that new class 3. connect that method to some GUI object, like a button that you will tap 4. implement the method by instantiating the new class and pushing it onto your stack.

9. You will notice that each of your "Back" buttons (which are automatically generated and managed by the NavigationController) actually says "Back". You can provide more meaningful titles for those buttons by setting a title for each of your viewControllers.

In your root ViewController.m file, set a title in your viewDidLoad method:

- (void) viewDidLoad { [super viewDidLoad]; self.title=@"Main menu"; }

You will see that your root view now displays this title, on the Navigation bar, and that your Level2 view has a modified Back button title. You can do this in any or all of your viewControllers.

10. One other concept that is sometimes extremely important – how to I pass data from one view to the next? This is most cleanly done with a method, defined in the subordinate viewController, and called by the "prior" viewController.

In Level2ViewController, define the following method in your .h file:

- (void) setColor:(UIColor *)color;

We will call this method from our root ViewController, passing the method a UIColor object, which the method will use to change the background color of its view.

Implement the method in Level2ViewController.m:

- (void) setColor:(UIColor *)color { self.view.backgroundColor=color; }

Finally, in ViewController.m, modify our method slightly by adding one line, to call the just- implemented setColor: method.

-(IBAction) showNextLevel { Level2ViewController *controller=[[Level2ViewController alloc] initWithNibName:@"Level2ViewController" bundle:nil]; [controller setColor:[UIColor yellowColor]]; [self.navigationController pushViewController:controller animated:YES]; }

page 2.138

Note that this is a very, very important ability to have (and to understand) – the ability to pass data from one class to another class. Again, the cleanest way to do this is for the first method to call a method of the second class, passing the data as an argument to the method.

11. In a program in which you have many, many views, it may be cumbersome to pass data from view 1 to view 2, then from view 2 to view 3, then from view 3 to view 4, and so on and so on and so on. An alternative is to use your appDelegate, because you can always refer to your appDelegate, from any view in your program. Thus, we can define a variable in the appDelegate, and have access to it from anywhere via methods that we define (also) in the appDelegate.

In AppDelegate.m, define a UIColor object which we will use as the tint color for all of our navigationBars (introduced in iOS 5, not available in prior versions of the SDK). Also, define a method that we can call to set that tint color, and one to get it.

#import

@class ViewController;

@interface AppDelegate : UIResponder { UIColor *tintColor; } @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) ViewController *viewController;

- (void) setTintColor:(UIColor *)color; - (UIColor *) getTintColor;

@end

In AppDelegate.m, implement these methods:

- (void) setTintColor:(UIColor *)color { tintColor=color; }

- (UIColor *) getTintColor { return tineColor; }

(Note that we could have opted to make tintColor a property, in which case we would not have defined these two methods, but would have used dot notation to set and get the value of the property. Either approach will work.)

page 2.139

In our root ViewController, pick a desired app-wide tint color, and call setTintColor: to store that desired color in tintColor in the AppDelegate. We need to import AppDelegate.h in our ViewController.h file:

#import #import "Level2ViewController.h" #import "AppDelegate.h"

Then in our viewDidLoad method, we can get a reference to our AppDelegate, and call one of its methods:

AppDelegate *appDelegate=[[UIApplication sharedApplication] delegate]; [appDelegate setTintColor:[UIColor magentaColor]]; Finally, in any class in which we want to use this tintColor, we will (1) import AppDelegate.h (in that class's .h file), (2) get a reference to our AppDelegate, and (3) call the getTintColor method. Here is our final viewDidLoad in our root ViewController.m file:

- (void) viewDidLoad { [super viewDidLoad]; self.title=@"Main menu";

AppDelegate *appDelegate=[[UIApplication sharedApplication] delegate]; [appDelegate setTintColor:[UIColor magentaColor]];

[self.navigationController.navigationBar setTintColor:[appDelegate getTintColor]];

}

and our viewDidLoad from Level2ViewController.m:

12. There may be cases in which you want to be able to pop all the way from some deep part of your navigation stack all the way to the top. This is easy to do:

- (IBAction) gotoTop { [self.navigationController popToRootViewControllerAnimated:YES]; }

Final note: We didn't say that these would be pleasing colors, but were intending only to demonstrate using the appDelegate as a sort of central repository for data!

page 2.140

Project name: AudioAndVideo-

AudioAndVideo

This project demonstrates the playing of both audio and video files within an app, using an AVAudioPlayer to play audio, and an MPMoviePlayerController to show the video file. An AVAudioPlayer object supports playing sound files of any format supported by iOS. Likewise, an MPMoviePlayerController object supports displaying video files of any format supported by iOS.

1. Create a Single View Application.

2. Drag your desired audio and/or video files into your project’s Supporting Files folder. We used .m4r files for audio, and .m4v for video. You can use any type of file supported by iOS, but this project assumes that you use the same type of file (same filename extension) for all of your audio files, and the same type for all of your video files.

page 2.141

3. In your .xib file, drag a UITableView object onto your view.

4. If you want to support both audio files and video files, you might want to list all of your audio files in one section of your table, followed by a second section that lists all of your video files. If you want such a multi-section table, in the Attributes tab of the Inspector, change the Style of your table to Grouped.

5. Quite naturally, you need to define a variable for your table (in your interface file), and connect that variable to your table (in Interface Builder). We named our variable table. (The UITableViewDataSource and UITableViewDelegate methods use the name tableView for one of their variables, so we avoid using that name ourselves.)

6. The AVAudioPlayer class is in the AVFoundation framework, which is not automatically available in your apps. To add this optional framework, click on your project in the Navigator pane (the leftmost pane), then click the Build Phases tab in the Editor pane. Click Link Binary with Libraries to open it and reveal its contents – the three automatically linked frameworks.

page 2.142

7. Click the small + sign below the three linked libraries to add your desired optional framework. Add the AVFoundation framework.

8. The MPMoviePlayerController class is in the MediaPlayer framework. Follow similar steps to add the MediaPlayer framework to your project.

9. After you add a framework to your project, you must also import the header file (the .h file) of the class that you want to use. Add two import statements to your interface file.

#import #import

10. There are many different ways that you could populate your table with the names of your audio and video files. We chose to use handy methods from UtilityFileIO (described in a separate File I/O project) to get directory listings from our application bundle. To do this, get a copy of UtilityFileIO.h, UtilityFileIO.m, RegexKitLite.h, and RegexKitLite.m, and drag them into your Classes folder (these files are available in this project, in the Supporting Files folder). Then add the appropriate import statement.

#import "UtilityFileIO.h"

Note: Any time that you use UtilityFileIO and RegexKitLite, you need to set a value under Build Phases (-fno-objc-arc) and Build Settings (-licucore). page 2.143

11. Since we are using a UITableView, we will want to implement the UITableViewDataSource and UITableViewDelegate protocols. We also want to implement the AVAudioPlayerDelegate protocol. List these three, enclosed in angle brackets, and the end of your @interface directive.

@interface ViewController : UIViewController

12. We will create two arrays, one to hold our list of audio file names, and one to create our list of video file names (we will use these arrays to populate our table). Define these two arrays, as well as an AVAudioPlayer object and an MPMoviePlayerController object. Your interface file now looks like:

#import #import #import

#import "UtilityFileIO.h"

@interface ViewController : UIViewController { IBOutlet UITableView *table;

NSArray *audioTitles; AVAudioPlayer *audioPlayer;

NSArray *videoTitles; MPMoviePlayerController *videoPlayer; }

@end

13. In your viewDidLoad method, use the getBundleDirOfType method of the UtilityFileIO class to get a directory listing of all of the audio files in your application bundle. Read this list into an array. Then do the same for your video files.

audioTitles=[UtilityFileIO getBundleDirOfType:@"m4r"]; videoTitles=[UtilityFileIO getBundleDirOfType:@"m4v"];

14. Tell your table that you will be its dataSource and its delegate.

table.dataSource=self; table.delegate=self;

15. Implement your UITableViewDataSource methods. First, assuming that you want to have two sections in your table (one for audio files, and one for video files), tell your table that it should have two sections:

- (NSInteger) numberOfSectionsInTableView:(UITableView *)tableView { return 2; }

page 2.144

16. Tell your table how many rows it should have in each section (use the number of elements in your two arrays for this):

- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { if(section==0) { return audioTitles.count; } else { return videoTitles.count; } return 0; }

17. Put an appropriate title above each section:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { if(section==0) { return @"Audio files"; } else { return @"Video files"; } return @""; }

18. Finally, populate the table cells. In this case, we need to know which section we are populating, so we will know which array to use (audio or video). Strip off the filename extension before displaying in the table cell, set a larger font, and display a disclosureIndicator so the user knows that he or she should tap the cell.

- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *cellStyle=@"cell"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellStyle]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellStyle]; }

NSString *title; if(indexPath.section==0) { title=[audioTitles objectAtIndex:[indexPath row]]; } else { title=[videoTitles objectAtIndex:[indexPath row]]; } cell.textLabel.text=[title substringToIndex:[title length]-4]; cell.textLabel.font=[UIFont systemFontOfSize:16.0f]; cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;

return cell; }

page 2.145

19. Implement the UITableViewDelegate method that responds to the user’s tapping a table cell. In this case, again, we want to know which section we are working with, because we handle audio one way, and video another.

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(indexPath.section==0) { NSString *title=[audioTitles objectAtIndex:[indexPath row]]; NSURL *url=[NSURL fileURLWithPath:[UtilityFileIO getBundleFilename:title]]; audioPlayer=[[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL]; audioPlayer.delegate=self;

[audioPlayer play]; } else { NSString *title=[videoTitles objectAtIndex:[indexPath row]]; NSURL *url=[NSURL fileURLWithPath:[UtilityFileIO getBundleFilename:title]]; videoPlayer=[[MPMoviePlayerController alloc] initWithContentURL:url];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:@"MPMoviePlayerPlaybackDidFinishNotification" object:videoPlayer]; videoPlayer.view.frame=self.view.bounds; [self.view addSubview:videoPlayer.view];

[videoPlayer play]; } } } In the previous method, we get the selected title from our array, use that string to create an NSURL, then use that url to create an AVAudioPlayer. Set ourself as the audioPlayer’s delegate (so we will be notified when the audio finishes playing), and call its play method.

Using an MPMoviePlayerController is very similar, except that MPMoviePlayerController does not have a delegate protocol. We set up our notification handling a bit differently, using the NSNotificationCenter class. Here, we specify one of our methods to be called when the movie finishes.

For the moviePlayerController, we need to set its frame to match our view’s frame, and add its view to our view hierarchy. Finally, we play the movie.

20. Write our delegate method to handle the notification of an audio file completing. Do whatever you want to do (if anything) when your audio file completes. (We will do something more significant later.)

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { audioPlayer=nil; }

page 2.146

21. Also write the method to handle the notification of a video file completing. (We chose to display an alert indicating the movie had finished playing.)

- (void) playbackFinished:(NSNotification *)notification { [videoPlayer.view removeFromSuperview];

UIAlertView *alert=[[UIAlertView alloc] initWithTitle:@"" message:@"The movie has finished playing" delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil]; [alert show];

}

22. There are a couple of other additions that we could incorporate. One, we could set up an NSTimer, and make the selected table cell blink as an audio file was playing. Second, we could prohibit the user from tapping a cell while an audio was currently playing. We need a few more variables, at which point our entire interface file becomes:

#import

#import #import

#import "UtilityFileIO.h"

@interface AudioViewController : UIViewController {

IBOutlet UITableView *table;

NSArray *audioTitles; AVAudioPlayer *audioPlayer;

NSArray *videoTitles; MPMoviePlayerController *videoPlayer;

UITableViewCell *selectedCell; NSTimer *timer; NSInteger toggle; BOOL canSelect; }

@end

23. Initialize our canSelect variable to Yes in viewDidLoad:

- (void)viewDidLoad { [super viewDidLoad];

audioTitles=[UtilityFileIO getBundleDirOfType:@"m4r"]; videoTitles=[UtilityFileIO getBundleDirOfType:@"m4v"];

table.dataSource=self; table.delegate=self; canSelect=YES; }

page 2.147

24. Incorporate our new logic in our tableViewDelegate method:

- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { if(canSelect) { if(indexPath.section==0) { NSString *title=[audioTitles objectAtIndex:[indexPath row]]; NSURL *url=[NSURL fileURLWithPath:[UtilityFileIO getBundleFilename:title]]; audioPlayer=[[AVAudioPlayer alloc] initWithContentsOfURL:url error:NULL]; audioPlayer.delegate=self;

[audioPlayer play]; selectedCell=[tableView cellForRowAtIndexPath:indexPath];

timer=[NSTimer scheduledTimerWithTimeInterval:.25 target:self selector:@selector(toggleCell) userInfo:nil repeats:YES]; canSelect=NO; } else { NSString *title=[videoTitles objectAtIndex:[indexPath row]]; NSURL *url=[NSURL fileURLWithPath:[UtilityFileIO getBundleFilename:title]]; videoPlayer=[[MPMoviePlayerController alloc] initWithContentURL:url];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:@"MPMoviePlayerPlaybackDidFinishNotification" object:videoPlayer];

videoPlayer.view.frame=self.view.bounds; [self.view addSubview:videoPlayer.view]; [videoPlayer play];

[table deselectRowAtIndexPath:indexPath animated:NO]; } } else { [[tableView cellForRowAtIndexPath:indexPath] setSelected:NO animated:NO]; } }

25. Write a toggleCell method, which our NSTimer will call every .25 seconds:

- (void) toggleCell { if(toggle==0) { [selectedCell setSelected:YES animated:YES]; } else { [selectedCell setSelected:NO animated:YES]; } toggle=1-toggle; }

26. Stop the timer and re-set our canSelect flag when an audio finishes:

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag { [timer invalidate]; [selectedCell setSelected:NO animated:YES]; canSelect=YES; } and you have yourself a pretty fancy program. page 2.148

Project name: Faces-

Faces

iOS5 introduced a new class named CIDetector, provided in the CoreImage framework. As the documentation of the class says, a CIDetector object "uses image processing to look for specific features in a picture." At this point, the specific facial recognition capabilities provided to us in the SDK are minimal, but you can identify a face's left eye, right eye, and mouth. This project shows how to identify these facial components.

1. Create a Single View Application.

2. We will use a UIImagePicker to select an image from your photo library. To use the imagePicker, we need to implement both the UIImagePickerControllerDelegate and the UINavigationControllerDelegate. Thus, we will list both of those protocols at the end of our @interface directive.

#import

@interface ViewController : UIViewController { }

@end

page 2.149

3. The CIDetector class is provided to us in the optional CoreImage framework. We need to add that framework to our project. In the Navigator, click on your project name, then click on the Build Phases tab.

In the Link Binary with Libraries section, you will see the three frameworks that Xcode automatically linked into our project (UIKit, Foundation, and CoreGraphics). Click the small + below these three frameworks, and select CoreImage from the list of additional available frameworks.

Import CoreImage's header file in your ViewController.h file, which now contains:

#import #import

@interface ViewController : UIViewController { }

@end

4. We are going to draw our circles by using a UILabel object, but rounding its corners. This functionality, which has been used in a number of our projects, is provided by the QuartzCore framework. All we need to do at this point is import its header file:

#import #import #import

@interface ViewController : UIViewController { }

@end

page 2.150

5. Define a UIImageView object. We will not use Interface Builder at all in this project, but will, instead, do everything programmatically. Also, define two method headers that we will implement: one named analyze that we will call to analyze an image for facial features, and one named selectImage that we will use to display our photo library.

There is actually only one more thing we will need in our interface file, so let's go ahead and do that now. When you work with images in iOS programming, you will find inconsistent coordinate systems – sometimes 0,0 is the top left corner, sometimes it's the bottom left, sometimes it's the bottom right. We will run into that with CIDetector's functionality, and we will solve the problem by flipping our window vertically. To do that – to refer to our application's window – we will need to be able to refer to our appDelegate. Thus, we will import AppDelegate.h, and we will define an AppDelegate variable. Our entire ViewController.h file is, then:

#import #import #import #import "AppDelegate.h"

@interface ViewController : UIViewController { UIImageView *imageView;

AppDelegate *appDelegate; }

- (void) analyze; - (void) selectImage;

@end

6. In our viewDidLoad method, we need to get a reference to our appDelegate so we can refer to our window, when needed:

appDelegate=[[UIApplication sharedApplication] delegate];

7. Instantiate a UIImageView object, using any image file that you want (drag the image into your application bundle – this is just our starting image; we will pick one from the photo library when the user taps a button later). CIDetector functionality works best when the imageView is set to UIViewContentModeBottomLeft, so the bottom left of the image is always in a consisten location. This also works best when the image is sized to properly fit on the screen.

imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MonaLisa.png"]]; imageView.frame=CGRectMake(0,0,320,460); imageView.contentMode=UIViewContentModeBottomLeft; [self.view addSubview:imageView];

8. Now, because of the aforementioned geometry inconsistencies with images, we need to flip both our window and our imageView horizontally, so top becomes bottom and bottom becomes top. We do this with a CGAggineTransform.

[appDelegate.window setTransform:CGAffineTransformMakeScale(1, -1)]; [imageView setTransform:CGAffineTransformMakeScale(1, -1)];

page 2.151

9. Instantiate a button to put at the bottom of the screen. We can instantiate the button programmatically, set its title, set up an event handler to respond when the user taps the button, set its frame, flip it vertically (required since the window has been flipped), and add it to our view.

UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"Select image" forState:UIControlStateNormal]; [button addTarget:self action:@selector(selectImage) forControlEvents:UIControlEventTouchDown]; button.frame=CGRectMake(100,20,120,27); [button setTransform:CGAffineTransformMakeScale(1,-1)]; [self.view addSubview:button];

10. After all of this, we simply call our analyze method, which we will write in just a moment. You can simply call [self analyze], or you can do as we did, and have that method run in the background, on a separate thread of execution so our main thread will still respond to things like user interactions. Now our entire viewDidLoad method is:

- (void)viewDidLoad { [super viewDidLoad]; appDelegate=[[UIApplication sharedApplication] delegate];

imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MonaLisa.png"]]; imageView.frame=CGRectMake(0,0,320,460); imageView.contentMode=UIViewContentModeBottomLeft; [self.view addSubview:imageView];

[appDelegate.window setTransform:CGAffineTransformMakeScale(1, -1)]; [imageView setTransform:CGAffineTransformMakeScale(1, -1)];

UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect]; [button setTitle:@"Select image" forState:UIControlStateNormal]; [button addTarget:self action:@selector(selectImage) forControlEvents:UIControlEventTouchDown]; button.frame=CGRectMake(100,20,120,27); [button setTransform:CGAffineTransformMakeScale(1,-1)]; [self.view addSubview:button];

[self performSelectorInBackground:@selector(analyze) withObject:nil];

}

11. The analyze method will do the actual facial recognition. We kept things relatively simply by drawing a fixed size circle on each eye and mouth (rather than trying to resize the circles based on the size of the recognized face). We did use separate colors for each facial component, just to identify them to ourselves.

First, we need to get a Core Image image from our UIImage:

CIImage *image=[CIImage imageWithCGImage:imageView.image.CGImage];

12. Next, we create a CIDetector, specifying an NSDictionary of options. At this time, there is only one option available: either high accuracy or low accuracy when trying to determine facial components. High accuracy takes longer but is, surprise, more accurate.

NSDictionary *options= [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh forKey:CIDetectorAccuracy]; CIDetector *detector= [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:options]; page 2.152

13. Now that we have a CIDetector object (which we named detector), we can use its featuresInImage: method to get an array of recognized facial features.

NSArray *features=[detector featuresInImage:image];

14. And, now, step through the array of features. For each feature, we can get its bounds, which we use to draw a red box (approximately) around the face, and to check for an identified leftEye, rightEye, and mouth. As you will see, we use a for loop to step through the elements in our array of features, extracting a feature from the array, drawing our red box, then checking for the other facial components (which may, or may not, be detected).

We can draw our red box with a few lines of code (the key is setting the frame of this UILabel equal to the bounds of the feature):

UILabel *outline=[[UILabel alloc] init]; outline.frame=feature.bounds; outline.backgroundColor=[UIColor clearColor]; outline.layer.borderWidth=1; outline.layer.borderColor=[[UIColor redColor] CGColor]; outline.alpha=1.0; [self.view addSubview:outline];

Similary, we can use a property of the feature to determine if a specific facial feature was detected (leftEye, rightEye, and mouth at this time, but hopefully additional features such as glasses and mood later as the CIDetector class's functionality is expanded), the use a UILabel with rounded corners to draw a circle as the identified location. We drew each circle with a diameter of 40, so we subtracted 20 from the detector's identified point to get the top left corner for our label:

if(feature.hasLeftEyePosition) { UILabel *leftEye=[[UILabel alloc] initWithFrame:CGRectMake(feature.leftEyePosition.x-20, feature.leftEyePosition.y-20,40,40)]; leftEye.layer.cornerRadius=20; leftEye.backgroundColor=[UIColor redColor]; leftEye.alpha=.5; [self.view addSubview:leftEye]; }

page 2.153

15. The entire analyze method is:

-(void)analyze { CIImage *image=[CIImage imageWithCGImage:imageView.image.CGImage];

NSDictionary *options= [NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh forKey:CIDetectorAccuracy]; CIDetector *detector= [CIDetector detectorOfType:CIDetectorTypeFace context:nil options:options];

NSArray *features=[detector featuresInImage:image];

for(NSInteger i=0;i

UILabel *outline=[[UILabel alloc] init]; outline.frame=feature.bounds; outline.backgroundColor=[UIColor clearColor]; outline.layer.borderWidth=1; outline.layer.borderColor=[[UIColor redColor] CGColor]; outline.alpha=1.0; [self.view addSubview:outline];

if(feature.hasLeftEyePosition) { UILabel *leftEye=[[UILabel alloc] initWithFrame:CGRectMake(feature.leftEyePosition.x-20, feature.leftEyePosition.y-20,40,40)]; leftEye.layer.cornerRadius=20; leftEye.backgroundColor=[UIColor redColor]; leftEye.alpha=.5; [self.view addSubview:leftEye]; }

if(feature.hasRightEyePosition) { UILabel *rightEye=[[UILabel alloc] initWithFrame:CGRectMake(feature.rightEyePosition.x-20, feature.rightEyePosition.y-20,40,40)]; rightEye.layer.cornerRadius=20; rightEye.backgroundColor=[UIColor blueColor]; rightEye.alpha=.5; [self.view addSubview:rightEye]; }

if(feature.hasMouthPosition) { UILabel *mouth=[[UILabel alloc] initWithFrame:CGRectMake(feature.mouthPosition.x-20, feature.mouthPosition.y-20,40,40)]; mouth.layer.cornerRadius=20; mouth.backgroundColor=[UIColor yellowColor]; mouth.alpha=.5; [self.view addSubview:mouth]; } } }

16. You can test your program now on the image that you have in your application bundle. You will get some warnings since we have not implemented all of our methods yet, but the program should run, and it should find facial features (and mark them).

page 2.154

17. Selecting an image from your photo library would be easy if not for the geometry inconsistency issues – simply instantiate a UIImagePickerController, and present it. We need to implement UIImagePickerControllerDelegate, and then set self to be the imagePicker's delegate. We also need to indicate what type of imagePicker we want, because we use this same functionality to access the device's camera.

When you try this, you will find that the imagePicker will not be visible, and that is because of the previous flip that we have applied to our window. So we flip the window back. Then, you will see that the imagePicker appears, but as it is appearing, you can see that our image (that was already visible, on the screen) goes upside down. To avoid this unsettling image, we change the alpha of the imageView to 0.0, so it is not visible. The entire method is then:

- (void) selectImage { UIImagePickerController *controller=[[UIImagePickerController alloc] init]; controller.delegate=self; controller.sourceType=UIImagePickerControllerSourceTypePhotoLibrary;

self.view.alpha=0.0;

[appDelegate.window setTransform:CGAffineTransformMakeScale(1, 1)];

[self presentModalViewController:controller animated:YES completion:nil];

}

18. The last steps are the implementation of UIImagePickerControllerDelegate's methods, of which there are two: one if we pick an image from the photo library, and one if we cancel. First we wil implement imagePickerController:didFinishPickingImage:editingInfo:.

We chose to do some basic scaling of the image that was selected from the photo library. The image is certainly more than 320 pixels wide and 460 tall, so we scaled it down to 460 tall, and proportionally scaled down the width:

CGSize size=CGSizeMake(image.size.width*(460/image.size.height),460); UIGraphicsBeginImageContext(size); [image drawInRect:CGRectMake(0,0,size.width,size.height)]; image=UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();

19. After scaling the image, we can display it in our imageView, dismiss the imagePicker, flip our window back upside down (mirrored), and change the alpha of our view back to 1.0 (totally opaque):

imageView.image=image; [picker dismissModalViewControllerAnimated:YES]; [appDelegate.window setTransform:CGAffineTransformMakeScale(1, -1)]; self.view.alpha=1.0;

page 2.155

20. We need to do one more thing. When we "mark" an image, we add a box and three circles to our view hierarchy. This is fine on the first image, but when we select a second image, we don't want to markings from the previous image. We need to remove them from the view hierarchy. Our view has a property named subViews, which is an array of subviews managed by the view. We will step through those subviews and remove all UILabel objects:

for(NSInteger i=self.view.subviews.count-1;i>=0;i--) { UIView *view=[self.view.subviews objectAtIndex:i]; if([view isKindOfClass:[UILabel class]]) { [view removeFromSuperview]; } }

Notice that we went from the end of the array to the beginning, because as we remove elements from the array, the elements are shifted upward.

21. Finally, call our analyze method, as we did before. The entire method is:

- (void) imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(NSDictionary *)editingInfo { CGSize size=CGSizeMake(image.size.width*(460/image.size.height),460); UIGraphicsBeginImageContext(size); [image drawInRect:CGRectMake(0,0,size.width,size.height)]; image=UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();

imageView.image=image; [picker dismissModalViewControllerAnimated:YES]; [appDelegate.window setTransform:CGAffineTransformMakeScale(1, -1)]; self.view.alpha=1.0;

for(NSInteger i=self.view.subviews.count-1;i>=0;i--) { UIView *view=[self.view.subviews objectAtIndex:i]; if([view isKindOfClass:[UILabel class]]) { [view removeFromSuperview]; } }

[self performSelectorInBackground:@selector(analyze) withObject:nil]; }

22. Last step, handle the possibility that the user will not select an image from the photo library, but will cancel.

- (void) imagePickerControllerDidCancel:(UIImagePickerController *)picker { [picker dismissModalViewControllerAnimated:YES]; [appDelegate.window setTransform:CGAffineTransformMakeScale(1, -1)]; self.view.alpha=1.0; }

As we said at the outset, at this point, CIDetector provides only very basic facial recognition, but that will surely be expanded at some point. Incorporating the expanded functionality into your app should be pretty easy! page 2.156

Project name: Mailer-

Mailer iOS3 introduced a new class named MFMailComposeViewController, provided in the MessageUI framework. As the documentation of the class says, a MFMailComposeViewController object "provides a standard interface that manages the editing and sending of an email message." This project shows how to use this class to create a basic email app of your own.

1. Create a Single View Application.

2. The MFMailComposeViewController class is available to us in the MessageUI framework. We need to add that framework to our project, then import its header file into our ViewController. (When you look up the documentation of a new class, be sure to note what framework the class is provided in. If the framework is anything other than UIKit, Foundation, or CoreGraphics, you need to add the framework to your project.)

In the Navigator pane, click on your project. Click the Build Phases tab. In the section for Link Binary with Libraries, click the + to add the MessageUI framework to your project.

3. After you add the framework to your project, import the appropriate header file in your interface file (ViewController.h).

#import #import

page 2.157

4. To respond to user interactions with our mailController (such as, to respond to the user either sending the message or cancelling), we need to implement the MFMailComposeViewControllerDelegate protocol. Thus, we will list that protocol at the end of our @interface directive.

#import #import

@interface ViewController : UIViewController { }

@end

5. We will use a button that the user will tap to send a message, and a label for some feedback. Define a variable for each of these. Also define a variable for your mailController. Finally, define a method header that we will implement to respond to a button tap.

#import #import

@interface ViewController : UIViewController {

IBOutlet UILabel *label; IBOutlet UIButton *button;

MFMailComposeViewController *controller;

}

- (IBAction) sendEmail;

@end

6. In Interface Builder, drag a UILabel and a UIButton onto your view.

Naturally, you connect your interface file variables to your GUI objects, and connect your sendEmail method to your button.

7. The MFMailComposeViewController class has a class method named canSendEmail that will check to see if the device is configured to send email. If this method returns a NO, then there is really nothing this app can do, so set a message on the label, and disable the button.

- (void) viewDidLoad { if(![MFMailComposeViewController canSendMail]) { label.text=@"can not send email"; button.enabled=NO; [button setTitle:@"disabled" forState:UIControlStateNormal]; } }

page 2.158

8. We need to over-ride one MFMailComposeViewControllerDelegate protocol method, mailComposeController:didFinishWithResult:, which will be called when the user either sends a message or cancels. Here, we want to set a message on our label to provide some feedback.

The key parameter received by this method is result, which can be compared with some pre- defined constants to determine "message sent", "message cancelled", or "message failed".

- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { [self dismissViewControllerAnimated:YES completion:NULL]; if(result==MFMailComposeResultSent) { label.text=@"message sent"; } if(result==MFMailComposeResultSaved) { label.text=@"message saved"; } if(result==MFMailComposeResultCancelled) { label.text=@"message cancelled"; } if(result==MFMailComposeResultFailed) { label.text=@"message failed"; } }

page 2.159

9. Actually sending the email is amazingly simple. Create an array of email addresses to which you want to send your message. Instantiate your MFMailComposeViewController object, set yourself as its delegate, pass your array to the setToRecipients: method, set the initial subject and message body, and present your viewController.

- (IBAction) sendEmail { NSMutableArray *array=[[NSMutableArray alloc] initWithCapacity:0]; [array addObject:@"[email protected]"];

controller=[[MFMailComposeViewController alloc] init]; controller.mailComposeDelegate=self; [controller setToRecipients:array]; [controller setSubject:@"test email"]; [controller setMessageBody:@"This is a test message." isHTML:NO]; [self presentViewController:controller animated:YES completion:NULL]; }

Note that when you present this viewController, the user will have the option to send the message, to specify different recipients, to modify the message, or even to cancel the message. Your app can not send an email message – only the user can do that. What you can do is pre-set some options for the user.

And now, email away!

page 2.160

Project name: Texter-

Texter iOS4 introduced a new class named MFMessageComposeViewController, provided in the MessageUI framework. As the documentation of the class says, a MFMessageComposeViewController object "provides a standard system user interface for composing SMS (Short Message Service) text messages." This project shows how to use this class to create a basic texting app of your own.

1. Create a Single View Application.

2. The MFMessageComposeViewController class is available to us in the MessageUI framework. We need to add that framework to our project, then import its header file into our ViewController. (When you look up the documentation of a new class, be sure to note what framework the class is provided in. If the framework is anything other than UIKit, Foundation, or CoreGraphics, you need to add the framework to your project.)

In the Navigator pane, click on your project. Click the Build Phases tab. In the section for Link Binary with Libraries, click the + to add the MessageUI framework to your project.

3. After you add the framework to your project, import the appropriate header file in your interface file (ViewController.h).

#import #import

page 2.161

4. To respond to user interactions with our messageController (such as, to respond to the user either sending the text or cancelling), we need to implement the MFMessageComposeViewControllerDelegate protocol. Thus, we will list that protocol at the end of our @interface directive.

#import #import

@interface ViewController : UIViewController { }

- (IBAction) sendText;

@end

5. We will use a button that the user will tap to send a text, and a label for some feedback. Define a variable for each of these. Also define a variable for your messageController. Finally, define a method header that we will implement to respond to a button tap.

#import #import

@interface ViewController : UIViewController {

IBOutlet UILabel *label; IBOutlet UIButton *button;

MFMessageComposeViewController *controller;

}

- (IBAction) sendText;

@end

6. In Interface Builder, drag a UILabel and a UIButton onto your view.

Naturally, you connect your interface file variables to your GUI objects, and connect your sendText method to your button.

7. The MFMessageComposeViewController class has a class method named canSendText that will check to see if the device is configured to send text messages. If this method returns a NO, then there is really nothing this app can do, so set a message on the label, and disable the button.

- (void) viewDidLoad { if(![MFMessageComposeViewController canSendText]) { label.text=@"can not send SMS message"; button.enabled=NO; [button setTitle:@"disabled" forState:UIControlStateNormal]; } } page 2.162

8. We need to over-ride one MFMessageComposeViewControllerDelegate protocol method, messageComposeViewController:didFinishWithResult:, which will be called when the user either sends a text message or cancels. Here, we want to set a message on our label to provide some feedback.

The key parameter received by this method is result, which can be compared with some pre- defined constants to determine "message sent", "message cancelled", or "message failed".

- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result { [self dismissViewControllerAnimated:YES completion:NULL]; if(result==MessageComposeResultSent) { label.text=@"message sent"; } if(result==MessageComposeResultCancelled) { label.text=@"message cancelled"; } if(result==MessageComposeResultFailed) { label.text=@"message failed"; } }

9. Actually sending the text message is amazingly simple. Create an array of phone numbers to which you want to send your message. Instantiate your MFMessageComposeViewController object, set yourself as its delegate, pass your array to the setRecipients: method, set the initial contents of your message, and present your viewController.

- (IBAction) sendText { NSMutableArray *array=[[NSMutableArray alloc] initWithCapacity:0]; [array addObject:@"800-555-1212"]; [array addObject:@"800-555-1213"]; [array addObject:@"800-555-1214"];

controller=[[MFMessageComposeViewController alloc] init]; controller.messageComposeDelegate=self; [controller setRecipients:array]; [controller setBody:@"This is a test message"]; [self presentViewController:controller animated:YES completion:NULL]; }

Note that when you present this viewController, the user will have the option to send the text, to specify different recipients, to modify the message, or even to cancel the message. Your app can not send a text message – only the user can do that. What you can do is pre-set some options for the user.

And now, text away!

page 2.163

Project name: Tweeter-

Tweeter

iOS5 introduced a new class named TWTweetComposeViewController, provided in the Twitter framework. As the documentation of the class says, a TWTweetComposeViewController object "presents a view to the user to compose a tweet." This project shows how to use this class to create a basic Twitter app of your own.

1. Create a Single View Application.

2. The TWTweetComposeViewController class is available to us in the Twitter framework. We need to add that framework to our project, then import its header file into our ViewController. (When you look up the documentation of a new class, be sure to note what framework the class is provided in. If the framework is anything other than UIKit, Foundation, or CoreGraphics, you need to add the framework to your project.)

In the Navigator pane, click on your project. Click the Build Phases tab. In the section for Link Binary with Libraries, click the + to add the Twitter framework to your project.

3. After you add the framework to your project, import the appropriate header file in your interface file (ViewController.h).

#import #import

page 2.164

4. We will use a UIWebView to display status information from your Twitter account. We need to implement both the UIWebViewDelegate protocol. Thus, we will list that protocol at the end of our @interface directive.

#import #import

@interface ViewController : UIViewController { }

@end

5. Define a variable that you can connect to your UIWebView. Also define a UIActivityIndicatorView (a spinning wheel that is used to show activity), and a UILabel that we will use to show status information. Finally, define a method header that we will implement to respond to a button tap.

#import #import

@interface ViewController : UIViewController { IBOutlet UIActivityIndicatorView *activityIndicator; IBOutlet UIWebView *webView; IBOutlet UILabel *label; }

- (IBAction) tweet;

@end

6. In Interface Builder, drag a UIWebView, a UILabel, and a UIButton onto your view. Also, drag a UIActivityIndicatorView, and make sure two properties are set: Animating (so the spinner will be spinning when our app starts), and Hides When Stopped (so the spinner will disappear when we stop its animation).

Naturally, you connect your interface file variables to your GUI objects, and connect your tweet method to your button. page 2.165

7. We're going to start our UIWebView totally transparent, submit an HTTP request to Twitter, then fade our UIWebView in when we get our HTTP response. In implementing UIWebViewDelegate, we need to tell the webView that we will be its delegate. Finally, we create an NSURLRequest for our Twitter account, and load it into our webView.

- (void) viewDidLoad { webView.alpha=0.0; webView.delegate=self;

// change this value to your twitter login NSString *twitterLogin=@"yourTwitterLogin"; NSString *urlString= [NSString stringWithFormat:@"http://www.twitter.com/%@",twitterLogin]; NSURL *url=[NSURL URLWithString:urlString]; NSURLRequest *request=[NSURLRequest requestWithURL:url]; [webView loadRequest:request]; }

8. We need to over-ride one UIWebViewDelegate protocol method, webViewDidFinishLoad:, which will be called when we have completely received our HTTP response and it has been loaded into our webView. Here, we want to fade our webView in (by changing its alpha), and stop our activityIndicator from spinning.

- (void)webViewDidFinishLoad:(UIWebView *)webViewObject { [UIView beginAnimations:nil context:nil]; webView.alpha=1.0; [UIView commitAnimations]; [activityIndicator stopAnimating]; }

9. The TWTweetComposeViewController class has a class method named canSendTweet that will check to see if the device is configured to send tweets (your device must be set up with Twitter login information – set this up in the Settings app).

Once we determine that we can send tweets from this device, we instantiate a TWTweetComoposeViewController object.

TWTweetComposeViewController *controller=[[TWTweetComposeViewController alloc] init];

When we present this controller to the user, the user will have control over the tweet. That is, you can not actually send a tweet from your app. You can set the tweet up, but then you present a controller to the user, where the user can make any change that he or she wants, and then the user actually sends the tweet. The user is in control, not you.

Having said this, we can at least set up some initial settings for the tweet. We can set its initial text. Finally, we can specify an action to be taken when the user either sends or cancels the tweet.

page 2.166

A TWTweetComposeViewController object has a property named completionHandler, to which we can assign a code block that indicates the action we want to execute when the tweet completes (the user either sends or cancels the tweet). A code block is somewhat like a method, in that it receives a parameter and consists of a block of code. The basic format of a code block is the ^ character, followed by the parameter that the code block receives, followed by a block of code enclosed in curly braces. The code block in this case would resemble the following:

controller.completionHandler= ^(TWTweetComposeViewControllerResult result) { // insert code block code here };

Putting all of this together, we: 1. instantiate our TWTweetComposeViewController object 2. call it setInitialText: method to set the initial text for the tweet 3. set its completionHandler property, specifying a code block to be executed 4. present the viewController

- (IBAction) tweet { // check to see if computer is configured to send tweets (use Settings to log in to Twitter) if([TWTweetComposeViewController canSendTweet]) { TWTweetComposeViewController *controller=[[TWTweetComposeViewController alloc] init]; [controller setInitialText:@""];

// Specify a code block to execute in response to completion controller.completionHandler= ^(TWTweetComposeViewControllerResult result) { [self dismissViewControllerAnimated:YES completion:NULL]; if(result==TWTweetComposeViewControllerResultDone) { label.text=@"tweet has been sent"; } else { label.text=@"cancelled"; } };

[self presentViewController:controller animated:YES completion:NULL]; } else { label.text=@"can not tweet"; } }

Note that the presentViewController:animated:completion: method allows the option of specifying a code block to be executed when that method finishes presenting its viewController. The corresponding dismissViewControllerAnimated:completion: method does as well. If you do not want to specify such a code block, pass NULL.

And now, tweet away! page 2.167

Project name: Textfields1-

TextFields1

This project demonstrates animation, using the class methods of the UIView class to move textfields on a screen so that they are always in the top half, so you can see them. The project also demonstrates implementation of the UITextFieldDelegate protocol (providing detailed handling of the events that make up the life of editing a textfield), and different keyboard types.

1. Create a Single View Application.

2. In your interface file, define a UIView object, and five UITextField objects.

#import

@interface ViewController : UIViewController {

IBOutlet UIView *container;

IBOutlet UITextField *name; IBOutlet UITextField *email; IBOutlet UITextField *url; IBOutlet UITextField *phone; IBOutlet UITextField *age; }

@end

The UIView object will server as a “container” into which we will add the labels and textfields for our app. When we move that container, it will also move all of the subviews (UILabels and UIButtons) in the container.

3. Click your .xib file to swap to open it in the Editor pane. Once there, drag a UIView object onto your view. Next, drag five UILabel objects and five UIButton objects onto your view. Arrange them as you want. Change the titles of your labels as you want.

page 2.168

4. In the Dock, you will see all of your subviews underneath your main view. Drag each of your UILabel and UIButton objects so that they are subviews underneath your added UIView rather than the original UIView. You should be able to achieve the structure shown here, although it may take some trial and error practice.

The structure here is very important – you want your labels and buttons to be subviews of the UIView that you added yourself – you are going to use that UIView as a container so that you can easily move all of the subviews simply by moving the container.

5. Make your connections for your UIView and each of your UIButton objects. Click on File’s Owner to highlight it, click on the Connections tab of the Inspector, and draw each of your connections from the Inspector to the corresponding object.

Tip: Make the UIView connection by drawing the connection from your outlet named container to the word View in the Dock.

6. We want to respond to the user’s tapping in a textfield – we will respond by scrolling our container up or down so that the “current” textfield is always in a constant location on the screen. To do this, we will implement the UITextFieldDelegate protocol.

There are three steps to implementing any delegate protocol: 1. say that you are going to implement the protocol 2. tell the appropriate object that you will be its delegate 3. over-ride any desired protocol methods

Step 1 is relatively easy: In your interface file, at the end of the @interface directive, enclose the name of the protocol in angle brackets:

@interface ViewController : UIViewController { page 2.169

Important concept: This establishes the all-important “is a” relationship between your class (ViewController) and UITextFieldDelegate – your class is a UITextFieldDelegate, just as it is a UIViewController (subclassing a class and implementing a protocol are similar in this regard – they both establish an is a relationship).

Step 2 is relatively easy: In your viewDidLoad method, tell each one of your textfields that you will be their delegates:

- (void)viewDidLoad { [super viewDidLoad];

name.delegate=self; email.delegate=self; url.delegate=self; phone.delegate=self; age.delegate=self; }

Step 3 can be complex, depending on what you want to do as part of serving as an object’s delegate. In this step, we look up the documentation of the protocol (of UITextFieldDelegate) and look at its various instance methods. These methods respond to various types of events, most of which are relatively obvious by their names:

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField - (void)textFieldDidBeginEditing:(UITextField *)textField - (BOOL)textFieldShouldEndEditing:(UITextField *)textField - (void)textFieldDidEndEditing:(UITextField *)textField

If you want to respond to the event that occurs when one of your textfields “should begin editing”, over-ride the first of these events. (Note that this method returns a BOOL, so in your method that you write, you can determine if you in fact want the textfield to begin editing or not, and return YES or NO accordingly.) You can over-ride any or all of UITextFieldDelegate’s instance methods, as you need for your desired functionality.

In our case, we want to respond to the event that occurs when the user taps in a textfield – the textfield should begin editing. We want to respond by moving our container up or down.

Copy/paste the method header from the documentation into your implementation file.

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { }

Copy/paste is really the way to go in setting up this method. Major warning: If you try to type this method header, and make any mistake, such as typing textfieldShouldBeginEditing instead of textFieldShouldBeginEditing (capital F), you will not get a syntax error, you will not have an execution error, but you will have a logic error (no error message). The situation will be that there IS a textFieldShouldBeginEditing page 2.170

method (the default one), and that is the method that is being called. Instead of over-riding that method, you just wrote your own method, by a different name, and it is NOT being called. Copy/paste that method header from the documentation!

Now, as to our action within this method, we want to move our “container” up or down the screen. We want to move it based on the y location of the textfield that the user tapped. We chose to always move our container such that the “current” textfield was in a fixed location – we chose the location of our original top textfield as the fixed location. Our top textfield happened to be at y coordinate 45. We then need one calculation, and one animation block.

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { NSInteger top=45-textField.frame.origin.y;

[UIView beginAnimations:nil context:nil]; container.frame=CGRectMake(0,top,container.frame.size.width,container.frame.size.height); [UIView commitAnimations];

return YES; }

Run your program. It should scroll the textfields!

7. We can add several niceties. For one, when you tap in a textfield, the keyboard automatically responds (as a matter of fact, like an EMT, the keyboard is a textfield’s first responder). We don’t have a good way to hide the keyboard. One possibility would be to insert a clear button, on top of our container UIView but behind the other subviews. If you look back at the Dock display earlier in this document, you will see that we do have one additional button at the top of the subview list. Drag a UIButton onto your view, and move it up to the top of your list of subviews. In the Inspector, change the type of the button to Custom – the default color of a Custom button is clearColor.

page 2.171

8. In ViewController.h, define a method named hidekeyboard:

#import

@interface TextfieldsViewController : UIViewController {

IBOutlet UIView *container;

IBOutlet UITextField *name; IBOutlet UITextField *email; IBOutlet UITextField *url; IBOutlet UITextField *phone; IBOutlet UITextField *age; }

- (IBAction) hideKeyboard;

@end

9. Connect this method to your Custom button, responding to touchDown.

10. In your implementation file, write your hideKeyboard method. It’s quite simple – tell each of your textfields that they should resign their first responder:

- (IBAction) hideKeyboard { [name resignFirstResponder]; [email resignFirstResponder]; [url resignFirstResponder]; [phone resignFirstResponder]; [age resignFirstResponder]; }

Run your program again. Now you should be able to tap anywhere on your background, and the keyboard should disappear.

11. You will notice that when we tap on the background to hide the keyboard, the container does not scroll back down into its original location. We probably want it to. Modify the hideKeyboard method, adding an animation block to move the container back to its original location:

- (IBAction) hideKeyboard { [name resignFirstResponder]; [email resignFirstResponder]; [url resignFirstResponder]; [phone resignFirstResponder]; [age resignFirstResponder];

[UIView beginAnimations:nil context:nil]; container.frame=CGRectMake(0,0,container.frame.size.width,container.frame.size.height); [UIView commitAnimations]; }

page 2.172

12. Another nicety would be to display an “appropriate” keyboard for various types of input. If you look up the documentation of the UITextField class, you will see that it “conforms to” (meaning, implements) the UITextInputTraits protocol. Each textField, then, inherits any properties and methods defined by that protocol. One of those is keyboardType, which has several pre-defined values. Consult the documentation to see the possible pre-defined keyboard types, then assign them to your textfields’ keyboardType properties.

- (void)viewDidLoad { [super viewDidLoad];

container.backgroundColor=[UIColor clearColor];

name.delegate=self; email.delegate=self; url.delegate=self; phone.delegate=self; age.delegate=self;

email.keyboardType=UIKeyboardTypeEmailAddress; url.keyboardType=UIKeyboardTypeURL; phone.keyboardType=UIKeyboardTypePhonePad; age.keyboardType=UIKeyboardTypeNumberPad; }

Test it out.

13. We should make one more change, mostly to enhance the flexibility and maintainability of our program. We have hard-coded a value of 45 into our textFieldShouldBeginEditing method (45 is the original y coordinate of our top textfield). If we get into Interface Builder and move that top textfield, we have to go back to Xcode and change the 45. We can do better than this.

In your interface file, define an int. Now, our entire interface file is:

#import

@interface TextfieldsViewController : UIViewController {

IBOutlet UIView *container;

IBOutlet UITextField *name; IBOutlet UITextField *email; IBOutlet UITextField *url; IBOutlet UITextField *phone; IBOutlet UITextField *age;

int base; }

- (IBAction) hideKeyboard;

@end

page 2.173

In viewDidLoad, programmatically get the initial y coordinate of the top textfield. (While we’re at it, change the background color of our container to clearColor.) The entire viewDidLoad method is:

- (void)viewDidLoad { [super viewDidLoad];

container.backgroundColor=[UIColor clearColor];

name.delegate=self; email.delegate=self; url.delegate=self; phone.delegate=self; age.delegate=self;

email.keyboardType=UIKeyboardTypeEmailAddress; url.keyboardType=UIKeyboardTypeURL; phone.keyboardType=UIKeyboardTypePhonePad; age.keyboardType=UIKeyboardTypeNumberPad;

base=name.frame.origin.y; }

Finally, in textFieldShouldBeginEditing, change the hard-code 45 to user our new variable.

- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField { NSInteger top=base-textField.frame.origin.y;

[UIView beginAnimations:nil context:nil]; container.frame=CGRectMake(0,top,container.frame.size.width,container.frame.size.height); [UIView commitAnimations];

return YES; }

14. At some point during your testing, you will notice that tapping on the background does not always seem to work when you edit the bottom textfield and try to tap just above the keyboard. To fix this, open your .xib file, and change the height of both your added UIView and your custom button to something like 960. You can actually make it as long as you want, and add more and more textfields for data entry.

And now, we have a pretty good data entry screen. It could, of course, be as long as we wanted.

page 2.174

Project name: Parks-

Parks

This project shows how to use MKMapView to display scrollable maps, complete with the ability to zoom in and out, and even to annotate locations on the map with your own markings. This could be ideal for a business with locations to advertise, to locate your friends and/or enemies, and more.

The MapKit framework provides several classes and protocols that you can use to display maps within your application. These classes are relatively easy to use, with the most difficult part being annotating maps with your own markings. This project explains how to do it all.

1. Create a Single View Application.

2. In your viewController’s interface file, define a UITableView object, using the IBOutlet keyword so the object will be recognized by Interface Builder (so you can make an easy connection).

So far, the interface file is small:

#import

@interface ViewController : UIViewController {

IBOutlet UITableView *table;

}

@end 3. Click your .xib file to swap to Interface Builder.

4. Drag a UITableView object onto your view. Size it as you wish.

page 2.175

5. In the Dock, click on File’s Owner to highlight it. In the Inspector’s Connections tab (the rightmost tab), draw a connection from your variable named table to the graphical tableView object. (We always draw from the Inspector, to the graphical object.)

6. We need to do all of the normal work to populate a table. We need to implement two protocols: UITableViewDataSource and UITableViewDelegate. Indicate that you will implement these two protocols by listing them, in angle brackets, at the end of your @interface directive (refer to the next page for sample code).

We will create a text file, and store it in our application bundle, then use that text file to provide the data that we will use to populate the table. We will use the UtilityFileIO class, first made available in the File I/O projects. A brief overview of using UtilityFileIO is included at the end of this project.

page 2.176

7. Define an NSArray that you can read the text file information into.

Your interface file now looks something like this:

#import #import "UtilityFileIO.h"

@interface ViewController : UIViewController {

IBOutlet UITableView *table; NSArray *parks; }

@end

8. In your implementation file's viewDidLoad method, read the text file into your array.

Indicate that you will serve as your table’s dataSource and delegate.

- (void)viewDidLoad { [super viewDidLoad];

parks=[UtilityFileIO readBundleFileIntoArray:@"NationalParks.txt"];

table.dataSource=self; table.delegate=self; }

9. You said that you would implement two protocols. Now do it. Over-ride several methods that are defined by those two protocols. (We will assume that you know how to do this for UITableView objects. If you don’t, please refer to the TableView projects for more detail.)

You need to over-ride three UITableViewDataSource protocol methods, to indicate: - the number of sections in your table - the number of rows in each section of your table - the contents to display for each tableViewCell

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return parks.count; }

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

NSString *cellIdentifier = @"cell"; UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; } NSString *string=[parks objectAtIndex:indexPath.row]; NSArray *fields=[string componentsSeparatedByString:@"\t"]; cell.textLabel.text=[fields objectAtIndex:0]; return cell; } page 2.177

One note of explanation is in order. Our text file contains data that shows the location of various national parks, with one record in the file for each park. Each record consists of seven variable-length, delimited fields. The fields are separated by tab characters (the tab character is the delimiter). The tableView:cellForRowAtIndexPath: method splits one of these records, based on the tab delimiter, into seven fields, then uses field #0 (the park name) to display in the table cell.

10. We need to implement one UITableViewDelegate protocol method:

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

NSString *string=[parks objectAtIndex:indexPath.row];

Map *controller=[[Map alloc] initWithNibName:@"Map" bundle:nil]; [self presentModalViewController:controller animated:YES]; [controller setup:string];

[tableView deselectRowAtIndexPath:indexPath animated:YES]; }

This method responds to tapping on a table cell. It gets the appropriate element from our parks array. It then instantiates an instance of our Map class, which we will need to write in upcoming steps. Map will be a subclass of UIViewController. About the easiest way to display a new viewController is with the presentModalViewController: method, which is what we do here. We then call a method of that viewController – setup: – and pass it the entire string of park information. Passing data from one class to another is a vital concept in object-oriented programming, and is demonstrated here. If you have a reference to an object – a way to refer to the object – such as the name controller, then you can operate on that object via its properties and methods.

11. We need to go back and import the Map class’s header file. Our complete interface file is now:

#import

#import "Map.h" #import "UtilityFileIO.h"

@interface ViewController : UIViewController {

IBOutlet UITableView *table; NSArray *parks;

}

@end

page 2.178

12. Our complete implementation file is:

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad { [super viewDidLoad];

parks=[UtilityFileIO readBundleFileIntoArray:@"NationalParks.txt"];

table.dataSource=self; table.delegate=self; }

#pragma mark - #pragma mark TableViewDataSource methods

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; }

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return parks.count; }

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

NSString *cellIdentifier = @"cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; if(cell==nil) { cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier]; }

NSString *string=[parks objectAtIndex:indexPath.row]; NSArray *fields=[string componentsSeparatedByString:@"\t"]; cell.textLabel.text=[fields objectAtIndex:0];

return cell; }

#pragma mark - #pragma mark TableViewDelegate methods

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSString *string=[parks objectAtIndex:indexPath.row];

Map *controller=[[Map alloc] initWithNibName:@"Map" bundle:nil]; [self presentModalViewController:controller animated:YES]; [controller setup:string];

[tableView deselectRowAtIndexPath:indexPath animated:YES]; }

@end

page 2.179

13. Create a new class named Map. Control-click on your project folder, then select New File. Create a UIViewController subclass. Name it Map.

Be sure to check the checkbox for "With XIB for user interface". page 2.180

14. Add the MapKit framework to your project. Click on your project, then Build Phases. In the Link Binary with Libraries section, click the small + sign.

From the list of available frameworks and libraries, select MapKit.

15. In your Map.h file, import MapKit’s header file.

16. We will implement the MKMapViewDelegate protocol. List the protocol, in angle brackets, at the end of your @interface directive. (As you know, a delegate protocol acts on behalf of some other object. Typically, it responds to some sort of event or notification that occurs on some other object.)

17. Define an MKMapView object, that we can connect in Interface Builder. Define a UILabel object, which we can use to display our park name. Define a setup: method (which we called in a previous step), and a close method that will dismiss the Map viewController.

Your Map.h interface file at this points looks like this (this includes a BOOL variable that will be discussed later):

#import #import

@interface Map : UIViewController {

IBOutlet MKMapView *map; IBOutlet UILabel *park;

BOOL loaded; }

- (void) setup:(NSString *)string; - (IBAction) close;

@end page 2.181

18. Click on Map.xib to swap over to Interface Builder. Drag an MKMapView onto your view, and connect your map variable to the mapView object.

19. In the Inspector’s Attributes tab, check the mapView object’s Shows User’s Location property. When you do this, the map will automatically use CoreLocation to get your current location, and display it on the map – you don’t need to add any frameworks, or do anything other than check that checkbox!

20. Drag a UIButton onto your view. Connect your close method to the button, responding to the TouchDown event. (We used a UIBarButtonItem, on top of a UINavigationBar; we don’t really have any navigation going on, but it was a convenient and attractive place to put the button).

Drag a UILabel onto your view, where you can display your park name. Make your connections.

page 2.182

21. Your Map.m's viewDidLoad method needs to try to load a preliminary map – sort of a headstart on loading a map.

An MKCoordinateRegion is a C structure (C, not Objective-C) that itself contains two C structures: a center and a span. Each of these two is itself a structure. center contains a latitude and a longitude. span contains a latitudeDelta and a longitudeDelta (the span determines how much land area the displayed map covers – the larger the delta values, the more land area is displayed on the map).

We chose to hard-code our own latitude and longitude, and chose a small delta that will display a large portion of the earth. This map will actually be only barely visible to the user, if at all, because the first thing we will do when the user selects a National Park is zoom the map in to that park. Creating this initial map in one location, and zooming to the national park map, displays a nice visual effect. Hopefully it will not give you motion sickness.

22. Be sure to set yourself as your map’s delegate.

- (void)viewDidLoad { [super viewDidLoad];

MKCoordinateRegion region={{34.0,-88.0},{1.0,1.0}}; [map setRegion:region animated:NO];

map.delegate=self;

}

23. Your setup: method receives an NSString that has data for the selected park. We need to break that string apart (it’s delimited with tab characters), then use the park name, latitude, and longitude data to re-orient the map. Our data file has latitude and longitude in degrees, minutes, and seconds – we need to convert those to decimal values, which is done in this method.

- (void) setup:(NSString *)string { loaded=NO;

NSArray *fields=[string componentsSeparatedByString:@"\t"];

park.text=[fields objectAtIndex:0];

latitude=abs([[fields objectAtIndex:1] doubleValue]) + [[fields objectAtIndex:2] doubleValue]/60 + [[fields objectAtIndex:3] doubleValue]/3600;

if([[fields objectAtIndex:1] doubleValue]<0) { latitude=-latitude; }

longitude=abs([[fields objectAtIndex:4] doubleValue]) + [[fields objectAtIndex:5] doubleValue]/60 + [[fields objectAtIndex:6] doubleValue]/3600;

if([[fields objectAtIndex:4] doubleValue]<0) { longitude=-longitude; }

} page 2.183

Go back to your interface file and define two double variables – latitude and longitude. By defining the variables in your interface file, they will be available in multiple methods. In a way, this allows one method to “communicate” with another. This has to do with the scope of the variable.

24. We have one MKMapViewDelegate method that we need to write. This method is called repeatedly as we move the map around. We want to load the map only one time, so we will use a BOOL variable (loaded), and set it to YES as soon as we do in fact load the map. All remaining times that this method is called, it will not load the map again, because !loaded will evaluate to NO (if loaded is YES, then !loaded is NO).

- (void)mapViewDidFinishLoadingMap:(MKMapView *)mapView { if(!loaded) { loaded=YES; MKCoordinateRegion region={{latitude,longitude},{10.0,10.0}}; [map setRegion:region animated:YES]; } }

25. Only one method left to write, the close method. It needs to dismiss the map viewController, which will return to our table.

- (IBAction) close { [self dismissModalViewControllerAnimated:YES]; }

26. Drag NationalParks.txt from the accompanying project into your project, then test your program!

27. Wouldn’t it be super cool to place stick pins and text labels on our parks? We can do that with annotations.

We will need to create two new classes. We will create a ParkAnnotation class that is a subclass of NSObject, and a ParkAnnotationPin class that is a subclass of MKPinAnnotationView.

page 2.184

28. Create a ParkAnnotation class, a subclass of NSObject. An instance of this class will represent an annotation for our map. Control-click on your project folder, click on New File, and create an Objective-C class.

In the select list, select a superclass of NSObject (we are creating a subclass of NSObject, thus it is our immediate superclass).

An instance of this class needs to have two properties: a map coordinate, and a title to display. It also needs to have an init method with which we can initialize the instance, and a method to retrieve the title.

This class will implement the MKAnnotation protocol, so much of our work involves matching items defined in the protocol. Our ParkAnnotation.h interface file is:

#import #import

@interface ParkAnnotation : NSObject {

CLLocationCoordinate2D coordinate; NSString *annotationTitle;

} @property(nonatomic) CLLocationCoordinate2D coordinate; @property(retain) NSString *annotationTitle;

-(id)initWithLatitude:(double)latitude longitude:(double)longitude title:(NSString *)thisTitle;

-(NSString *)title;

@end page 2.185

29. The implementation file has only two methods: an init method, and a method to retrieve the annotation’s title. (Be sure to include an @synthesize for each of your properties.)

@implementation ParkAnnotation

@synthesize coordinate; @synthesize annotationTitle;

-(id)initWithLatitude:(double)latitude longitude:(double)longitude title:(NSString *)thisTitle { self=[super init]; CLLocationCoordinate2D center={latitude,longitude}; self.coordinate=center; self.annotationTitle=thisTitle;

return self; }

- (NSString *)title { return self.annotationTitle; }

@end

30. The ParkAnnotationPin class will subclass MKPinAnnotationView, and may be one of the easiest interface files you have ever written. Create this new file as a subclass of NSObject (just as you did the ParkAnnotation class), but then edit the @interface directive to subclass MKPinAnnotationView rather than NSObject.

#import #import

@interface ParkAnnotationPin : MKPinAnnotationView {

}

@end

31. The implementation file simply needs to add our desired specific functionality that complements the common functionality that we inherit from our superclass. In particular, we want to add a UILabel to the pin.

#import "ParkAnnotationPin.h"

@implementation ParkAnnotationPin

- (id) initWithAnnotation:(id )thisAnnotation reuseIdentifier:(NSString *)thisIdentifier { self=[super initWithAnnotation:thisAnnotation reuseIdentifier:thisIdentifier];

UILabel *label=[[UILabel alloc] initWithFrame:CGRectMake(10,8,300,30)]; label.backgroundColor=[UIColor clearColor]; label.text=[thisAnnotation title]; [self addSubview:label];

return self; }

@end

page 2.186

32. Now we need to go back and create the annotations. Our Map class will need to refer to both ParkAnnotation objects and ParkAnnotationPin objects, so we need a couple of import statements. In our implementation file (Map.m), we will also now need to read the data from our text file, so we also need to import UtilityFileIO. Thus, the now-complete Map.h file contains:

#import #import

#import "ParkAnnotation.h" #import "ParkAnnotationPin.h"

#import "UtilityFileIO.h"

@interface Map : UIViewController {

IBOutlet MKMapView *map; IBOutlet UILabel *park; BOOL loaded; double latitude; double longitude; }

- (void) setup:(NSString *)string; - (IBAction) close;

@end

33. In Map.m’s viewDidLoad method, we need to read the data from our text file, and store annotations with the map. Read the file into an array, then step through the elements in the array. For each element (which corresponds to each record from the original data file), break the record into its seven fields, the calculate the latitude and longitude for this park. Use the park name, latitude, and longitude to instantiate a ParkAnnotation object, then add that object to the map’s annotation list (call the addAnnotation: method).

NSArray *parks=[UtilityFileIO readBundleFileIntoArray:@"NationalParks.txt"];

for(NSInteger i=0;i

NSArray *fields=[[parks objectAtIndex:i] componentsSeparatedByString:@"\t"];

latitude=abs([[fields objectAtIndex:1] doubleValue]) + [[fields objectAtIndex:2] doubleValue]/60 + [[fields objectAtIndex:3] doubleValue]/3600; if([[fields objectAtIndex:1] doubleValue]<0) { latitude=-latitude; }

longitude=abs([[fields objectAtIndex:4] doubleValue]) + [[fields objectAtIndex:5] doubleValue]/60 + [[fields objectAtIndex:6] doubleValue]/3600; if([[fields objectAtIndex:4] doubleValue]<0) { longitude=-longitude; }

ParkAnnotation *annotation=[[ParkAnnotation alloc] initWithLatitude:latitude longitude:longitude title:[fields objectAtIndex:0]]; [map addAnnotation:annotation]; }

page 2.187

34. One more method to implement. MKMapViewDelegate defines a method which will provide annotations to the map. This method operates much like a UITableViewDataSource method, in that it will create and re-use annotation objects as the scroll in and out of view. You would probably need to modify only two things: replace our ParkAnnotationPin with the name of your own subclass that you created, and set the pinColor to red or purple if you prefer that to green (red, purple, and green are the only available colors).

-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation { NSString *pinIdentifier=@"pin"; ParkAnnotationPin *view= (ParkAnnotationPin *)[map dequeueReusableAnnotationViewWithIdentifier:pinIdentifier]; if(view==nil) { view=[[ParkAnnotationPin alloc] initWithAnnotation:annotation reuseIdentifier:pinIdentifier]; }

[view setPinColor:MKPinAnnotationColorGreen];

return view; }

With this, you have a working mapping program, that you can probably apply to many situations. It was a lot of steps, but we believe that this is a pretty cool program, especially because we really like the national parks.