0% found this document useful (0 votes)
3 views403 pages

Learn Ios7 Programming From Scratch

Uploaded by

yves.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views403 pages

Learn Ios7 Programming From Scratch

Uploaded by

yves.hong
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 403

TABLE OF CONTENTS

Preface
What You Need to Start iOS Development
Chapter 1: Say Hello World ......................................................................................................................... 10
Chapter 2: Hello World App Explained ...................................................................................................... 27
Chapter 3: Creating a Simple Table App .................................................................................................... 34
Chapter 4: Customizing Table View Using Prototype Cell ......................................................................... 51
Chapter 5: Table Row Selection .................................................................................................................. 71
Chapter 6: Table Row Deletion and Model-View-Controller ..................................................................... 81
Chapter 7: Enhance Table App With Property List .................................................................................... 88
Chapter 8: Launch Image ............................................................................................................................99
Chapter 9: Navigation Controller .............................................................................................................. 107
Chapter 10: Passing Data Between View Controllers ................................................................................ 117
Chapter 11: Tab Bar Controller and Web View ......................................................................................... 126
Chapter 12: Object Oriented Programming - An Introduction ................................................................ 143
Chapter 13: Beautify the Recipe App ......................................................................................................... 154
Chapter 14: Adding Search Bar ................................................................................................................. 167
Chapter 15: Grid Layout Using UICollectionView .................................................................................... 180
Chapter 16: UICollectionView Part 2 ........................................................................................................ 191
Chapter 17: UICollectionView Part 3 ........................................................................................................ 203
Chapter 18: Introduction to Core Data ..................................................................................................... 219
Chapter 19: Core Data Part 2 .................................................................................................................... 235
Chapter 20: Localization .......................................................................................................................... 245
Chapter 21: Static Table View ................................................................................................................... 261
Chapter 22: Building a Simple RSS Reader App ...................................................................................... 270
Chapter 23: Building a Simple Camera App ............................................................................................ 282
Chapter 24: Video Recording and Playback ............................................................................................. 290
Chapter 25: Local Notification ................................................................................................................. 300
Chapter 26: Introduction to UIPageViewController ................................................................................ 309
Chapter 27: Slide-out Sidebar Menu ........................................................................................................ 327
Chapter 28: Adding a Cloud Backend Using Parse .................................................................................. 345
Chapter 29: Parse Cloud Part 2 ................................................................................................................ 364
Chapter 30: AirDrop ................................................................................................................................. 377
Bonus Chapter: Objective-C Basics .......................................................................................................... 387

i
Copyright © 2014 AppCoda.com
All rights reserved. Please do not distribute or share without permission. No part of
this book or corresponding materials (such as images or source code) may be
distributed by any means without prior written permission of the author.

All trademarks and registered trademarks appearing in this book are the property of
their respective owners.

ii
PREFACE
In September 2011, I released my first iPhone app on the App Store. It’s truly an amazing
experience to develop an app. I have been programming for over 10 years and developed
various kinds of business systems. Yet this is the very first time I put up my own product
and make it available globally. I can’t tell in words how happy when my app went live and
got thousands of downloads.

The iOS and App Store has changed the way we distribute and consume software. It’s the
first time in the computing history that any developer can create an app and deliver it to
millions of devices all on your own. This is a great opportunity for all developers. Everyone
are talking about apps and want to build one. Are you ready for that?

When I first created my iOS app, it’s just like most of you. Despite having years of
programming experience, I knew nothing about Objective-C and iOS programming. From
my experience, the best way to learn a new programming language is to get your hand dirty.
It’s similar to learning a foreign language. You can’t just read a book and teach yourself
Japanese (or other languages). You have to practice, practice and practice! That’s the same
for studying a new programming language. So I decided to create a real app. It took me
several weeks to grasp the basics of iOS programming and developed the app. This turned
out to be a great learning experience.

This book will take you through a similar learning adventure and teach you how to build
your first apps on iOS 7. If you’re planning to read this book in bed and expect to
understand app development completely, this book is not for you. It’s a book for those who
take actions. You’ll get a lot of hands-on exercises and projects. You’ll need to code, debug
and build some real apps. There are a lot of works to do but it would be a fantastic
experience and you’ll master the fundamentals of iOS 7 programming along the way.

Thanks for picking up this book. I hope you’ll enjoy reading it and that you’ll soon put up
your first iOS app on App Store. If you’d like to share with me about your first app, drop me
email at [email protected]. I love to hear from you.

Simon Ng
Founder of appcoda.com

iii
WHAT YOU NEED TO START
IOS APP DEVELOPMENT
So you want to create your own app? That’s great! Creating an app is a fun and rewarding
experience. But before we begin to dive into iOS programming, let’s first go through the
tools you need to build an app.

1. Get a Mac
Yes, you need a Mac. It’s the basic requirement for iOS development. To develop an iPhone
(or iPad) app, you need to first get a Mac with Intel-based processor running on Mac OS X
version 10.8 (or up). Probably you still own a PC, the cheapest option is to purchase the
Mac Mini. As of this writing, the retail price of the entry model is US$599. You can hook it
up with the monitor of your PC. The basic model of Mac mini comes with 2.3GHz dual-core
Intel Core i5 processor and 4GB memory. It should be well enough to run the iOS
development tool smoothly. Of course, if you have more budget, get the higher model or
iMac with better processing power.

2. Register an Apple Developer Account


Don’t mix this up with the paid iOS Developer Program that we’re going to talk about in
later section. Everyone can register as an Apple developer for free. By registering the
developer account, you’re allowed to download Xcode, access documentation of the iOS
SDK and other technical resources such as development videos.

You can go to Apple’s developer website (https://developer.apple.com/programs/register/)


for registration. The registration process is very straightforward. Simply create an Apple ID
(if you don’t have) and fill in your personal profile.

iv
3. Install Xcode
To start developing iOS apps, Xcode is the only tool you need to download. Xcode is an
integrated development environment (IDE) provided by Apple. Xcode provides everything
you need to kick start your app development. It already bundles the latest version of iOS
SDK (short for Software Development Kit), a built-in source code editor, graphic user
interface (UI) editor, debugging tools and many more. Most importantly, Xcode comes with
an iPhone (and iPad) simulator so you can test your app even without the physical devices.

v
To download Xcode, launch Mac App Store on your Mac. If you’re using the latest version of
Mac OS, you should be able to open the Mac App Store from the icon in the dock. In case
you can’t find it, you may need to upgrade the Mac OS.

In the Mac App Store, simply search “Xcode” and click “Free” button to download it.

Once you complete the installation process, you’ll find the Xcode folder in the Launchpad.

vi
At the time of this writing, the latest version of Xcode is 5.0.2, which added the support of
iOS 7.0. Throughout this book, we will use this version of Xcode to create our demo apps.
Even you’ve installed Xcode before, I suggest you to upgrade to the latest version. This
should make it easier for you to follow the tutorials.

4. Enroll in iOS Developer Program (Optional)


A common question about developing iOS app is whether you need to enroll in the iOS
Developer Program (https://developer.apple.com/programs/ios/). The short answer is
“optional”. As mentioned earlier, Xcode already includes a built-in iPhone and iPad
simulator. You can develop and test out your app right on your Mac.

Without joining the iOS Developer Program, the simulator is the only mean to run your
apps. You can’t deploy and test the app on your iPhone or iPad. Needless to say, you’re not
permitted to submit your app to App Store.

The simulator is powerful, however, it doesn’t simulate all features of iPhone. For instance,
it doesn’t come with the camera or video capture feature. So if you’re building a camera
app, the only way is to test it on a real iOS device. In other words, you have to join the iOS
Developer Program!

Okay, should you enroll in the program now? The iOS Developer Program costs US$99 per
year. It’s some big money but it’s not cheap either. As you’re reading this book, you’re
probably a new comer and just start exploring iOS development. My suggestion is to first
start with the simulator to test out your app. You can complete most of the exercise in this

vii
book by using the simulator. You can wait until you complete the first 20 chapters before
enrolling in the program.

That’s all for the introduction. Take some time to register your developer account and
install Xcode. When we move onto the next chapter, we’ll start to build an app. So get
ready!

viii
Where to Download the Full Source Code of the Book?
At the end of each chapter, you can find the download link of the Xcode project. If you like,
however, you can download all source code in a single zipped file via the following link:

http://www.appcoda.com/book/resources/ios7_programming_sourcecode.zip

ix
1

SAY HELLO WORLD


I hope you have configured your development environment properly with Xcode installed.
If you haven’t done so, check out the previous chapter about what you need to begin iOS
programming. We’ll use Xcode 5.0.2 to work on all exercises in this book.

You may have heard of “Hello World” program if you have read any programming book
before. It has become the traditional program for first-time learner to create. It’s a very
simple program that usually outputs “Hello, World” on the display of a device. In this

10
chapter, let’s follow the programming tradition and create a “Hello World” app using
Xcode. Despite its simplicity, the “Hello World” program serves a few purposes:

• It gives you a better idea about the syntax and structure of Objective C, the programming
language of iOS.
• It also gives you a basic introduction of the Xcode environment. You’ll learn how to create
a Xcode project and create user interface with the built-in interface builder.
• You’ll learn how to compile a program, build the app and test it using the Simulator.
• Lastly, it makes you think programming is not difficult. I don’t want to scare you away.

Take a Look at Your First App


Before we go into the coding part, let’s first take a look at our version of the “Hello World”
app. The final deliverable will look like this:

Figure 1-1. Your First iPhone App - Hello World

It’s very simple and shows only a “Hello World” button. When tapped, the app prompts you
a message. That’s it. Nothing complex but it helps you kick off your iOS programming
journey.

11
Start Coding!
First, launch Xcode. If you’ve installed Xcode via Mac App Store, you should be able to
locate Xcode in the LaunchPad. Just click on the Xcode icon to start it up.

Figure 1-2. Xcode icon in the Launchpad

Once launched, Xcode displays a welcome dialog. From here, choose “Create a new Xcode
project” to start a new project:

Figure 1-3. Xcode - Welcome Dialog

Xcode shows you various project template for selection. For your first app, choose “Empty
Application” and click “Next”.

12
Figure 1-4. Xcode Project Template Selection

This brings you to another screen to fill in all the necessary options for your project.

Figure 1-5. Project Options for Hello World App

You can simply fill in the options as follows:

• Product Name: HelloWorld – This is the name of your app.

13
• Company Identifier: com.appcoda – It’s actually the domain name written the other
way round. If you have a domain, you can use your own domain name. Otherwise, you
may use mine or just fill in “edu.self”.
• Class Prefix: HelloWorld – Xcode uses the class prefix to name the class
automatically. In future, you may choose your own prefix or even leave it blank. But for
this chapter, let’s keep it simple and use “HelloWorld”.
• Device Family: iPhone – Just use “iPhone” for this project.
• Use Core Data: [unchecked] – Do not select this option. You do not need Core Data
for this simple project.

Click “Next” to continue. Xcode then asks you where to save the “Hello World” project. Pick
any folder (e.g. Desktop) on your Mac. You may notice there is an option for version
control. Just deselect it. We do
not need to use this option in
this book. Click “Create” to
continue.

As you confirm, Xcode


automatically creates the
“Hello World” project based
on all the options you
provided. The screen will look
like the screenshot shown in
figure 1-7.

Figure 1-6. Pick a folder to save your project

14
Figure 1-7. Main Xcode Window for Hello World Project

Familiarize with Xcode Workspace


Before we move on to code your app, let’s take a few minutes to have a quick look at the
Xcode workspace environment. On the left pane, it’s the project navigator. You can find all
your files under this area. The center part of the workspace is the editor area. You do all the
editing stuffs (such as edit project setting, class file, user interface, etc) in this area
depending on the type of file selected. Under the editor area, you’ll find the debug area. This
area is shown when you run the app. The rightmost pane is the utility area. This area
displays the properties of the file and allows you to access Quick Help. If Xcode doesn’t
show this area, you can select the rightmost view button in the toolbar to enable it.

15
Figure 1-8. Xcode Workspace

Lastly, it’s the toolbar. It provides various functions for you to run your app, switch editor
and the view of the workspace.

Figure 1-9 .Toolbar in Workspace

16
Run Your App for the First Time
Even you haven’t written any code, you can run your app to try out the Simulator. This gives
an idea how you build and test your app in Xcode. Simply hit the “Run” button in the
toolbar.

Figure 1-10. Run Button in Xcode

Xcode automatically builds the app and runs it in the Simulator. This is how the Simulator
looks like:

Figure 1-11. The Simulator

A white screen with nothing inside?! That’s normal. As your app is incomplete, the
Simulator just shows a blank screen. To terminate the app, simply hit the “Stop” button in
the toolbar.

Figure 1-12. Terminate the Running App

17
By default, Xcode sets to use iPhone Retina (3.5-inch) as the simulator. You are free to
select other simulators to test the app. Try to select another simulator and run the app.

Figure 1-13. Various types of iOS device simulators

Back to Code
Okay, let’s move on and start to add the Hello World button to our app. Before we can put a
button, we first need to create a view and its corresponding controller. You can think of a
view as a container that holds other UI items such as button. Go back to the Project
Navigator. Right-click on the HelloWorld folder and select “New File”.

Figure 1-14. Creating a new file in Xcode project

Under the Cocoa Touch category, select the Objective-C class template and click Next.

18
Figure 1-15. Creating the new class using Objective-C class template

Name the new class as HelloWorldViewController and the subclass as UIViewController.


Make sure you check the “With XIB for user interface” option. By selecting this option,
Xcode automatically generates a Interface Builder file for the view controller.

Figure 1-16. Setting the class name and subclass

19
You’ll be prompted with a file dialog. Simply click the Create button to create the class and
XIB. Once done, Xcode generates three new files including HelloWorldViewController.h,
HelloWorldViewController.m and HelloWorldViewController.xib. If you select the
HelloWorldViewController.xib file, you’ll find an empty view similar to figure 17:

Figure 1-17. Adding an empty view

Now we’ll add a Hello World button to the view. In the lower part of the utility area, it
shows the Object library. From here, you can choose any of the UI Controls, drag-and-drop
it into the view. For the Hello World app, let’s pick the button and drag it into the view. Try
to place the button at the center of the view.

20
Figure 1-18. Drag the Round Rect Button to the View

Next, let’s rename the button. To edit the label of the button, double-click it and name it
“Hello World”.

If you now try to run the app, you’ll still


end up with a blank screen. The reason is
that we haven’t told the app to load the
new view. We’ll need to add a few lines of
code to load up the
HelloWorldViewController.xib. Figure 1-19. Renaming the button

Select the AppDelegate.m in Project Navigator. Add the following import statement at the
very beginning of the file:
#import "HelloWorldViewController.h"

In the didFinishLaunchingWithOptions: method, add the following lines of code right after
“self.window.backgroundColor = [UIColor whiteColor]“.
HelloWorldViewController *viewController = [[HelloWorldViewController alloc]
initWithNibName:@"HelloWorldViewController" bundle:nil];
self.window.rootViewController = viewController;

21
Your code should look like this after editing:

What you have just done is to load the HelloWorldViewController.xib and set it as the root
view controller. Now try to run the app again and you should have an app like this:

Figure 1-20. Hello World app with a Button

For now, if you tap the button, it does nothing. We’ll need to add the code for displaying the
“Hello, World” message.

22
Coding the Hello World Button
In the Project Navigator, select the “HelloWorldViewController.h”. The editor area now
displays the source code of the selected file. Add the following line of code before the
“@end” line:
-(IBAction)showMessage;

Your complete code should look like this after editing:


#import <UIKit/UIKit.h>

@interface HelloWorldViewController : UIViewController

-(IBAction)showMessage;

@end

Next, select the “HelloWorldViewController.m” and insert the following code before the
“@end” line:
- (IBAction)showMessage
{
UIAlertView *helloWorldAlert = [[UIAlertView alloc]
initWithTitle:@"My First App" message:@"Hello, World!"
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

// Display the Hello World Message


[helloWorldAlert show];
}

After editing, your code should look like figure 1-21.

23
Figure 1-21. Complete code of HelloWorldViewController.m

Forget about the meaning of the above Objective-C code. I’ll explain to you in the next
chapter. For now, just think of showMessage as an action and this action instructs iOS to
display a “Hello World” message on screen.

Connecting Hello World Button with the Action


But here is the question:

How can the “Hello World” button know what action to invoke when someone taps it?

Next up, you’ll need to establish a connection between the “Hello World” button and the
“showMessage” action you’ve just added. Select the “HelloWorldViewController.xib” file to
go back to the Interface Builder. Press and hold the Control key on your keyboard, click the
“Hello World” button and drag to the “File’s Owner”. Your screen should look like this:

24
Figure 1-22. Connecting Hello World button with code

Release both buttons and a pop-up shows the “showMessage” action. Select it to make a
connection between the button and “showMessage” action.

Figure 1-23. Send Event Pop-up from File's Owner

25
Test Your App
That’s it! You’re now ready to test your first app. Just hit the “Run” button. If everything is
correct, your app should run properly in the Simulator.

Figure 1-24. Hello World app

What’s Coming Next


Congratulation! You’ve built your first iPhone app. It’s a simple app however, I believe you
already have a better idea about Xcode and how an app is developed. It’s easier than you
thought, right?

In the next chapter, we’ll discuss about the details of the Hello World app and explain how
the stuffs work together.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/r20n7ttkta6kcdf/HelloWorld.zip.

26
CHAPTER 2

HELLO WORLD APP


EXPLAINED
“When you learn through coding, [you're]
coding to learn. You're learning it in a
meaningful context, and that's the best
way of learning things.”
~ Mitch Resnick

Isn’t it easy to build an app? I hope you enjoy reading the first chapter and already created
your first iPhone app. Before we continue to build another app, let’s step back and have a
closer look at the Hello World app. It’ll be good for you to understand some of the
Objective-C syntax and the inner workings of the app.

So far you follow the step-by-step procedures to build the Hello World app. But as you go
through the previous chapter, you may come across these questions:

• What are those .xib, .h and .m files?


• What is the “ugly” code inside “showMessage”? What does it mean?!
• What actually happens after you tap the “Hello World” button? How does the button
trigger the “showMessage” action?
• How does the “Run” button in Xcode work?
I want you to focus on exploring the Xcode environment so I didn’t explain any of the above
questions. Yet it’s essential for every developer to understand the inner details behind the
code and grasp the basic concept of iOS programming. For some technical concepts, they

27
may be a bit hard to understand particularly you have no programming background. Don’t
worry, however. This is just a start. As you move on and write more code in later chapters,
you’ll get better understanding about iOS programming. Just try your best to learn as much
as possible.

Interface Builder, Header and Implementation Files


First, what are those .xib, .h and .m files? Under the Project Navigator, you should find
three main types of files – .xib, .h and .m. (If you expand the “Supporting Files” folder,
you’ll find other file types such as plist and framework. But for now, let’s forget about them
first. We’ll talk about them later.)

.xib – For files with .xib extension, they’re Interface Builder files that store the
application’s user interface (UI). As you click on the .xib file, Xcode automatically switches
to the Interface Builder for you to edit the UI of the app via drag-and-drop.

Figure 2-1. Interface Builder in Xcode

.h and .m – Files with .h extension refers to the header files while those with .m extension
are the implementation files. Like most of the programming languages, the source code of
Objective-C is divided into two parts: interface and implementation.

Well, to put in analogy that you can better understand both terms, let’s consider a TV
remote. It’s convenient to control the volume of a TV set wirelessly with a remote. To
increase the speaker volume, you press the “Volume +” button. To switch channel, you
simply key in the channel number. Let me ask you. Do you know what happens behind the
scene when pressing the “Volume” button? Probably not. I believe most of us don’t know
how the remote communicates with the TV set and controls the speaker volume. What we

28
just know is, that button is used for changing the volume. In this example, the button that
interacts with you is the “interface” and the inner detail which is hidden behind the button
is referred as the “implementation”.

Now you should have a better idea about interface and implementation. Let’s go back to the
code. In Objective-C, interfaces of a class are organized in “.h” file. We use the syntax
“@interface” to declare the interface of a class. Take a look at the
HelloWorldViewController.h, which is the header file:
@interface HelloWorldViewController : UIViewController

-(IBAction)showMessage;

@end

It starts with @interface followed by HelloWorldViewController, which is the class name.


Inside, it declares a “showMessage” action, which is known as a method call.
Like the “Volume” button, apparently we do not know how the “showMessage” action
works. You just know it’s used to display a message on screen. The actual implementation is
put in the HelloWorldViewController.m, the implementation file:
@implementation HelloWorldViewController

// I've removed other methods for better reading. Focus on the showMessage method first.

- (IBAction)showMessage
{
UIAlertView *helloWorldAlert = [[UIAlertView alloc]
initWithTitle:@"My First App" message:@"Hello, World!"
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

// Display the Hello World Message


[helloWorldAlert show];
}

@end

As you can see from the above, you use the declaration @implementation to declare an
implementation. Inside the “showMessage”, it’s the actual code defined to display the alert
message on screen. You may not understand every single line of code inside the
“showMessage” method. In brief, it creates an UIAlertView with “My First App” as the title
and “Hello, World” as the message. It then calls up the “show” method and request iOS to
display the pop-up message on screen.

29
Behind the Touch and Tap
What actually happened after tapping the “Hello World” button? How does the “Hello
World” button invoke the “showMessage” method to display the “Hello World” message?

Recalled that you established a connection between the “Hello World” button and the
“sendMessage” action in Interface Builder. Try opening up the
“HelloWorldViewController.xib” again and select the “Hello World” button. Click the “Sent
Events” button in the Utility area to open the Sent Events.

Figure 2-2. Hello World App

30
Figure 2-3. Sent events under Utility area

The Sent Events section shows you all connections between events and actions. As you can
see in the above figure, the “Touch Up Inside” event is connected to the “showMessage”
action. In iOS, apps are event-driven. The control/object (e.g. UIButton) listens for certain
events (e.g. touches and taps). When the event is triggered, the object calls up the preset
action that associates with the event.

In our Hello World app, when users lift up the finger inside the button, the “Touch Up
Inside” event is triggered. Consequently, it calls up the “showMessage” action to display the
“Hello World” message.

The illustration shown in Figure 2-4 sums up the event flow and what I have just described.

31
Figure 2-4. Event and Message Flow of Hello World App

Behind the Scene of the “Run” Button


When you click the “Run” button, Xcode automatically launches the Simulator and runs
your app. But what happens behind the scene? As a programmer, you have to look into the
entire process.

The entire process can be broken into three


phases: compile, package and run.

Compile – You probably think iOS


understands Objective-C code. In reality, iOS
only reads machine code. The Objective-C code
is only for you, the programmer to write and
read. To make iOS understand the source code
of the app, we have to go through a translation
process to translate the Objective-C code into
machine code. This process is referred as
Figure 2-5. Running an app
“compile”. Xcode already comes with a built-in
compiler to compile the source code.

32
Package – Other than source code, an app usually contains resource files such as images,
text files, xib files, etc. All these resources are packaged to make up the final app.

We used to refer these two processes as the “build” process.

Figure 2-6. Build Process

Run – This actually launches the Simulator and loads your app.

Summary
You should now have a basic idea of how Hello World app works. As a beginner without
prior programming experience, however, it’s not easy to understand all the programming
concepts we just discussed. Don’t worry. As you code more and develop more apps in later
chapters, you’ll get a better idea about Objective C and iOS programming.

In the next chapter, let’s start to build another app using table view.

33
CHAPTER 3

CREATING A SIMPLE
TABLE APP
“Life is really simple, but we insist on
making it complicated.”
~ Confucius

Is it fun to create the Hello World app? In this chapter, we’ll work on something more
complex and build a simple app using Table View.

First, what’s a Table View in iPhone app? Table view is one of the most common UI
elements in iOS apps. Most apps (except games), in some ways, make use of Table View to
display list of data. The best example is the built-in Phone app. Your contacts are displayed
in a table view. Another example is the Mail app. It uses Table View to display your mail
boxes and emails. Not only designed for showing textual data, Table View allows you to
present the data in the form of images. The YouTube and Airbnb apps are great examples
for the usage. Figure 3-1 displays a few table view applications. Though they look different,
all are built using the UITableView component.

34
Figure 3-1. Sample Table View from Snapguide, Digg and Airbnb

Creating a SimpleTable Project


With an idea of table view, let’s get our hands dirty and create a simple app. Don’t just read
the chapter if you’re serious about learning iOS programming. Stop reading, open your
Xcode and code! This is the best way to study programming.

After launching Xcode, create a new project using the “Single View application” template.

35
Figure 3-2. Xcode Project Template Selection

Click “Next” to continue. Again, fill in all the required options for the Xcode project:

• Product Name: SimpleTable – This is the name of your app.


• Company Identifier: com.appcoda – It’s actually the domain name written the other
way round. If you have a domain, you can use your own domain name. Otherwise, you
may use mine or just fill in “edu.self”.
• Class Prefix: SimpleTable – Xcode uses the class prefix to name the class
automatically. In future, you may choose your own prefix or even leave it blank. But for
this project, let’s keep it simple and set it to “SimpleTable”.
• Device Family: iPhone – Just use “iPhone” for this project.

36
Figure 3-3. SimpleTable Project Options

Click “Next” to continue. Xcode then asks you where you saves the “SimpleTable” project.
Pick any folder (e.g. Desktop) to save your project. Like before, deselect the option for
Source Control. Click “Create” to continue. Simply pick a folder to save your project. As you
confirm, Xcode automatically creates the “SimpleTable” project based on the options you’ve
provided. The resulting screen looks like the one shown in figure 3-4.

37
Figure 3-4. Main Screen of SimpleTable Project

Designing the View


First, we’ll create the user interface and add the table view. Select “Main.storyboard” to
switch to the Storyboard interface.

Figure 3-5. Storyboard for SimpleTable Project

38
In the Object Library, select the “Table View” object and drag it into the view.

Figure 3-6. Drag a Table View from Object Library and add it to the view

Resize its height a little bit, so that it doesn’t cover the status bar. Your screen should look
like below after inserting the table view.

What’s Main.storyboard? If you follow the chapter carefully, you’ll notice the use of
Storyboard to design the user interface. This differs from the interface builder (.xib) that
was covered in the previous chapter. Storyboard, introduced in Xcode 4.2, is a modern way
to design user interface. In Xcode, you can either use Interface Builder or Storyboard to
design your views. With Storyboards, all screens are stored in a single file. This gives you a
conceptual overview of the visual representation for the app and shows you how the screens

39
are connected. Since the release of Xcode 5, Apple promotes the use of Storyboard over
Interface Builder. Therefore, we’ll use Storyboard to design an app interface for the rest of
the chapters.

Figure 3-7. Drag a table view from Object Library

Run Your App for the First Time


Before moving on, try to run your app using the Simulator. Click the “Run” button to build
your app and test it.

The Simulator screen will look like the one in figure 3-8.

40
! ! ! ! !
Figure 3-8. SimpleTable App running in the simulator

Easy, right? You already designed the table view in your app. For now, however, it doesn’t
contain any data. Next up, we’ll write some code to add the table data.

Adding Table Data


Go back to the Project Navigator and select “SimpleTableViewController.h”. Append
“<UITableViewDelegate, UITableViewDataSource>” after “UIViewController”. Your code
should look like below:

#import <UIKit/UIKit.h>

@interface SimpleTableViewController : UIViewController <UITableViewDelegate,


UITableViewDataSource>

@end

41
The “UITableViewDelegate” and “UITableViewDataSource” are known as protocol in
Objective-C. Basically, in order to display data in Table View, we have to conform to the
requirements defined in the protocols and implement all the mandatory methods.

UITableViewDelegate and UITableViewDataSource


Earlier, we’ve added the “UITableViewDelegate” and “UITableViewDataSource” protocols
in the header file. It may be confusing. What’re they?

The UITableView, the actual class behind the Table View, is designed to be flexible to
handle various types of data. You may display a list of countries or contact names. Or like
this example, we’ll use the table view to present a list of recipes. So how do you tell
UITableView the list of data to display? UITableViewDataSource is the answer. It’s the link
between your data and the table view. The UITableViewDataSource protocol declares two
required methods (tableView:cellForRowAtIndexPath and
tableView:numberOfRowsInSection) that you have to implement. Through implementing
these methods, you tell Table View how many rows to display and the data in each row.

UITableViewDelegate, on the other hand, deals with the appearance of the UITableView.
Optional methods of the protocols let you manage the height of a table row, configure
section headings and footers, re-order table cells, etc. We do not change any of these
methods in this example. Let’s leave them for the later chapter.

Okay, let’s continue to code the app. Select “SimpleTableViewController.m” and define an
instance variable for holding the table data.

@implementation SimpleTableViewController
{
NSArray *recipes;
}

In the “viewDidLoad” method, add the following code to declare the “recipes” array. We
initialize an array with a list of recipes.
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize table data
recipes = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full Breakfast",
@"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut", @"Starbucks
Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork", @"Japanese
Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and Cheese
Panini", nil];
}

42
What is an array?
An array is a fundamental data structure in computer programming. You can think of an
array as a collection of data elements. Consider the recipes array in the above code, it
represents a collection of textual elements. You may visualize the array like this:

Figure 3-9. recipes array

Each of the array elements is identified or accessed by an index. An array with 10 elements
will have indices from 0 to 9. That means, recipes[0] returns the first element of the
“recipes” array.

In Objective C, NSArray is the class for creating and managing array. You can use NSArray
to create static array for which the size is fixed. If you need a dynamic array, use
NSMutableArray instead.

NSArray offers a set of factory methods to create an array object. In our code, we use
“arrayWithObjects” to instantiate a NSArray object and preload it with the specific elements
(e.g. Hamburger).

You can also use other built-in methods to query and manage the array. Later, we’ll call the
“count” method to query the number of data elements in the array. To learn more about the
usage of NSArray, you can always refer to Apple’s official document (https://
developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/
Classes/NSArray_Class/NSArray.html).

Finally, we have to add two datasource methods: “tableView:numberOfRowsInSection” and


“tableView:cellForRowAtIndexPath”. These two methods are part of the
UITableViewDataSource protocol. It’s mandatory to implement the methods when
configuring a UITableView. The first method is used to inform the table view how many
rows are in the section. So let’s add the below code. The “count” method simply returns the
number of items in the “recipes” array.

43
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [recipes count];
}

Next, we implement the other required methods.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *simpleTableIdentifier = @"SimpleTableCell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}

cell.textLabel.text = [recipes objectAtIndex:indexPath.row];


return cell;
}

The “cellForRowAtIndexPath” is called every time when a table row is displayed. The below
illustration will give you a better understanding about how UITableView and
UITableDataSource work.

44
Figure 3-10. How UITableView and UITableDataSource work together

Okay, let’s hit the “Run” button and try out your final app. Oops! you still got a blank app!
It’s the same as before.

Why is it still blank? We’ve already written the code for generating the table data and
implemented the required methods. But why the Table View isn’t shown up as expected?

There is still one thing left.

Connecting the DataSource and Delegate


Like the “Hello World” button in the first chapter, we have to establish the connection
between the Table View and the two methods we just created.

Go back to the “Main.storyboard”. Select the table view. Press and hold the control key on
your keyboard, click the table view and drag to the “Simple Table View Controller” icon of
the dock. Your screen should look like below:

45
Figure 3-11. Connecting Table View with its Datasource and Delegate

Release both buttons and a pop-up shows both “dataSource” & “delegate”. Select
“dataSource” to make a connection between the Table View and its data source. Repeat the
above steps and make a connection with the delegate.

Figure 3-12. Connect dataSource and delegate

That’s it. To ensure the connections are linked properly, you can select the Table View
again. In the upper part of the Utility area, you can reveal the existing connections in the
“Connection Inspector” (i.e. the rightmost tab).

46
Figure 3-13. Show the Connections Inspector

Test Your App


Finally, it’s ready to test your app. Simply hit the “Run” button and let the Simulator load
your app:

! ! ! ! !
Figure 3-14. SimpleTable App with data

47
Add Thumbnail to Table View
The table view is too plain, right? What about adding an image to each row? The iOS SDK
makes it extremely easy to do this. You just need to add a line of code for inserting a
thumbnail for each row.

First, download this sample image (http://www.appcoda.com/wp-content/uploads/


2012/04/creme_brelee.jpg). Alternatively, you can use your own image but make sure you
name it “creme_brulee.jpg”. In Project Navigator, right-click the “SimplyTable” folder and
select “Add Files to SimpleTable…”.

Figure 3-15. Adding Image to Xcode Project

Select the image file you just downloaded and check “Copy items to destination group’s
folder” checkbox. By selecting the option, the image will be copied to the project folder.
Click “OK” to add the file.

48
Figure 3-16. Pick your image file and add to the project

Now edit the “SimpleTableViewController.m”. Add the following line of code in


“tableView:cellForRowAtIndexPath” method and put it right before “return cell”:

cell.imageView.image = [UIImage imageNamed:@"creme_brelee.jpg"];

Your code should look like this after editing:


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *simpleTableIdentifier = @"SimpleTableCell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}

cell.textLabel.text = [recipes objectAtIndex:indexPath.row];


cell.imageView.image = [UIImage imageNamed:@"creme_brelee.jpg"];

return cell;
}

The table view cell comes with a default property for showing image. The line of code
simply loads the image and display it in the image area of the table cell. Now, hit the “Run”
button again and your SimpleTable app should display the image in each row.

49
! ! ! !
Figure 3-17. SimpleTable App with Image

Summary
It’s simple to create a table view, right? Table view is one of the most commonly used
elements in iOS programming. If you’ve followed the chapter and build the app, you should
have a basic idea about how to create the table view. I try to keep everything simple in the
demo app. In reality, the table data will not be stored directly in the code. Usually, it’s
loaded from file, database or somewhere else. In later chapter, we’ll see how to store and
load the table data from a file. But before moving on, make sure you thoroughly understand
how the table view works. Otherwise, go back to the beginning and study the chapter again.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/lbbzpncj3beb288/SimpleTable.zip.

50
4

CUSTOMIZE TABLE VIEW


USING PROTOTYPE CELL
In the last chapter, we created a simple table view app to display a list of recipes with a pre-
defined image. In this chapter, we’ll beautify the table cell and make the app look better.
There are a number of changes and enhancement we’ll work on together:

1. Rebuild the same app using prototype cell


2. Display individual image for each recipe instead of showing the same thumbnail for all
recipes
3. Custom the table view instead of using the default style of table view cell

51
You may wonder why we need to rebuild the same app. There are always more than one
way to do things. Previously, we used the table view from Object Library to create the table
view. Meanwhile we’ll show you another way to create table view in Xcode. Will it be easier?
It may not be easier if you just want to create a simple table. It, however, allows developers
to customize a table cell easily.

Building Table View Using Prototype Cell


Since the release of iOS 5, Apple introduced Storyboarding that simplifies the way to design
user interfaces for your app. Along with Storyboard, Xcode introduced a new table element
called Prototype Cell. You can create table view without using prototype cell, just like what
we did earlier. The introduction of prototype cell, however, simplifies the way of creating
and designing table cells. You can simply design the cell right inside the Storyboard editor.

We’ll go into the details of cell customization. But first let’s see how we use prototype cell to
re-create the same Simple Table app. To begin, fire up your Xcode! Once launched Xcode,
create a new project using the “Single View application” template. Name the project as
“CustomTable” and fill in all the required options for the Xcode project, just like you did in
the previous chapter.

Figure 4-1. CustomTable Project Options

52
Once you create the Xcode project, select “Main.storyboard” and jump to the Storyboard
Editor. As usual, you should find a default view controller generated by Xcode. We’ll not use
the default controller. So select the view controller and hit the delete button to delete it.

Then drag a Table View Controller from the Object Library and put it into the storyboard.

Figure 4-2. Drag table view controller into storyboard

If you compile and run the app now, you’ll end up with a blank table view app. Obviously,
we haven’t populated the recipe data into the table.

Presently, the table view controller we added is associated with the default
UITableViewController class. In order to populate data, we have to associate it with our
own class.

53
Go back to the Project Navigator and select “CustomTableViewController.h”. The class is
originally extended from UIViewController. This class cannot be used to associate with the
table view controller as it’s not a member of UITableViewController. Replace the
declaration with the following line to make it as a member of UITableViewController:
@interface CustomTableViewController : UITableViewController

Go back to Storyboard and select the table view controller. Under Identity Inspector, set the
custom class to “CustomTableViewController”. Now we have set up the relationship
between the table view controller in the storyboard and the CustomTableViewController
class.

Figure 4-3. Setting the custom class

54
There is one more thing to configure the table view. Select the table view cell. Change the
style to “Basic” and set the identifier as “CustomTableCell”.

Figure 4-4. Changing the style and identifier of table view cell

What we’ve configured is to give the cell a name for later identification. You can think of the
cell as a template. Later when we populate the data in code, iOS uses this cell template to
render the content such that each cell is of the same look and feel. The “Basic” style is used
to display simple table data. We choose this style to re-create the same table view as we did
in the last chapter.

The design is ready. Let’s start to code. Select the “CustomTableViewController.m” in the
Project Navigator and declare an instance variable for holding the table data.
@implementation CustomTableViewController
{
NSArray *recipeNames;
}

In the “viewDidLoad:” method, add the following code to initialize the “recipeNames” array.
We initialize an array with a list of recipes:
- (void)viewDidLoad
{
[super viewDidLoad];

recipeNames = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full


Breakfast", @"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut",
@"Starbucks Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork",
@"Japanese Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and
Cheese Panini", nil];

Again we need to implement two datasource methods -


“tableView:numberOfRowsInSection” and “tableView:cellForRowAtIndexPath”. If you’re
not forgetful, you know these two methods are part of the UITableViewDataSource
protocol.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [recipeNames count];
}

55
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

cell.textLabel.text = [recipeNames objectAtIndex:indexPath.row];


cell.imageView.image = [UIImage imageNamed:@"creme_brelee.jpg"];

return cell;
}

The code is exactly the same as the SimpleTable app except that the identifier is set to
“CustomTableCell”. Do you still remember the identifier we set in the prototype cell? Here
we refer to that cell by using the identifier.

Now, hit the “Run” button and your CustomTable app should look like Figure 4-5.

Figure 4-5. CustomTable App

56
Cool! Does the app look the same as the one we developed before? Yes, exactly! But wait...
it’s not identical. If you look at the app carefully, the first cell is displayed right behind the
status bar. It’s the default behavior of view controllers in iOS 7, in which view controllers
use full-screen layout. That’s why the table view controller takes up the whole screen.

To avoid the table content overlaps with the status bar, add the following code in
“viewDidLoad” method:
[self.tableView setContentInset:UIEdgeInsetsMake(20, self.tableView.contentInset.left,
self.tableView.contentInset.bottom, self.tableView.contentInset.right)];

The line of code simply shifts down the table view a bit. You can run the app again after the
change and it should look like Figure 4-6.

Figure 4-6. CustomTable App after adjustment

Display Different Thumbnails


Now you should have a basic understanding about UITableViewController and Prototype
Cell. Let’s continue to tweak the app. The demo app currently displays the same recipe
image for all table rows. Wouldn’t be great to show different recipe thumbnails?

57
Before we move on to change the code, let’s revisit the code for displaying thumbnail in
table row.

cell.imageView.image = [UIImage imageNamed:@"creme_brelee.jpg"];

The above line code in the “tableView:cellForRowAtIndexPath:” method instructs


UITableView to display “creme_brelee.jpg” for each row. Obviously, in order to show
different images, we need to alter this line of code. As explained before, the
“cellForRowAtIndexPath” method is called by iOS automatically each time before a table
row is displayed.

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath

If you look into the method parameter, it passes the “indexPath” when invoked. The
indexPath parameter contains the row number (as well as the section number) of the table
row. You can simply use “indexPath.row” property to find out which row it currently points
to. Like an array, the count of table row starts from zero. In other words, “indexPath.row”
property returns 0 for the first row of table.

So to display different thumbnails, what we need to do is to add a new array (let’s name it as
recipeImages) that stores the file name of thumbnails:
@implementation CustomTableViewController
{
NSArray *recipeNames;
NSArray *recipeImages;
}

- (void)viewDidLoad
{
[super viewDidLoad];

recipeNames = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full


Breakfast", @"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut",
@"Starbucks Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork",
@"Japanese Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and
Cheese Panini", nil];

recipeImages = [NSArray arrayWithObjects:@"egg_benedict.jpg", @"mushroom_risotto.jpg",


@"full_breakfast.jpg", @"hamburger.jpg", @"ham_and_egg_sandwich.jpg", @"creme_brelee.jpg",
@"white_chocolate_donut.jpg", @"starbucks_coffee.jpg", @"vegetable_curry.jpg",
@"instant_noodle_with_egg.jpg", @"noodle_with_bbq_pork.jpg", @"japanese_noodle_with_pork.jpg",
@"green_tea.jpg", @"thai_shrimp_cake.jpg", @"angry_birds_cake.jpg", @"ham_and_cheese_panini.jpg",
nil];

[self.tableView setContentInset:UIEdgeInsetsMake(20, self.tableView.contentInset.left,


self.tableView.contentInset.bottom, self.tableView.contentInset.right)];
}

58
As you can see from the above code, we initialize the recipeImages array with a list of image
file names. The order of images are aligned with that of the recipeNames.

You can download this image pack* and add them into your project. After adding the image
files, you should find them in the Project Navigator like the screen shown below.

Figure 4-7. Recipe images showing in Project Navigator

Lastly, change the line of code in “cellForRowAtIndexPath” method to:


cell.imageView.image = [UIImage imageNamed:[recipeImages objectAtIndex:indexPath.row]];

What’s [thumbnails objectAtIndex:indexPath.row]?


The line of code retrieves the name of images for the specific row. Say, for the first row,
indexPath.row property returns 0 and we picks the first image (i.e. egg_benedict.jpg) from
the thumbnails array using the “objectAtIndex” method.

After saving all the changes, try to run your app again. It should now display different
thumbnails in the table:

* You can download the image pack from http://www.appcoda.com/download/simpletable_image_pack.zip

59
Figure 4-8. CustomTable App Showing Different Recipe Images

Customize Table View Cell


Does the app look better? We’re going to make it even better by customizing the table cell.
So far we utilize the default style of table view cell. The location and size of the thumbnail
are fixed. What if you want to make the thumbnail bigger and show the preparation time for
each recipe just like the below screen?

60
Figure 4-9. CustomTable App Showing Different Recipe Images

Designing Prototype Cells in Storyboard


When you add a UITableViewController in the storyboard, by
default, you only have an empty prototype cell. The beauty of
prototype cell, as mentioned before, is that it allows
developers to customize the cell right within storyboard. To
build a custom cell, you simply add other UI control elements
(e.g. UILabel, UIImageView) right into the prototype cell.

Let’s first change the style of the cell. You can’t customize the Figure 4-10. Changing the cell
cell when it’s set to the Basic style. So select the cell and style to Custom
change the style to Custom under the Attributes Inspector.

61
In order to accommodate a larger thumbnail, we’ll change the height of the cell. You’ll need
to change both the row height of table view and prototype cell. Select the table view and
change the row height to 71.

Figure 4-11. Changing the row height of table view

Then select the prototype cell and click the “Size” inspector. Check the custom checkbox
and change the row height from 44 to 71.

Figure 4-12. Changing row height in Storyboard

After altering the row height, you can now add other UI elements to the cell. Drag an Image
View from Object Library to the prototype cell. You’re free to resize the image to fit your
need. Select the image view, click “Size” inspector and change the “X”, “Y”, “Width” and
“Height” attributes as shown in Figure 4-13:

Figure 4-13. Add image view to prototype cell

62
Next, we’ll add two labels: Name and Time. While the “Name” label is used to display the
name of recipe, the “Time” label is created to show the actual preparation time for the
specific recipe.

To add a label, select “Label” in Object library and drag it to cell. Name the first label as
“RecipeName”. This label is used to display the name of recipe. In the “Size” inspector, set
“X” as 92, “Y” as 7, “Width” as 215 and “Height” as 23.

You may notice your font size and style are different from the demo app shown above. In
the “Attributes” inspector, change the font type to “Helvetica Neue Condensed Bold” and
set the font size to 21 points.

Figure 4-14. Adding name label to the prototype cell

Drag another label to the cell and name it as “PrepTime”. In the “Size” inspector, set “X” as
92, “Y” as 32, “Width” as 186 and “Height” as 33. Your final design of the prototype cell
should look like Figure 4-15.

Figure 4-15. Add detail label to the prototype cell

Creating a Class for the Custom Cell


So far, we’ve designed the table cell. But how can we change the label values of the
prototype cell? Those values are supposed to be dynamic.

63
We’re going to create a new class to associate with
the cell. This class represents the underlying data
model of the custom cell.

Just like before, right click the “CustomTable”


folder in Project Navigator and select “New
File…”.
Figure 4-16. Creating a new class file
Right after selecting the option, Xcode prompts you to select a template. As we’re going to
create a new class for the custom table view cell, select “Objective-C class” under “Cocoa
Touch” and click “Next”.

Fill in “CustomTableCell” for the class name and select “UITableViewCell” for the “Subclass
of” option.

Figure 4-17. Creating a new class file

Click “Next”, save the file in the CustomTable project folder and click “Create” to continue.
Xcode should create two files named “CustomTableCell.h” and “CustomTableCell.m” in the
Project Navigator.

Open “CustomTableCell.h” and add the following properties before the “@end” line:

64
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@property (nonatomic, weak) IBOutlet UILabel *prepTimeLabel;
@property (nonatomic, weak) IBOutlet UIImageView *thumbnailImageView;

Property and Outlet


The above lines of code defines three instance variables that later to be associated with the
table cell view in storyboard. The keyword “@property” is used to declare a property within
a class in the following form:

@property (attributes) type name;

Referring to the lines of code above, weak and nonatomic are the attributes of the property.
UILabel and UIImageView are the types, while the “nameLabel”, “prepTimeLabel” and
“thumbnailImageView” are the names.

So what’s IBOutlet? You can think of IBOutlet as an indicator. To associate the instances
variables with the elements in the prototype cell in storyboard, we use the keyword
“IBOutlet” to let the prototype cell know that they’re allowed to make connection. Later,
you’ll see how to make a connection between these outlets and the objects in storyboard.

As mentioned before, the CustomTableCell class serves as the data model of the custom cell.
In the cell, we have three values that are changeable: the thumbnail image view, the name
label and the time label. The data model stores and provides the values to be displayed.

You may wonder how the prototype cell in storyboard connects to the CustomTableCell.
You’re right. Presently, there is no connection between the CustomTableCell class and the
prototype cell. This is what we’re going to do next.

Establishing the Connection


By default, the prototype cell is associated with the default UITableViewCell class. The very
first thing we have to do is to change it to the CustomTableCell class we’ve just created.

Select the prototype cell in storyboard. Under Identity Inspector, set the custom class to
CustomTableCell.

Figure 4-18. Changing custom class

65
Next we’ll establish the connections between the properties of the class and the Label /
ImageView created in the Interface. Right click the “CustomTableCell” in Document Outline
of storyboard to display the “Outlets” inspector. Click and hold the circle next to
“nameLabel”, and drag it to the “Label – RecipeName” object. Xcode automatically
establishes the connection.

Figure 4-19. Connecting the outlets

Repeat the above procedures for “prepTimeLabel” and “thumbnailImageView”:

• Connect “prepTimeLabel” with “Label – PrepTime” object


• Connect “thumbnailImageView” with “Image View” object
After you made all the connections, it should look like the screenshot shown in figure 4-20.

66
Figure 4-20. Connecting the outlets

Coding the Table View Controller


We have completed the design and coding for the custom table cell. Finally we come to the
last part of the change – to make use of the custom cell in the CustomTableViewController.

Let’s revisit the code in “CustomTableViewController.m” that currently used to create table
row:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

cell.textLabel.text = [recipeNames objectAtIndex:indexPath.row];


cell.imageView.image = [UIImage imageNamed:[recipeImages objectAtIndex:indexPath.row]];

return cell;
}

We use the default table view cell (i.e. UITableViewCell) for showing the table items. In
order to use our custom table cell, we have to alter the code in
“CustomTableViewController.m” to the following:

67
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...


if (cell == nil) {
cell = [[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// Display recipe in the table cell


cell.nameLabel.text = [recipeNames objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[recipeImages
objectAtIndex:indexPath.row]];

return cell;
}

However, as soon as you update the code, Xcode detects there are some errors as indicated
in the source code editor.

Figure 4-21. Errors detected in the code

What’s the problem? The code we’ve just changed tells CustomTableViewController to use
CustomTableCell class as the table cell instead of UITableViewCell class. It turns out
CustomTableViewController has no idea about it. That’s why Xcode displays the errors.

As explained before, a header file declares the interface of a class. For


CustomTableViewController to get to know the CustomTableCell class, we have to import
the “CustomTableCell.h” in the “CustomTableViewController.m”. So adding the following
import statement at the very beginning of the code:

#import "CustomTableCell.h"

68
By importing CustomTableCell.h, the CustomTableViewController knows what it is and can
make use of it. Once you add the import statement, you should be able to get rid of the
errors.

Now compile and run the app. Your app should look like this:

Figure 4-22. CustomTable App using custom table cell

Your Exercise
You may notice the app just displays “PrepTime” for all table rows. I intentionally leave this
for you. Try to make some changes of your code and update the preparation time. Your final
app will look very similar to below:

69
Figure 4-23. CustomTable App showing preparation time

Summary
This chapter is a bit long. I just want to make sure you understand the concept of prototype
cell and cell customization thoroughly. If you’re still with me, you should feel pretty darn
good about yourself.

Table view is a backbone for most of the great iPhone apps. Except for game development,
you’ll probably implement table view in some ways when building your own apps. Table
view may be a bit complex for some of you. So take some time to work on the exercise and
play around with the code. “Get Your Hands Dirty” is the best way to learn coding.

In the next chapter, we’re going to see how to handle cell selection. It’s going to be fun but
it’s built on what we’ve learnt about table view. Therefore, go through the chapter again if
you do not understand the table view implementation thoroughly.
For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/lc9osjkf0xsi8yt/CustomTable.zip.

70
5

TABLE ROW SELECTION


I believe you should manage to create a simple table view app and understand how to
custom a table cell. So far we focus on displaying data in table view. But how do we know
when someone taps or selects a table row (or cell)? This is what we’ll go through in this
chapter and see how you can handle cell selection.

We’ll build on the CustomTable app that we’ve worked on in the previous chapter. And we’ll
add a couple of enhancements:

71
• Display an alert message when user taps a table cell
• Display a check mark when user selects a table cell

Easy, right? Let’s get started.

Understanding UITableViewDelegate
When we first built the Simple Table View app in Chapter 3, we declared two delegates
(UITableViewDelegate, UITableViewDataSource) in the SimpleTableController.h:
#import <UIKit/UIKit.h>

@interface SimpleTableViewController : UIViewController <UITableViewDelegate,


UITableViewDataSource>

@end

As explained in the earlier chapters, both delegates are known as protocol in Objective-C.
You have to conform with the requirements defined in these protocols in order to build a
UITableView.

It’s very common to come across various delegates in iOS programming. Each delegate is
responsible for a specific role or task to keep the system simple and clean. Whenever an
object needs to perform a certain task, it depends on another object to handle it. This is
usually known as separation of concern in software design.

When you look at the UITableView class, it also applies this design concept. The two
delegates are catered for different purpose. The UITableViewDataSource delegate, that
we’ve learnt, defines methods that are used for displaying table data. On the other hand, the
UITableViewDelegate deals with the appearance of the UITableView, as well as, handles the
table row selection.

Obviously, we’ll make use of the UITableViewDelegate and implement the required
methods for handling the selection.

Handling Table Row Selection


Before we change the code, you may wonder:

How do we know which methods in UITableViewDelegate need to implement?

There are two ways to access the documentation. You can always refer to the Apple’s official
iOS developer reference (https://developer.apple.com/library/ios/) and search for the API

72
document. Or simply look it up inside Xcode. For example, you can bring up the API
document of UITableViewDelegate, just place the cursor over the class name and press
“control-command-?”. You’ll see the following popup:

Figure 5-1. Shortcut to Access API Doc

Click the UITableViewDelegate Protocol Reference to display the API document:

Figure 5-2. UITableViewDelegate Protocol Reference

If you’ve read through the document, you’ll find the below methods that are used for
managing row selection:

– tableView:willSelectRowAtIndexPath:
– tableView:didSelectRowAtIndexPath:

73
Both methods are designed for row selection. The only difference is that
“willSelectRowAtIndexPath” is called when a specified row is about to be selected. Usually
you make use of this method to prevent selection of a particular cell from taking place.
Typically, you use “didSelectRowAtIndexPath” method, which is invoked after user selects a
row, to handle the row selection. And this is where you add code to specify the action when
the row is selected. Later, we’ll add a couple of actions to handle row selection:

• Display an alert message when a row is selected


• Display a check mark to indicate the row is selected

Let’s Code
Okay, that’s enough for the explanation. Let’s move onto the interesting part – code, code,
code!

In Xcode, open the “CustomTableViewController.m” and add the following method before
“@end”.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* selectedRecipe = [recipeNames objectAtIndex:indexPath.row];
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:@"Row Selected" message:selectedRecipe
delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];

// Display Alert Message


[messageAlert show];
}

The code is very easy to understand. When a row is selected, the indexPath contains the
selected row number. From that, we look up the name of recipe. We then creates an
UIAlertView and shows an alert message. Try to run the app and this is what the app looks
like when you tap a row.

74
Figure 5-3. When a row is selected, an alert message is displayed

Easy, right? With the use of delegate, it’s very straightforward to detect row selection. Next,
we’ll add a few lines of code to display a tick for the selected item. Before that, let me take a
look at the default content of a table cell:

Figure 5-4. Default Structure of a UITableViewCell

75
A table cell in basic style can be broken into three parts:

• Image – the left part is reserved for displaying thumbnail just like what we’ve done in the
Simple Table App tutorial
• Content – the main part is used for displaying text label and detailed text
• Accessory view – the right part is reserved for accessory view. There are four types of
built-in accessory view including disclosure indicator, detail disclosure button, checkmark
and detail. Figure 5-5 shows you how these indicators look like.

Figure 5-5. UITableViewCell Accessory View

To display a check mark when a row is selected, you just need to add two lines of code after
the “[messageAlert show]”:
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;

The first line retrieves the selected table cell by using the indexPath. The second line
updates the accessoryType property of the cell with a check mark.

Compile and run the app. After you tap a row, it’ll show you a check mark.

76
Figure 5-6. Checkmark shown when a row is selected

For now, when you select a row, that row will be highlighted in gray color. Try to add the
following code at the end of the didSelectRowAtIndexPath: method to deselect the row.
[tableView deselectRowAtIndexPath:indexPath animated:YES];

Why Duplicated Checkmark?


The app looks great. However, if you look at it closely, there is a bug in the app. Say, you
check the “Egg Benedict” item. Scroll through the table and you’ll find the “Instant Noodle
with Egg” is also checked. What’s wrong with it? Why did the app add an extra checkmark?

77
Figure 5-7. Both items display are checked. What’s wrong?

The problem is related to cell reuse. Say, the UITableView has 30 cells to display. For
performance reason, instead of creating 30 table cells, UITableView may just create 10 cells
and reuse them as you scroll through the table. In this case, the UITableView will reuse the
1st cell for displaying row 11. Apparently, row 1 and 11 share the same property.

So back to the bug of the demo app. Obviously, the “Instant Noodle with Egg” item and the
“Egg Benedict” item share the same cell. In our code, we only update the image view and
labels when the table view reuses the same cell. The accessory view is not updated. That’s
why both items share the same accessory view (in this case, the checkmark).

Okay, how can we resolve the bug?

We have to find another way to keep track of the checked items. Let’s create another array
to save the checked recipes. In the CustomTableViewController.m, declare a BOOL array:

BOOL recipeChecked[16];

The BOOL is a data type in Objective C that holds a Boolean value. It accepts only two
values, either YES or NO. We declare the recipeChecked array to hold a collection of
Boolean values. Each value in the array indicates whether the corresponding recipe is
checked or not. For example, we’ll look up to the value of recipeChecked[0] to see if “Egg
Benedict” is checked or not.

78
The values in recipeChecked array are initialized to NO. In other words, the recipe items are
unchecked by default.

There are two changes left in order to fix the bug. First, we have to update the value of the
boolean array when an item is checked. Add the following line of code in
“tableView:didSelectRowAtIndexPath:” method:

recipeChecked[indexPath.row] = YES;

The code is very straightforward. We update the boolean value of the selected item from NO
to YES.

Lastly, we have to add a few lines of code to update the accessory view in the
“tableView:cellForRowAtIndexPath:” method right before “return cell”:

if (recipeChecked[indexPath.row]) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
}

Here we verify the specific value of the recipeChecked array. If the item is checked, the cell
is set to UITableViewCellAccessoryCheckmark. Otherwise, it is set to
UITableViewCellAccessoryNone.

Now, compile and run the app. You should get the bug resolved.

Your Exercise
Meanwhile, you can only add a checkmark to the selected item. When you tap the same
item again, the checkmark is still there. You can’t uncheck it.

Think about how you can alter the code so that the app can toggle the checkmark. It’s not
very difficult to implement it (hint: you just need to change the code in
tableView:didSelectRowAtIndexPath: method).

Summary
At this point, you should have a solid understanding about how to create table view,
customize table cells and handle table row selection. You’re ready to build a simple table
view app on your own. Yes, start your own project. I don’t mean you have to start a big one.
Just don’t follow us to create an app with a list of recipes. If you love travel, create a simple

79
app that displays a list of your favorite destination. If you love music, create your own app
that shows a list of your favorite albums. Get more practice, make mistake and learn along
the way.

In the next chapter, we’ll continue to explore table view. You know how to display table
data, customize the cell and handle row selection. But how can you delete a table row?
That’s what we’ll cover shortly.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/has2ktuiuj79shh/CustomTableRowSelection.zip.

80
6

TABLE ROW DELETION &


MODEL-VIEW-CONTROLLER
As mentioned in the previous chapter, there is still one thing left about UITableView.

How can we delete a row from UITableView?

This is another common question people raised when building the Simple Table App.
Again, it’s easier than you thought. But before jumping into the coding part, I have to
introduce you the Model-View-Controller model, which is one of the most quoted design
patterns for user interface programming.

81
You can’t escape from learning Model-View-Controller (MVC for short) if you’re serious
about iOS programming. Not limited to iOS programming, MVC is commonly used and
quoted in other programming languages such as Java. If you come from other
programming backgrounds, MVC shouldn’t be new to you.

Understanding Model-View-Controller
“At the heart of MVC, and the idea that was the most influential to later frameworks,
is what I call Separated Presentation. The idea behind Separated Presentation is to
make a clear division between domain objects that model our perception of the real
world, and presentation objects that are the GUI elements we see on the screen.
Domain objects should be completely self contained and work without reference to
the presentation, they should also be able to support multiple presentations, possibly
simultaneously. This approach was also an important part of the Unix culture, and
continues today allowing many applications to be manipulated through both a
graphical and command-line interface.”
By Martin Fowler
http://www.martinfowler.com/eaaDev/uiArchs.html#ModelViewController

No matter what computer language you learn, one important concept that makes you
become a better programmer is Separation of Concerns (SoC). The concept is pretty simple.
Concerns are the different aspects of software functionality. The concept encourages
developers to break a big feature or program into several areas of concern that each area
has its own responsibility. The delegate pattern that is commonly found in iOS
programming we explained in the earlier chapters is one of the example of SoC.

Here, model-view-controller (MVC) is another example of SoC. The core idea behind MVC
is to clearly separate user interface into three areas (or groups of objects) that each area is
responsible for a particular functionality. As the name suggests, MVC breaks an user
interface into three parts:

Model – model is responsible for holding the data or any operations on the data. The
model can be as simple as an array object that stores all the table data. Add, edit and delete
are examples of the operations. In reality, the operations are usually known as business
rules.

View – view manages the visual display of information. For example, UITableView shows
information in a table view format.

82
Controller – controller is the bridge between model and view. It translates the user
interaction from the view (e.g. tap) into appropriate action to be performed in the model.
For example, user taps a delete button in the view and controller, in turn, triggers a delete
operation in the model. After that, it also requests the view to refresh itself to reflect the
update of the data model.

To help you better understand MVC, let’s use our Simple Table app we developed in
Chapter 3 as an example. The app displays a list of recipes in the table view. If you turn the
concept into visual representation, here is how the table data is displayed:

Figure 6-1. MVC Model Illustrated Using Simple Table as Example

The recipe information that are stored in separate array objects is the Model. Each table
row maps to an element of the recipe arrays. The UITableView object is the View that is the
interface to be seen by the user. It’s responsible for all the visuals (e.g. color of the table
rows, font size and type). The TableViewController acts as the bridge between the
TableView and Recipe data model. When display table data, UITableView actually asks the
Controller for the data to display, that in turn, picks the data from the model.

83
Delete a Row from UITableView
I hope you now have a better understanding of Model-View-Controller. Let’s move onto the
coding part and see how we can delete a row from UITableView. To make thing simple, I’ll
use the Simple Table app as an example.

If you thoroughly understand the MVC model, you probably have some ideas how to
implement row deletion. There are three main things we need to do:

1. Write code to switch to edit mode for row deletion


2. Delete the corresponding table data from the model
3. Reload the table view in order to reflect the change of table data

1. Switching To Edit Mode For Row Deletion


In iOS app, user normally swipes across a row to initiate the delete button. Recalled that we
have adopted the UITableViewDataSource protocol (http://developer.apple.com/library/
ios/#documentation/uikit/reference/UITableViewDataSource_Protocol/Reference/
Reference.html), if you refer to the API doc, there is a method named
tableView:commitEditingStyle:. When user swipes across a row, the table view will check
to see if the method has been implemented. If the method is found, the table view will
automatically show the “Delete” button.

Simply add the following code to your table view app and run your app:
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{

Even the method is empty and doesn’t perform anything, you’ll see the “Delete” button
when you swipe across a row.

84
Figure 6-2. Swipe to Delete a Table Row

2. Delete The Corresponding Table Data From The Model


The next thing is to add code to the method and remove the actual table data. Like other
table view methods, it passes the indexPath as parameter that tells you the row number for
the deletion. So you can make use of this information and remove the corresponding
element from the data array.

In the original code of Simple Table App, we use NSArray to store the table data, which is
the model. In Objective C, NSArray is immutable. That means, you cannot add or remove
items from NSArray once it is initialized. In order to remove the array items dynamically,
we’ll use NSMutableArray instead, the mutable counterpart of NSArray.

Back to our code, change the NSArray declaration to NSMutableArray


@implementation SimpleTableViewController
{
NSMutableArray *recipes;
}

- (void)viewDidLoad
{
[super viewDidLoad];

// Initialize table data


recipes = [NSMutableArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full
Breakfast", @"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut",

85
@"Starbucks Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork",
@"Japanese Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and
Cheese Panini", nil];
}

In the tableView:commitEditingStyle: method, add a line of code to remove the actual data
from the array. Your method should look like this:

- (void)tableView:(UITableView *)tableView commitEditingStyle:


(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
[recipes removeObjectAtIndex:indexPath.row];
}

The NSMutableArray provides a number of operations for you to manipulate the content of
an array. Here we utilize the “removeObjectAtIndex:” method to remove a particular item
from the array. You can try to run the app and delete a row.

Oops! The app doesn’t work as expected.

It’s not a bug. The app does delete the item from the array. The reason why the deleted item
still appears is the view hasn’t been refreshed to reflect the update of the data model.

3. Reload The Table View


Therefore, once the underlying data is removed, we need to invoke “reloadData” method to
request the table View to refresh. Here is the updated code:
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
// Remove the row from data model
[recipes removeObjectAtIndex:indexPath.row];

// Request table view to reload


[tableView reloadData];
}

Test the App


Try to run your app again and swipe to delete a row. You should be able to delete it.

86
Figure 6-3. Delete a row and the item is removed

Summary
This chapter introduced you to MVC and how to manage the deletion of table row. The
demo app is very simple but you should have a solid understanding of the MVC concept.

It is a short chapter. The MVC concept is however an important stuff. If you’re a


programming newbie, it may take some time to digest the stuff. Go back to the very
beginning and read through the part that you don’t understand again.

In the next chapter, we’ll take a look at something new. So far the table data are stored in
arrays. You’ll learn how to externalize the data and put them in a property file.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/lh3kmes9rptl3nu/SimpleTableDeletion.zip.

87
CHAPTER 7

ENHANCE TABLE APP


WITH PROPERTY LIST
“Talent is cheaper than table salt. What
separates the talented individual from the
successful one is a lot of hard work.”
~ Stephen King

We already built a very simple table app displaying list of recipes. If you look into the app,
all our recipes are stored in the source code as arrays. I try to keep the thing simple and
focus on showing how to create and customize a UITableView. However, it’s not a good
practice to “hard code” everything in the code.

In real app, we used to externalize these static items (i.e. the recipe information) and put
them in a file or database or somewhere else. In iOS programming, there is a file type
known as Property List. This kind of file is commonly found in Mac OS and iOS, and is used
for storing simple structured data (e.g. application setting). In this chapter, we’ll make
some changes in our simple table app and tweak it to use Property List.

In brief, here are a couple of things we’ll cover:

• Convert table data from static array to property list


• How to read property list

88
Why Externalize the Table Data?
It’s always a good practice to separate static data from the code. But why? What’s the
advantage to put the table data into an external source? Let me ask you. How do you add 50
more recipes in our custom table app we’ve built in chapter 4? Probably, you’ll go back to
your code and put all the new recipes in the initialization:
recipeNames = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full
Breakfast", @"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut",
@"Starbucks Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork",
@"Japanese Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and
Cheese Panini", nil];

recipeImages = [NSArray arrayWithObjects:@"egg_benedict.jpg", @"mushroom_risotto.jpg",


@"full_breakfast.jpg", @"hamburger.jpg", @"ham_and_egg_sandwich.jpg", @"creme_brelee.jpg",
@"white_chocolate_donut.jpg", @"starbucks_coffee.jpg", @"vegetable_curry.jpg",
@"instant_noodle_with_egg.jpg", @"noodle_with_bbq_pork.jpg", @"japanese_noodle_with_pork.jpg",
@"green_tea.jpg", @"thai_shrimp_cake.jpg", @"angry_birds_cake.jpg", @"ham_and_cheese_panini.jpg",
nil];

recipePrepTimes = [NSArray arrayWithObjects:@"30 min", @"30 min", @"20 min", @"30 min", @"10
min", @"1 hour", @"45 min", @"5 min", @"30 min", @"8 min", @"20 min", @"20 min", @"5 min", @"1.5
hour", @"4 hours", @"10 min", nil];

There is nothing wrong doing this. But look at the code! It’s not easy to edit and you have to
strictly follow the Objective C syntax. Changing the code may accidentally introduce other
errors. That’s not we want. Apparently, it would be better to separate the data and the
programming logic (i.e. the code). Does it look better when the table data is stored like this?

89
Figure 7-1. Sample Property List

In reality, you may not be the one who provides the table data (i.e. the recipe information).
The information may be given by others without iOS programming experience. When we
put the data in an external file, it’s easier to read/edit and more understandable.

As you progress, you’ll learn how to put the data in server side (or what-so-called the
Cloud). All data in your app are pulled from the server side on demand. It offers one big
benefit. Meanwhile, any change of the data will require you to build the app and submit it
for Apple’s approval. By separating the data and put them in the Cloud, you can change the
data anytime without updating your app.

In later chapters, we’ll talk about the Cloud. For now, let’s study the basics and see how you
can put all the recipes in a Property List.

What is Property List


First thing first, what’s a property list? Property list offers a convenient way to store simple
structural data. It usually appears in XML format. If you’ve edited some configuration files
in Mac or iPhone before, you may come across files with .plist extension. They are examples
of the Property List.

90
You can’t use property list to save all types of data. The items of data in a property list are of
a limited number of types including “array”, “dictionary”, “string”, etc. For details of the
supported types, you can refer to the Property List documentation (https://
developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/
AboutPropertyLists/AboutPropertyLists.html).

Property list is commonly used in iOS app for saving application settings. It doesn’t mean
you can’t use it for other purposes. However, it is designed for storing small amount of data.

Is it the Best Way to Store Table Data?


No, definitely not. We use property list to demonstrate how to store table data in an
external file. It’s just an example. As you gain more experience, you’ll learn other ways to
store the data. And as a developer, you have to decide which approach is the best for your
app.

Convert Table Data to Property List


That’s enough for the background. Let’s get our hands dirty and convert the data into a
property list. We’ll use the CustomTable project as example. So first, open the Custom
Table project in Xcode. Right click on the “CustomTable” folder and select “New File…”.
Select “Resource” under “iOS” template, choose “Property List” and click “Next” to
continue.

91
Figure 7-2. Create a New Property List File

When prompted, use “recipes” as the file name. Once you confirm, Xcode will create the
property list file for you. By default, the property list is empty.

92
Figure 7-3. Empty Property List

To edit the property list, you can right-click on the editing area and select “Add Row” to add
a new value.

93
Figure 7-4. Add a New Row in Property List Editor

As we’re going to put the three data arrays in the property list, we’ll add three rows with
“array” type. Name them with the keys: Name, Image and PrepTime. The key serves as an
identifier and later you’ll use it in your code to pick the corresponding array.

Figure 7-5. Define Three Arrays in Property List

To add data in the array, just expand it and click the “+” icon to add a new item. Follow the
steps in the below illustration if you don’t know how to do it.

94
Figure 7-6. Step by Step Procedures to Add an Item in Array

Repeat the procedures until you add all the values for the array. Your property list should
look like this:

Figure 7-7. Recipe Property List

95
For your convenience, you may also download the recipes.plist (https://www.dropbox.com/
s/fk7pqxji0abdkcs/recipes.plist) and add it to your project.

As mentioned earlier, the property list is usually saved in the format of XML. To view the
source of the property list, right click and select “Open as Source Code”.

Figure 7-8. View the Source Code of Property List

The source code of “recipes.plist” file looks like this:


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/
PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Name</key>
<array>
<string>Egg Benedict</string>
<string>Mushroom Risotto</string>
<string>Full Breakfast</string>
<string>Hamburger</string>
<string>Ham and Egg Sandwich</string>
<string>Creme Brelee</string>
<string>White Chocolate Donut</string>
<string>Starbucks Coffee</string>
<string>Vegetable Curry</string>
<string>Instant Noodle with Egg</string>
<string>Noodle with BBQ Pork</string>
<string>Japanese Noodle with Pork</string>
<string>Green Tea</string>
<string>Thai Shrimp Cake</string>
<string>Angry Birds Cake</string>
<string>Ham and Cheese Panini</string>
</array>
<key>Image</key>
<array>
<string>egg_benedict.jpg</string>
<string>mushroom_risotto.jpg</string>
<string>full_breakfast.jpg</string>
<string>hamburger.jpg</string>
<string>ham_and_egg_sandwich.jpg</string>
<string>creme_brelee.jpg</string>
<string>white_chocolate_donut.jpg</string>
<string>starbucks_coffee.jpg</string>
<string>vegetable_curry.jpg</string>
<string>instant_noodle_with_egg.jpg</string>
<string>noodle_with_bbq_pork.jpg</string>
<string>japanese_noodle_with_pork.jpg</string>
<string>green_tea.jpg</string>
<string>thai_shrimp_cake.jpg</string>
<string>angry_birds_cake.jpg</string>

96
<string>ham_and_cheese_panini.jpg</string>
</array>
<key>PrepTime</key>
<array>
<string>30 min</string>
<string>30 min</string>
<string>20 min</string>
<string>30 min</string>
<string>10 min</string>
<string>1 hour</string>
<string>45 min</string>
<string>5 min</string>
<string>30 min</string>
<string>8 min</string>
<string>20 min</string>
<string>20 min</string>
<string>5 min</string>
<string>1.5 hour</string>
<string>4 hours</string>
<string>10 min</string>
</array>
</dict>
</plist>

Loading Property List in Objective C


We’ve created the property file for the recipes. Next,
we’ll change our code and load the recipe from the
property list. It’s fairly easy to read the content of
property list. The iOS SDK already comes with some
built-in functions to handle the read/write of the file.

Replace the following code in


CustomTableViewController.m:
recipeNames = [NSArray arrayWithObjects:@"Egg Benedict",
Figure 7-9. Demo app of Property List
@"Mushroom Risotto", @"Full Breakfast", @"Hamburger", @"Ham
and Egg Sandwich", @"Creme Brelee", @"White Chocolate
Donut", @"Starbucks Coffee", @"Vegetable Curry", @"Instant
Noodle with Egg", @"Noodle with BBQ Pork", @"Japanese Noodle with Pork", @"Green Tea", @"Thai
Shrimp Cake", @"Angry Birds Cake", @"Ham and Cheese Panini", nil];

recipeImages = [NSArray arrayWithObjects:@"egg_benedict.jpg", @"mushroom_risotto.jpg",


@"full_breakfast.jpg", @"hamburger.jpg", @"ham_and_egg_sandwich.jpg", @"creme_brelee.jpg",
@"white_chocolate_donut.jpg", @"starbucks_coffee.jpg", @"vegetable_curry.jpg",
@"instant_noodle_with_egg.jpg", @"noodle_with_bbq_pork.jpg", @"japanese_noodle_with_pork.jpg",
@"green_tea.jpg", @"thai_shrimp_cake.jpg", @"angry_birds_cake.jpg", @"ham_and_cheese_panini.jpg",
nil];

recipePrepTimes = [NSArray arrayWithObjects:@"30 min", @"30 min", @"20 min", @"30 min", @"10
min", @"1 hour", @"45 min", @"5 min", @"30 min", @"8 min", @"20 min", @"20 min", @"5 min", @"1.5
hour", @"4 hours", @"10 min", nil];

with:

// Find out the path of recipes.plist


NSString *path = [[NSBundle mainBundle] pathForResource:@"recipes" ofType:@"plist"];

97
// Load the file content and read the data into arrays
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
recipeNames = [dict objectForKey:@"Name"];
recipeImages = [dict objectForKey:@"Image"];
recipePrepTimes = [dict objectForKey:@"PrepTime"];

Behind the Code Change


Line #2 – Before reading the “recipes.plist” file, you have to first retrieve the full path of
the resource. You can use the “pathForResource:” method to get the file path of the
recipes.plist.
Line #5 – You have defined three keys (Name, Image, PrepTime) in the property list. In
the example, each key is associated with a specific array, which is the value. In iOS
programming, we use the term dictionary to refer to this key-value pair association.
NSDictionary class provides the necessary methods for managing the dictionary. Here, we
use the “initWithContentsOfFile” method of NSDictionary class to read the key-value pairs
in a property list file.

Line #6-8 – These lines of code retrieves the corresponding array with the key we defined
earlier.

Once you complete the change, try to run the app again. The app is the same as before.
Internally, however, the recipes are loaded from the property list.

Summary
Again, I hope you learnt a ton through this chapter. The app is still the same but you should
have a better idea about property list and how you can make use of it to store small amount
of data. Let me ask you the same question once more. How do you add 50 new recipes in
the demo app?

By separating the recipe data from the code, you should now be able to edit the recipes
without changing the code.

This comes to an end of the table view discussion. Next up, let’s talk something simple
before moving onto another exciting topic - Navigation Controller.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/r352vsa6l4de9rm/CustomTablePropertyList.zip.

98
8

LAUNCH IMAGE
Before moving onto another detailed chapter about Navigation Controller, I’d like to first
answer a common question when building an app.

How can I add a splash screen for my app?

While you may think you need to write code for the splash screen, Apple has made it
extremely easy to get it done in Xcode. No coding is required. What you just need to do is to
make a couple of configuration in Xcode.

99
It’s Launch Image, Not Splash Screen
First thing first, if you are new to programming and haven’t heard of the term “Splash
Screen”, let me first give a brief explanation about it. Splash screen is commonly found in
desktop applications to display branding information. This is the first screen you see when
launching an application. Usually, splash screen is an image covering the entire screen and
disappears after the main screen is loaded.

In iOS, however, it doesn’t use the term “Splash Screen”. This kind of startup screen is
commonly known as Launch Image. Below figure shows you a few samples of launch image:

Figure 8-1. Sample Launch Images

Unlike splash screen, you’re not encouraged to use launch image for branding purpose,
though some apps make use of it to display their logos. According to Apple, the primary
purpose of launch image is to let user know your app is loading and give users an
impression that your app is immediately ready for use. Launch image is especially
important for apps that take longer time to launch. Perceptually, it gives a better user
experience.

As an example, figure 8-2 shows the launch image of Yahoo’s Weather app.

100
Figure 8-2. Launch Images of Yahoo’s Weather App

Yes, it’s really simple and boring. It’ll take some time for the Weather app to fetch the
weather information. The launch image simply gives users a perception that the app is
quick to launch and responsive.

Adding Launch Image in Your App


As mentioned earlier, it’s not required to write any code to display the launch image. For
every Xcode project, it comes with a built-in setting called launch image. You can simply
add your launch images into Xcode via drag & drop. If you specify any image as launch
image, it will be automatically loaded when user opens the app and disappeared until the
app is fully ready to use.

101
Preparing Your Launch Image
Since the release of iPhone 5, iOS developers have to cater for different screen sizes in their
apps. As you know, iPhone 5 or 5S comes with a 4-inch screen, while its predecessors are
with 3.5-inch display. In order to support various screen resolution of older iPhone models
and latest models, you have to prepare two versions of launch image of these sizes:

• 640 x 1136 pixels (for iPhone 5 / 5s)


• 640 x 960 pixels (for iPhone 4 / 4S)
• 320 x 480 pixels (optional - only if your app needs to support even older generation of
devices with non-retina display)
For the sake of simplicity, I’ll show you how to add launch image in iPhone app. In case
you’re building an iPad app, you can refer to Apple’s iOS Human Interface Guideline
(https://developer.apple.com/library/ios/documentation/userexperience/conceptual/
mobilehig/LaunchImages.html) about the launch image size.

The launch image should be in PNG format. By default, you should name the image file for
lower screen resolution as Default.png. For the image intended for Retina Display (i.e. 640
x 960 screen resolution), name the file as [email protected]. The “@2x” is a standard scale
modifier used in iOS. All image files designated for displaying in Retina Display should
name with the optional string “@2x”. To specify default launch images for iPhone 5 or 5s,
use the modifier string “-568h” and name the file as [email protected].

Instead of using “Default” as the file name of launch image, you may change it to your
preferred one. The built-in asset manager in Xcode 5 allows you to add your preferred
launch images via drag & drop. Let’s see how to add the launch images by using a demo.

But first, download the sample splash screens from https://dl.dropboxusercontent.com/u/


2857188/launch_images.zip. Of course, you can design your own launch images.

102
Figure 8-3. Launch Images for demo

Add Your Launch Image in Xcode


After preparing the launch image, go back to Xcode and open any the demo Xcode project.
Here we’ll continue to use our CustomTable project.

Under Project Navigator, click the “CustomTable” project and select the “CustomTable”
target. You should see the “Launch Images” section under General.

103
Figure 8-4. Launch Images option in Xcode project

Click the small arrow next to LaunchImage and you’ll see a screen as shown in Figure 8-5.

Figure 8-5. Managing launch image

From here, you can simply drag & drop your launch image into the 2x and R4 wells. Drag
the launch image for 3.5-inch display (i.e. 640x960 pixels) into the 2x well. Put the one for
4-inch display (i.e. 640x1136 pixels) into the R4 well.

If you’ve done everything correctly, you’ll see both files in your project and Xcode
automatically recognizes the files as launch images.

104
Figure 8-6. Launch Image added in Xcode

Run the App to Test it Out


Simply run your app again. This time you’ll see a launch image shown up instantly as it
runs. For now, there is not much thing to load during application startup. The launch image
only shows up for a second and disappears.

Figure 8-7. Launch image in Demo App

105
Where are the Launch Images?
Once you added the launch images into the Xcode project, the launch images are saved in a
separate folder. Open Finder, locate your Xcode project and you’ll find the launch images
under “images.xcassets/LaunchImage.launchimage”.

Figure 8-8. Launch Images in Finder

Further Information
In this really short chapter, we show you how to add a simple launch image in your iPhone
app. The launch image we use is in portrait orientation. What if you’re design an app in
landscape orientation? How should you name the launch image? I high recommend you to
check out Apple’s programming guideline for App Launch Image (http://
developer.apple.com/library/ios/#documentation/iphone/conceptual/
iphoneosprogrammingguide/App-RelatedResources/App-RelatedResources.html#//
apple_ref/doc/uid/TP40007072-CH6-SW12) for further information. iOS supports various
versions of launch image with different naming convention. So check it out and learn more
about it.

In the next chapter, we’ll start to talk about navigation controller.

106
CHAPTER 9

NAVIGATION
CONTROLLER
“If you hear a voice within you say 'you
cannot paint,' then by all means paint,
and that voice will be silenced”
~ Vincent Van Gogh

In this chapter, we’ll show you how to use Storyboards to build a Navigation interface and
integrate it with table view. We try to keep thing simple and focus on explaining the
concept. In chapter 5, we’ve created a demo app that handles table row selection. We’ll use
that project as foundation and build upon it. Don’t expect any fancy interface or pretty
graphic. We’ll keep tweaking the artwork in later chapters.

What’s Navigation Controller?


First thing first, what’s navigation controller? Like table view, navigation controller is
another UI element you commonly find in iOS app. It provides a drill-down interface for
hierarchical content. Take a look at the built-in Photos app, YouTube, and Contacts. They’re
all built using navigation controller to display hierarchical content. Usually table view and
navigation controller work hand in hand for most of the apps. That said, this doesn’t mean
you have to use both together.

107
Figure 9-1. An example of Navigation Controller - Photos App

Scene and Segue in Storyboard


We’ve built most of the apps by using Storyboard. But so far we just layout one view
controller in the storyboard for building table view app. Storyboard allows you to do more
than that. You can easily embed a view controller in navigation controller and define the
transition (known as segues) between various screens simply using point and click.

Figure 9-2. Storyboard - Scene and Segue

108
When working with Storyboards, scene and segues are two terms you always come across.
Within a storyboard, a scene refers to a single view controller and its view. Each scene has a
dock, which is used primarily to make action and outlet connections between the view
controller and its views.

Segue sits between two scenes and manages the transition between two scenes. Push and
Modal are two common types of transition.

Creating Navigation Controller


As mentioned, we’ll build on top of the CustomTable app and turn it into a realistic Recipe
app. You can use the Xcode project named RecipeAppTemplate (https://
www.dropbox.com/s/6q4qph8z7usuy2m/RecipeAppTemplate.zip) to start with. The
project is exactly the same as that of CustomTable. The app template still displays a list of
recipes in a table view. What we’re going to do is to embed it with a navigation controller.
When users select any of the recipes, the app navigates to the next screen showing the
details.

Okay, fire up Xcode and open the RecipeAppTemplate project. Select the Main.storyboard
to switch to the storyboard editor. Presently, you only see a table view controller. As we’ll
use navigation controller to control the screen navigation, we first change the view
controller to navigation controller. Select the table view controller and click “Editor” in the
menu. Choose “Embed in” and then “Navigation Controller”.

Figure 9-3. Embed table view controller in navigation controller

109
Xcode automatically embeds the RecipeBook View Controller with Navigation Controller.
Your storyboard should look like this:

Figure 9-4. Embedded View Controller with Navigation Controller

Before moving on, let’s run the app and see how it looks. Hit the “Run” button and you
should get the same app but added with a navigation bar. This shows you’ve successfully
embed your app in a Navigation Controller.

110
Figure 9-5. Recipe app using navigation controller

Adding the Detail View Controller


Easy, right? With just a few clicks, you added a navigation interface to your app. What’s
missing is another view controller that displays the recipe details. Let’s just refer it as the
detail view controller. When user selects any of the recipes, the app should transit to the
detail view controller and show the recipe details.

Okay, let’s add a new View Controller as the detail view controller in the storyboard.

111
Figure 9-6. Add a new view controller

The primary purpose of this chapter is to show you how to implement navigation controller.
We’ll keep the detail view as simple as possible. Let’s just a label to display the recipe name.
Drag the label from the object library and place it at the center of the view. You may change
the font size or type to make the label look better.

Next, we’ll add a segue to connect the prototype cell and the detail view controller. It’s very
straightforward to add a segue object. Press and hold the control key, click on the prototype
cell and drag to the View Controller.

112
Figure 9-7. Connecting scenes with segue

Release both buttons and a pop-up shows three types of segues (push, modal and custom).

Figure 9-8. Storyboard Segues (Push, Modal and Custom)

As explained earlier, segue defines the type of transition between scenes. For standard
navigation, we use “Push”. Once chosen, Xcode automatically connects the prototype cell
and the detail view controller with a Push segue (see Figure 9-9).

113
Figure 9-9. Storyboard Segue

Finally, let’s give a title for the navigation bar. Select the navigation bar of the table view
controller. Under Attributes Inspector, set the title as “Recipes”.

114
Figure 9-10. Setting the title of navigation bar

Now, you’re ready to run the app. Once launched, select any of the recipes and the app
should navigate to the detail view
controller. Right now, the detail view
controller just shows a static label.
It’s boring but you know how to make
the navigation interface work.

What’s Coming Next


Without writing a single line of code,
you’ve added a navigation controller
in your app. It’s unbelievably easy,
right? At the same time, however, I
believe you got a couple of questions
in your mind:

How can I pass the recipe Figure 9-11. Recipe app with detail view controller

115
information from the “Recipe Table View Controller” to the “Detail View Controller”?

How can I display the recipe photo in the detail view controller for the selected recipe?

Don’t worry. We’ll look into them one by one. Next up, we’ll see how we can pass the recipe
name between view controller with segue in the next chapter.

Just like table view, navigation controller is another fundamental UI element and
commonly used when building iOS apps. So take some time to go through the chapter and
make sure you have a thorough understanding of the material.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/m01x6xfuzugtlub/RecipeAppNavigation.zip.

116
C H A P T E R 10

PASSING DATA BETWEEN


VIEW CONTROLLERS
“Sometimes when you sacrifice something
precious, you're not really losing it.
You're just passing it on to someone else.”
~ Mitch Albom

Previously, we built a simple Recipe app with navigation interface. As I said before, we left
one thing that was not discussed: data passing between scenes (i.e. view controllers) with
segue.

First, let’s take a quick look at what we’ve accomplished. So far, we learnt to use
Storyboards to build a couple of things:

• Embedded a view controller in a navigation controller


• Switched from one view controller to another using segue
Yet, we haven’t implemented the detail view which just shows a static label. In this chapter,
we’ll continue to work on this project and polish the app.

Creating a View Controller Class


In the previous chapter, we created a view controller that serves as the detail view controller
of a recipe in the storyboard editor. The view controller is assigned with the
UIViewController class by default.

117
Figure 10-1. Default View Controller - UIViewController

Let’s revisit our problem. The label in the view should be changed with respect to the
selected recipe. Obviously, there must be a variable in the UIViewController for storing the
name of recipe.

The fact is the UIViewController class only provides the fundamental view management
model. It corresponds to a blank view. There is no variable assigned for storing the recipe
name. Thus, instead of using UIViewController directly, we extend from it and create our
own class (known as the subclass of UIViewController).

In the Project Navigator, right-click the “RecipeApp” folder and select “New File…”. Choose
“Objective-C Class” under Cocoa Touch as the class template.

Name the class as “RecipeDetailViewController” and it’s a subclass of “UIViewController”.


Make sure you uncheck the option of “With XIB for user interface”. As we’re using
Storyboards for designing the user interface, we do not need to create a separate interface
builder file. Click “Next” and save the file in your project folder.

118
Figure 10-2. Create a RecipeDetailViewController class (Subclass of UIViewController)

Next, we have to assign the RecipeDetailViewController class to the view controller. Go


back to the Storyboards editor and select the detail view controller. In the identity
inspector, change the custom class to “RecipeDetailViewController”.

119
Figure 10-3. Assign a custom class for the detail view controller

Adding Variables to the Custom Class


We’ve just created our custom view controller class by extending from the
UIViewController class. However, it doesn’t differ from the parent class until we add our
own variables and methods. There are a couple of things we have to change:

• Assign a variable (recipeName) for data passing – when user selects a recipe in the Recipe
view, there must be a way to pass the name of recipe to the detail view.
• Assign an outlet variable (recipeLabel) for the text label – presently the label is static. It
should be updated as the name of recipe changes.
Okay, let’s add these two variables (recipeLabel and recipeName). Select the
“RecipeDetailViewController.h” and adds two properties for the interface:
@interface RecipeDetailViewController : UIViewController

@property (nonatomic, strong) IBOutlet UILabel *recipeLabel;


@property (nonatomic, strong) NSString *recipeName;

@end

120
Establish a Connection Between Variable and UI Element
Next, we have to link up the “recipeLabel” variable with the visual Label. In the Storyboards
editor, press & hold the control key and then click the “Recipe Detail View Controller” icon,
drag it to the Label object. Release both buttons and a pop-up shows variables for your
selection. Choose the “recipeLabel” variable.

Figure 10-4. Establish the Connection between UI element and Variable

That’s it. Now you’ve linked up the variable with the Label UI element. Any change made to
the variable will be reflected visually. But there is still one thing left. We want to have the
label to display the recipe name. So in the viewDidLoad: method, we add the following code
that sets the label text the same as the recipe name.

121
- (void)viewDidLoad
{
[super viewDidLoad];

// Set the Label text with the selected recipe


self.recipeLabel.text = self.recipeName;
}

Try to compile and run your app. Oops! The detail view is completely blank after selecting
any recipe. That’s the expected behavior. We haven’t written any code to pass along the
recipe name. Yet, the “recipeName” variable is blank that contributes to the empty text
label.

Figure 10-5. Recipe App with a blank detail view controller

Passing Data Using Segue


This comes to the core part of the chapter about how to pass data between view controller
using Segue. Segue manages the transition between view controllers. On top of this, segue
objects are used to prepare for the transition from one view controller to another. When a
segue is triggered, before the visual transition occurs, the storyboard runtime invokes the

122
prepareForSegue:sender: method of the current view controller (here, it’s the
RecipeTableViewController). By implementing this method, you can pass any needed data
to the view controller that is about to be displayed.

The best practice is to give each segue in your storyboard an unique identifier. This
identifier is a string that your application uses to distinguish one segue from another. As
your app becomes more complex, it’s likely you’ll have more than one segue between view
controllers.

To assign the identifier, select the segue and set it in the attributes inspector. Let’s name the
segue as “showRecipeDetail”.

Figure 10-6. Storyboard Segue Identifier

Next, we’ll implement the prepareForSegue:sender: method in the


RecipeTableViewController, which is the source view controller of the segue. Select the
“RecipeTableViewController.m” and add the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.recipeName = [recipeNames objectAtIndex:indexPath.row];
}

123
}

The prepareForSegue: method will be called when the transition begins. The 1st line is
used to verify the identifier of segue. In this case, the identifier is “showRecipeDetail”. The
2nd line of code invokes the tableView:indexPathForSelectedRow: method to retrieve the
selected table row. Once we have the selected row, we’ll pass it to the
RecipeDetailViewController. A Segue object contains the view controller whose contents
should be displayed at the end of the segue. You can always use
“segue.destinationViewController” to retrieve the destination controller. In this case, the
destination controller is the RecipeDetailViewController. The rest of the code is to pass the
recipe name to the destination controller.

Before running your app, don’t forget to import the “RecipeDetailViewController.h” at the
beginning of the “RecipeTableViewController.m”.

#import "RecipeDetailViewController.h"

Ready to Test
Now, it’s ready to test the app. Hit the Run button to compile and run the app. This time,
your app should work as expected. Try to select any recipe and the detail view should
display the name of the selected item.

Figure 10-7. Recipe App

124
Summary
After reading this chapter, I believe you should have a basic idea of segue data passing. The
demo app is really simple and only passes the recipe name from one view controller to
another. So why stop here? As an exercise, try to tweak the app and display preparation
time in the detail view controller.

The app we’ve created is simple and with an elementary UI. Being that said, it elaborates
the fundamental concept that you can build upon it. If you fully understand how data
passing is done, you’re ready to build a more complex app.

In the next chapter, we’ll take a look at tab controller which is also a common element in
iOS app.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/uj6nzej8n3v3ibd/RecipeAppDataPassing.zip.

125
C H A P T E R 11

TAB BAR CONTROLLER


AND WEB VIEW
“Anyone who stops learning is old,
whether at twenty or eighty. Anyone who
keeps learning stays young. The greatest
thing in life is to keep your mind young.”
~ Henry Ford

Last time, we discussed how to pass data between view controllers with segue. It should be
easy, right? In this chapter, we’ll continue to polish our Recipe app and see how you can
make use of two other common UI elements in iOS apps.

Here are what you’re going to learn:

• How to Create a Tab Bar Controller


• How to Create an About page using UIWebView

Tab Bar Controller and UIWebView


As usual, let’s first have a brief introduction for Tab Bar Controller and UIWebView. You
may not be familiar with the term “Tab Bar” but you should come across it in most of the
iOS apps. Just take a look at figure 11-1 that shows a few sample apps including Twitter,
TED, iTunes and TheVerge. All of them make use of the tab bar controller to display distinct
view in each tab. Normally the tab bar contains at least two tabs and you’re allowed to add
up to five tabs depending on your needs.

126
Figure 11-1. Sample Apps with Tab Bar Interface

UIWebView, on the other hand, is a handy component to load web content. In some cases,
you want to display a single web page locally in your app or let users access external web
pages within your app. You can simply embed the UIWebView object in your app and send
it a HTTP request to load the web content.

Creating a Tab Bar Controller


Okay, let’s go back to our Xcode project. Let me first recap what we’ve done. If you’ve
followed the previous chapter, you should build a simple recipe app with navigation
interface (https://www.dropbox.com/s/uj6nzej8n3v3ibd/RecipeAppDataPassing.zip). It’s
not fully implemented but just works.

127
Figure 11-2. Final deliverable in previous chapter

Now we’re going to polish the app and create a tab bar interface. You may think it’ll be
difficult and requires a lot of coding. Again storyboard simplifies everything. Just like
adding a navigation controller, you can embed a tab bar controller with point and click,
without a single line of code.

First, select the “Navigation Controller” in MainStoryboard.storyboard. Then select “Editor”


in the menu and choose “Embed in”, followed by “Tab Bar Controller”.

128
Figure 11-3. Embed Navigation Controller in Tab Bar Controller

As you can see in Figure 11-4, Xcode automatically puts the navigation controller inside a
tab bar controller. Super easy, right? You may think that’s trivial. Thanks to the
storyboarding feature. Before Apple introduced the storyboarding feature, you would need
to write code and design a separate interface in order to embed a navigation controller.
Anyway, that’s how you can create a tab bar controller.

Figure 11-4. Navigation Controller is embedded in Tab Bar Controller

129
Changing Tab Bar Item Name
By default, the tab bar item is not named and without an icon. Select the tab item in the
navigation controller. You’re free to change the name and specify an icon in the Attributes
Inspector. The tab bar item is default to the “custom” identifier. When it’s set to custom,
that means you have to specify the title and image manually.

Figure 11-5. Customize tab bar item

130
You can also use some built-in tab bar items (e.g. More, Favorites, Top Rated) that already
include an icon. Let’s keep it simple and utilize the built-in item. Change the identifier from
“Custom” to “Featured”.

Run Your App


It’s now ready to test the app. Hit the Run button and see how it looks. The app should be
very similar the one we built in the previous chapter but with a tab bar.

Figure 11-6. Recipe app with tab bar

131
Adding a New Tab
You use tab bar interface to organize your app into distinct modes of operation. Each tab
delivers specific functions. In general, there are at least two tabs in an app when tab bar
controller is used. So we’re going to create a new tab for displaying the app’s “About” page.

Drag a “Navigation Controller” object into the Storyboard. The default navigation controller
is associated with a table view controller. Just leave it as is. We’ll change it later.

Figure 11-7. Adding another navigation controller

Next, we have to establish a relationship between the new navigation controller and the
existing tab bar controller. Press and hold the “control” key, click the tab bar controller and
drag the pointer to the new navigation controller.

132
Figure 11-8. Link up tab bar controller and the new navigation controller

Release both buttons and a pop-up is displayed. Select the “Relationship Segue - View
Controllers” option. This tells Xcode that the
new Navigation Controller is a part of Tab Bar
Controller.

As soon as the relationship is established, the


Tab Bar Controller automatically adds a new tab
and associates it with the new Navigation
Controller.

Figure 11-9. Establish relationship with the


new navigation controller
133
Figure 11-10. Tab bar controller with new tab

Change the tab bar item of the new navigation controller to


“More”. Save the Storyboard and run the app again. Don’t you
see the new “More” tab?

Figure 11-11. Recipe app with


two tab bar items
134
Creating a UIWebView
The “More” tab is now empty. We’re going to turn it into a web view for displaying the
“About” page of the app. First, delete the “Table View Controller” and replace it with a View
Controller. Simply select the “Table View Controller” and click delete button to remove it.
Then drag the “View Controller” object from Object Library.

Figure 11-12. Replace table view controller with view controller

As of now, the blank view controller is not associated with the navigation controller. We
have to link them up. Again, press and hold the control key, select the navigation controller
of “More” item and drag it towards the View Controller.

135
Figure 11-13. Link up view controller and “More” navigation controller

When prompted, select the “Relationship Segues – Root view” option to establish the
relationship between both controllers.

136
Figure 11-14. Associated the relationship between view Controller and “More” navigation controller

The main purpose of this view controller is to render the “About” web page. iOS SDK has a
built-in UIWebView object to display web content. You can simply embed the UIWebView
object into any view controller. Later you send it a HTTP request and it automatically loads
the web content for you. Locate the “Web View” in Object Library and add it into the View
Controller. You may need to resize it to make it fit into the viewable area.

137
Figure 11-15. Add Web View into View Controller

Assigning a New View Controller Class


As explained before, a blank view controller is by default assigned with the
UIViewController class, that only provides the fundamental view management model.
Apparently, it doesn’t come with a variable for controlling the UIWebView. Yet we have to
create a new View Controller class that extends from the UIViewController.

In the Project Navigator, right-click the “RecipeApp” folder and select “New File…”. Choose
“Objective-C Class” under Cocoa Touch as the class template. Name the class as
“AboutViewController” and make it a subclass of “UIViewController”. Click “Next” and
save the file in your project folder.

138
Figure 11-16. Create the AboutViewController class

Next, assign the view controller with the “AboutViewController”. Go back to the
Storyboards editor and select the view controller. In the identity inspector, change the
custom class to “AboutViewController”.

Use UIWebView to Load a Request


To request UIWebView to load web content, we have to write a few lines of code. In brief,
there are three things we need to implement:

• Add the “about.html” and “about.jpg” to your Xcode project


• Assign a variable for UIWebView and associate it with the Web View object in Storyboard
• Use the loadRequest: method in UIWebView to load web content

Adding ‘About.html’
The UIWebView class allows you to load web content both locally and remotely. You can
send it a HTTP request (e.g. http://www.google.com) to load a remote website.
Alternatively, you can tell UIWebView to load web pages embed in your app. For this demo,
we’ll put the “about.html” inside our app.

139
You can create your own about.html. For your convenience, however, download the
about.zip (https://dl.dropboxusercontent.com/u/2857188/book-tabbar-about.zip), extract
it and add all the files into the Xcode project.

Assign a Variable for UIWebView


Select the “AboutViewController.h” and add a new outlet named “webView” for connecting
with the UIWebView:
@interface AboutViewController : UIViewController

@property (nonatomic, strong) IBOutlet UIWebView *webView;

@end

As usual, it’s required to establish connection between the “webView” variable and the
visual web view. In the Storyboards editor, press & hold the control key and then click the
“About View Controller” icon in the dock, drag it to the Web View. Release both buttons and
a pop-up shows variables for your selection. Choose the “webView” variable.

140
Figure 11-17. Link up UIWebView and webView Variable

Load Web Content


Inside the viewDidLoad: method of AboutViewController.m, add the following code to load
the “about.html”:
- (void)viewDidLoad
{
[super viewDidLoad];

// Add code to load web content in UIWebView


NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"about.html"
ofType:nil]];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}

141
The UIWebView class provides a convenient method named loadRequest: to load web
content. What you need to do is to pass it a URL request as parameter. But before that, we
have to determine the system path of the about.html. Line 5 of the above code is written for
that. It returns the system path of the about.html in the form of NSURL object. Then we
creates a NSURLRequest using the URL. Obviously, the last line of code sends the request
to web view.

Time to Test the App


Hit the Run button and test out the app. If you follow everything correctly, your final
deliverable should look like that in Figure 11-18.
When you select the “More” tab, it automatically
loads the “about.html”.

Summary
By now, you should be very comfortable to create a
tab bar controller and add new tab bar items. Not
only tab bar controller, you got to know how how
to use UIWebView to load web content in this
chapter. UIWebView offers basic function of a
mobile browser. Whenever you need to display
web page within your app, embed the web view
and send it a request.

If you stick with me from the very beginning of the


book, congratulation on make it through! You’ve
already learnt some of the most common
components in iOS app including navigation
controller, tab bar controller and table view
controller. Figure 11-18. More tab showing
the about.html
In the upcoming chapter, we’ll continue to polish the
recipe app and make it even better.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/e1xzhrhmclyowor/RecipeAppTabbar.zip.

142
C H A P T E R 12

OBJECT-ORIENTED
PROGRAMMING
“Most good programmers do
programming not because they expect to
get paid or get adulation by the public,
but because it is fun to program.”
~ Linus Torvalds

If you read from the very beginning of the book and have worked through all the projects,
you’ve gone pretty far. By now, you should be able to build an iPhone app with tab bar,
navigation controller and table view using Storyboard. We’ll further enhance the detail view
of the Recipe app as the original detail view was way too plain. How can we display more
information including the recipe image in the detail view? This shouldn’t be difficult if you
understand the materials and I intentionally left out that part for you at the time I wrote the
chapter.

Did you manage to create your own detail view for the Recipe app? Anyway, we’ll revisit it
and show you how to improve the detail screen. But before that, I have to introduce you the
basics of Object Oriented Programming. In the next chapter, we’ll build on top of what
we’ll learn in this chapter and enhance the detail view screen.

Don’t be scared by the term “Object Oriented Programming” or OOP in short. It’s not a new
kind of programming language but a programming concept/technique. While some
programming books start out by introducing the OOP concept, I intentionally left it out
when I first began writing the book. I want to keep thing simple and show you (even

143
without any programming background) how to create an app. I don’t want to scare you
away from building apps, just because of a technical term. However, I think it’s time to
cover the concept. If you’re still around reading the book, I believe you’re determined to
learn iOS programming and you want to take programming skills to the next level.

Okay, let’s get started.

Object Oriented Programming – Some Theory


Objective-C is known as an Object Oriented Programming (OOP) language. OOP is a way of
constructing software application composed of objects. In other words, most of the code
you’ve written in the app in some ways deal with objects of some kind. UIViewController,
UIButton, UINavigationController and UITableView are some of the objects come with the
iOS SDK. Not only you’ve used the built-in objects in your iOS app, you’ve created some
objects of your own such as RecipeDetailViewController and SimpleTableCell.

So why OOP? One important reason is that we want to decompose a complex software into
smaller pieces (or building block) which are easier to develop and manage. Here, the
smaller pieces are the objects. Each object has its own responsibility and objects coordinate
with each other in order to make the software work. That’s the basic concept of OOP.

Take the Hello World app that we’ve built at the very beginning as an example. The
UIViewController object is responsible for the view of the app and as a placeholder for the
Hello World button. The UIButton (i.e. Hello World button) object is responsible to display
a standard iOS button on screen and listen to any touch events. The UIAlertView object, on
the other hand, is responsible to display the alert message to user. Most importantly, all
these objects work together to create the Hello World app.

144
Figure 12-1. Sample Objects in Hello World App

In Object Oriented Programming, an object shares two characteristics: properties and


functionalities. Let’s consider a real world object – Car. A car has its own color, model, top
speed, manufacturer, etc. These are the properties of a car. In terms of functionalities, a car
should provide basic functions such as accelerate, brake, steering, etc.

If we go back to the iOS world, let’s take a look at the properties and functionalities of the
UIButton object in Hello World app:

• Properties – Background, size, color and font are examples of the UIButton properties
• Functionalities – When the button is tapped, it recognizes the tap event and displays an
alert message on screen. So “showMessage” is the function of the button.
In earlier chapters, you always come across the term “method”. In Objective-C, we create
methods to provide the functionalities of an object. Usually a method corresponds to a
particular function of an object.

145
Class, Object and Instance
Other than method and object, you may come across terms like instance and class in OOP.
Let me give a brief explanation.

A class is a blueprint or prototype for creating objects. Basically, a class consists of


properties and methods. Let’s say we want to define a Course class. A Course class contains
properties such as name, course code and max. number of students. This class represents
the blueprint of a Course. We can use it to create different courses like iOS Programming
course with course code IPC101, Cooking course with course code CC101, etc. Here, the “iOS
Programming course” and “Cooking course” are known as the objects of the Course class.
Sometimes, we also refer them as instances of the Course class. For the sake of simplicity,
you can consider instance as another word for object.

Revisit the Customize Table Project


So why do we cover OOP today? There is no better way to explain the concept than showing
you an example. Let’s use the Recipe app as demo.

In the viewDidLoad: method of the project, we created three arrays to store different types
of data: recipe name, image and the preparation time. If you understand the OOP concept,
these data can be characterized as the properties of recipe.
- (void)viewDidLoad
{
[super viewDidLoad];

recipeNames = [NSArray arrayWithObjects:@"Egg Benedict", @"Mushroom Risotto", @"Full


Breakfast", @"Hamburger", @"Ham and Egg Sandwich", @"Creme Brelee", @"White Chocolate Donut",
@"Starbucks Coffee", @"Vegetable Curry", @"Instant Noodle with Egg", @"Noodle with BBQ Pork",
@"Japanese Noodle with Pork", @"Green Tea", @"Thai Shrimp Cake", @"Angry Birds Cake", @"Ham and
Cheese Panini", nil];

recipeImages = [NSArray arrayWithObjects:@"egg_benedict.jpg", @"mushroom_risotto.jpg",


@"full_breakfast.jpg", @"hamburger.jpg", @"ham_and_egg_sandwich.jpg", @"creme_brelee.jpg",
@"white_chocolate_donut.jpg", @"starbucks_coffee.jpg", @"vegetable_curry.jpg",
@"instant_noodle_with_egg.jpg", @"noodle_with_bbq_pork.jpg", @"japanese_noodle_with_pork.jpg",
@"green_tea.jpg", @"thai_shrimp_cake.jpg", @"angry_birds_cake.jpg", @"ham_and_cheese_panini.jpg",
nil];

recipePrepTimes = [NSArray arrayWithObjects:@"30 min", @"30 min", @"20 min", @"30 min", @"10
min", @"1 hour", @"45 min", @"5 min", @"30 min", @"8 min", @"20 min", @"20 min", @"5 min", @"1.5
hour", @"4 hours", @"10 min", nil];

Considered that the name, image and preparation time (i.e. prepTime) are all related to a
recipe, instead of storing these data in separate arrays, it’s better to create a Recipe class to
model a recipe.

146
Figure 12-2. Converting multiply arrays into Recipe Class

We’ll revisit the Recipe app project and convert it to use the new Recipe class.

Creating the Recipe Class


First, we’ll create the Recipe Class. Right click on RecipeApp folder and select “New File…”.
Choose the “Objective-C class” template under Cocoa Touch and click “Next”. Name the
class as “Recipe” and as a subclass of “NSObject”. Click “Next” and save the file in your
Xcode project folder.

147
Figure 12-3. Create the Recipe Class with NSObject as subclass

Once completed, Xcode will create the Recipe.h and Recipe.m files. In the header file, add
the properties of the Recipe class:
@property (nonatomic, strong) NSString *name; // name of recipe
@property (nonatomic, strong) NSString *prepTime; // preparation time
@property (nonatomic, strong) NSString *image; // image filename of recipe

What we just did is to create a Recipe class with three properties. Later we’ll make use of it
to instantiate different recipe objects. So how can we convert the original arrays into an
array of Recipe objects? The three data arrays can be depicted as follows:

148
Figure 12-4. Three Arrays to Store Recipe Name, Thumbnail and Preparation Time

With the new Recipe class, we can turn the arrays into a single array of Recipe objects that
each object stores the recipe data.

Figure 12-5. An Array of Recipe Objects

149
Initialize the Array of Recipe Objects
Okay, let’s get back to the coding part. First, import the “Recipe.h” file we’ve just created
and place the line of code at the beginning of RecipeTableViewController.m:
#import "Recipe.h"

Next, instead of declaring three arrays (recipeNames, recipeImages, recipePrepTimes) in


the RecipeTableViewController.m, we’ll declare a single variable for the array of recipe
objects.
@implementation RecipeTableViewController
{
NSArray *recipes;
}

In the viewDidLoad: method, we initialize the Recipe objects (a total of 16 Recipe objects)
and put them into the “recipes” array.

- (void)viewDidLoad
{
[super viewDidLoad];

// Initialize the recipes array


Recipe *recipe1 = [Recipe new];
recipe1.name = @"Egg Benedict";
recipe1.prepTime = @"30 min";
recipe1.image = @"egg_benedict.jpg";

Recipe *recipe2 = [Recipe new];


recipe2.name = @"Mushroom Risotto";
recipe2.prepTime = @"30 min";
recipe2.image = @"mushroom_risotto.jpg";

Recipe *recipe3 = [Recipe new];


recipe3.name = @"Full Breakfast";
recipe3.prepTime = @"20 min";
recipe3.image = @"full_breakfast.jpg";

Recipe *recipe4 = [Recipe new];


recipe4.name = @"Hamburger";
recipe4.prepTime = @"30 min";
recipe4.image = @"hamburger.jpg";

Recipe *recipe5 = [Recipe new];


recipe5.name = @"Ham and Egg Sandwich";
recipe5.prepTime = @"10 min";
recipe5.image = @"ham_and_egg_sandwich.jpg";

Recipe *recipe6 = [Recipe new];


recipe6.name = @"Creme Brelee";
recipe6.prepTime = @"1 hour";
recipe6.image = @"creme_brelee.jpg";

Recipe *recipe7 = [Recipe new];


recipe7.name = @"White Chocolate Donut";
recipe7.prepTime = @"45 min";
recipe7.image = @"white_chocolate_donut.jpg";

Recipe *recipe8 = [Recipe new];

150
recipe8.name = @"Starbucks Coffee";
recipe8.prepTime = @"5 min";
recipe8.image = @"starbucks_coffee.jpg";

Recipe *recipe9 = [Recipe new];


recipe9.name = @"Vegetable Curry";
recipe9.prepTime = @"30 min";
recipe9.image = @"vegetable_curry.jpg";

Recipe *recipe10 = [Recipe new];


recipe10.name = @"Instant Noodle with Egg";
recipe10.prepTime = @"8 min";
recipe10.image = @"instant_noodle_with_egg.jpg";

Recipe *recipe11 = [Recipe new];


recipe11.name = @"Noodle with BBQ Pork";
recipe11.prepTime = @"20 min";
recipe11.image = @"noodle_with_bbq_pork.jpg";

Recipe *recipe12 = [Recipe new];


recipe12.name = @"Japanese Noodle with Pork";
recipe12.prepTime = @"20 min";
recipe12.image = @"japanese_noodle_with_pork.jpg";

Recipe *recipe13 = [Recipe new];


recipe13.name = @"Green Tea";
recipe13.prepTime = @"5 min";
recipe13.image = @"green_tea.jpg";

Recipe *recipe14 = [Recipe new];


recipe14.name = @"Thai Shrimp Cake";
recipe14.prepTime = @"1.5 hours";
recipe14.image = @"thai_shrimp_cake.jpg";

Recipe *recipe15 = [Recipe new];


recipe15.name = @"Angry Birds Cake";
recipe15.prepTime = @"4 hours";
recipe15.image = @"angry_birds_cake.jpg";

Recipe *recipe16 = [Recipe new];


recipe16.name = @"Ham and Cheese Panini";
recipe16.prepTime = @"10 min";
recipe16.image = @"ham_and_cheese_panini.jpg";

recipes = [NSArray arrayWithObjects:recipe1, recipe2, recipe3, recipe4, recipe5, recipe6,


recipe7, recipe8, recipe9, recipe10, recipe11, recipe12, recipe13, recipe14, recipe15, recipe16,
nil];

In Objective C, we use the “new” method (which is actually a method provided by


NSObjects) to instantiate an object. You have two ways to set the value of the property. In
the above code, we use the dot-syntax to assign the value. For instance,

recipe1.name = @"Egg Benedict";

Alternatively, you can invoke the setName method using square bracket notation ([]). Here
is the equivalent code:

1
[recipe1 setName:@"Egg Benedict"];

151
Both syntaxes perform exactly the same thing. But we’ll use the dot syntax throughout the
book.

Replacing Table Data with Recipes Array


There are a few things we need to change in order to use the recipes array. In the
numberOfRowsInSection: method, we change “recipeNames” to “recipes”:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// DELETE this line return [recipeNames count];
return [recipes count];
}

For the cellForRowAtIndexPath: method, we replace the “recipeNames”, “recipeImages”


and “recipePrepTimes” with the Recipe array:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
CustomTableCell *cell = (CustomTableCell *)[tableView
dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...


if (cell == nil) {
cell = [[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// Display recipe in the table cell


/* DELETE this section
cell.nameLabel.text = [recipeNames objectAtIndex:indexPath.row];
cell.thumbnailImageView.image = [UIImage imageNamed:[recipeImages
objectAtIndex:indexPath.row]];
cell.prepTimeLabel.text = [recipePrepTimes objectAtIndex:indexPath.row];
*/

Recipe *recipe = [recipes objectAtIndex:indexPath.row];


cell.nameLabel.text = recipe.name;
cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
cell.prepTimeLabel.text = recipe.prepTime;

return cell;
}

Finally, change the prepareForSegue: method to the following:


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;

Recipe *recipe = [recipes objectAtIndex:indexPath.row];


destViewController.recipeName = recipe.name;
}
}

152
As you can see from the above code, by changing the three data arrays into the Recipes
array, the code is more readable and understandable.

You can now run your app. Don’t expect any change to the interface. The look & feel of the
app is exactly the same as before. However, internally we beautify our code by creating our
own Recipe object and this is one of the beauties of OOP.

What’s Upcoming Next?


Congratulation if you made it this far. I hope you’re not bored by the chapter. We’ve gone
over the basics of Object Oriented Programming. Yes, this is just the basic concept. There
are other OOP concepts such as polymorphism we do not have time to discuss. If you want
to become a professional iOS developer, grab a book or take a free course* to further dive
into OOP. It’ll take lots of practices and study to gain proficiency. Anyway, if you made it
this far, congratulation! It’s really a good start.

In the next chapter, base on what we’ve learnt so far, we’ll see how to tweak the detail view
screen of the Recipe app. It’s going to be fun!

* - http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-01sc-introduction-to-electrical-engineering-and-computer-science-i-
spring-2011/unit-1-software-engineering/object-oriented-programming/
For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/l5qrqbzfojun99r/RecipeAppOOP.zip.

153
C H A P T E R 13

BEAUTIFY THE RECIPE


APP
“Beautiful things don't ask for attention.”
~ James Thurber

We already showed you how to use segue in storyboard to pass data between different view
controllers. You’ve built a simple Recipe app to display a list of recipes. When user taps on
any of the recipes, the app navigates to a detailed view and brings up the recipe name. This
is very simple app. But if you understand how it works, this is the foundation to help you
advance into a full-fledge iOS developer.

Wouldn’t be great if we further improve the detail view of the app? The original detail view
is primitive with the recipe name only. How can we improve it and display more
information such as the preparation time, ingredient and the dish photo? In this chapter,
we’ll work on it together and make the app even better. Before we move on, however, make
sure you check out the chapter about Object Oriented Programming.

You have to understand the basics of OOP before you can work on the project. If you
haven’t done so, take some time and read through the chapter. You can’t become a full-
fledged iOS developer without learning objects and classes.

154
The Final Deliverable
To give you an idea about the improvement, let’s first take a look at the final deliverable. As
you can see from figure 1, the revamped Recipe app shows detailed information about a
recipe with a better look & feel.

Figure 13-1. Recipe app with improved detail view

Not only with improved user interface, the app gives user more information about a recipe
including a list of ingredients, preparation time and a large photo of the recipe.

Adding Ingredients to the Recipe Data


With the additional property of ingredients, apparently, we have to update our code to due
with it.

First, add a new property named ingredients in the Recipe.h:


@property (nonatomic, strong) NSArray *ingredients;

In the “viewDidLoad” method of “RecipeTableViewController.m”, we initialize the Recipe


objects with additional ingredients data:

- (void)viewDidLoad
{

155
[super viewDidLoad];

// Initialize the recipes array


Recipe *recipe1 = [Recipe new];
recipe1.name = @"Egg Benedict";
recipe1.prepTime = @"30 min";
recipe1.image = @"egg_benedict.jpg";
recipe1.ingredients = [NSArray arrayWithObjects:@"2 fresh English muffins", @"4 eggs", @"4
rashers of back bacon", @"2 egg yolks", @"1 tbsp of lemon juice", @"125 g of butter", @"salt and
pepper", nil];

Recipe *recipe2 = [Recipe new];


recipe2.name = @"Mushroom Risotto";
recipe2.prepTime = @"30 min";
recipe2.image = @"mushroom_risotto.jpg";
recipe2.ingredients = [NSArray arrayWithObjects:@"1 tbsp dried porcini mushrooms", @"2 tbsp
olive oil", @"1 onion, chopped", @"2 garlic cloves", @"350g/12oz arborio rice", @"1.2 litres/2
pints hot vegetable stock", @"salt and pepper", @"25g/1oz butter", nil];

Recipe *recipe3 = [Recipe new];


recipe3.name = @"Full Breakfast";
recipe3.prepTime = @"20 min";
recipe3.image = @"full_breakfast.jpg";
recipe3.ingredients = [NSArray arrayWithObjects:@"2 sausages", @"100 grams of mushrooms", @"2
rashers of bacon", @"2 eggs", @"150 grams of baked beans", @"Vegetable oil", nil];

Recipe *recipe4 = [Recipe new];


recipe4.name = @"Hamburger";
recipe4.prepTime = @"30 min";
recipe4.image = @"hamburger.jpg";
recipe4.ingredients = [NSArray arrayWithObjects:@"400g of ground beef", @"1/4 onion
(minced)", @"1 tbsp butter", @"hamburger bun", @"1 teaspoon dry mustard", @"Salt and pepper",
nil];

Recipe *recipe5 = [Recipe new];


recipe5.name = @"Ham and Egg Sandwich";
recipe5.prepTime = @"10 min";
recipe5.image = @"ham_and_egg_sandwich.jpg";
recipe5.ingredients = [NSArray arrayWithObjects:@"1 unsliced loaf (1 pound) French bread",
@"4 tablespoons butter", @"2 tablespoons mayonnaise", @"8 thin slices deli ham", @"1 large
tomato, sliced", @"1 small onion", @"8 eggs", @"8 slices cheddar cheese", nil];

Recipe *recipe6 = [Recipe new];


recipe6.name = @"Creme Brelee";
recipe6.prepTime = @"1 hour";
recipe6.image = @"creme_brelee.jpg";
recipe6.ingredients = [NSArray arrayWithObjects:@"1 quart heavy cream", @"1 vanilla bean,
split and scraped", @"1 cup vanilla sugar", @"6 large egg yolks", @"2 quarts hot water", nil];

Recipe *recipe7 = [Recipe new];


recipe7.name = @"White Chocolate Donut";
recipe7.prepTime = @"45 min";
recipe7.image = @"white_chocolate_donut.jpg";
recipe7.ingredients = [NSArray arrayWithObjects:@"3 1/4 cups flour", @"2 teaspoons baking
powder", @"1/4 teaspoon salt", @"2 beaten eggs", @"2/3 cup sugar", @"2 ounces melted white
chocolate", @"1/2 cup milk", nil];

Recipe *recipe8 = [Recipe new];


recipe8.name = @"Starbucks Coffee";
recipe8.prepTime = @"5 min";
recipe8.image = @"starbucks_coffee.jpg";
recipe8.ingredients = [NSArray arrayWithObjects:@"2/3 cup whole milk", @"6 tablespoons white
chocolate chips", @"coffee", @"whipped cream", nil];

Recipe *recipe9 = [Recipe new];


recipe9.name = @"Vegetable Curry";
recipe9.prepTime = @"30 min";

156
recipe9.image = @"vegetable_curry.jpg";
recipe9.ingredients = [NSArray arrayWithObjects:@"1 tablespoon olive oil", @"1 onion,
chopped", @"2 cloves garlic", @"2 1/2 tablespoons curry powder", @"2 quarts hot water", nil];

Recipe *recipe10 = [Recipe new];


recipe10.name = @"Instant Noodle with Egg";
recipe10.prepTime = @"8 min";
recipe10.image = @"instant_noodle_with_egg.jpg";
recipe10.ingredients = [NSArray arrayWithObjects:@"1 pack of Instant Noodle", @"2 eggs",
nil];

Recipe *recipe11 = [Recipe new];


recipe11.name = @"Noodle with BBQ Pork";
recipe11.prepTime = @"20 min";
recipe11.image = @"noodle_with_bbq_pork.jpg";
recipe11.ingredients = [NSArray arrayWithObjects:@"1 pack of Instant Noodle", @"BBQ pork",
@"Salt and Pepper", nil];

Recipe *recipe12 = [Recipe new];


recipe12.name = @"Japanese Noodle with Pork";
recipe12.prepTime = @"20 min";
recipe12.image = @"japanese_noodle_with_pork.jpg";
recipe12.ingredients = [NSArray arrayWithObjects:@"1 pack of Japanese Noodle", @"2 green
onions", @"2 garlic cloves, minced", @"4 boneless pork loin chops", nil];

Recipe *recipe13 = [Recipe new];


recipe13.name = @"Green Tea";
recipe13.prepTime = @"5 min";
recipe13.image = @"green_tea.jpg";
recipe13.ingredients = [NSArray arrayWithObjects:@"Green tea", nil];

Recipe *recipe14 = [Recipe new];


recipe14.name = @"Thai Shrimp Cake";
recipe14.prepTime = @"1.5 hours";
recipe14.image = @"thai_shrimp_cake.jpg";
recipe14.ingredients = [NSArray arrayWithObjects:@"8 oz (250g) peeled and deveined raw
shrimp", @"2 tablespoons red curry paste", @"1 large egg", @"2 teaspoon fish sauce", @"1
tablespoon sugar", @"2 tablespoons coconut milk", @"2 tablespoons roughly chopped Thai basil
leaves", nil];

Recipe *recipe15 = [Recipe new];


recipe15.name = @"Angry Birds Cake";
recipe15.prepTime = @"4 hours";
recipe15.image = @"angry_birds_cake.jpg";
recipe15.ingredients = [NSArray arrayWithObjects:@"12 tablespoons (1 1/2 sticks) unsalted
butter", @"2 1/2 cups all-purpose flour", @"1 tablespoon baking powder", @"1 teaspoon salt", @"1
3/4 cups sugar", @"2 large eggs, plus 3 large egg yolks", @"1 cup of milk", nil];

Recipe *recipe16 = [Recipe new];


recipe16.name = @"Ham and Cheese Panini";
recipe16.prepTime = @"10 min";
recipe16.image = @"ham_and_cheese_panini.jpg";
recipe16.ingredients = [NSArray arrayWithObjects:@"2 tablespoons unsalted butter", @"4 cups
thinly sliced shallots", @"2 teaspoons fresh thyme", @"1/4 cup grainy Dijon mustard", @"8 slices
rustic white bread", @"8 slices Gruyere cheese", @"8 ounces sliced cooked ham", nil];

recipes = [NSArray arrayWithObjects:recipe1, recipe2, recipe3, recipe4, recipe5, recipe6,


recipe7, recipe8, recipe9, recipe10, recipe11, recipe12, recipe13, recipe14, recipe15, recipe16,
nil];

157
Redesign the Detail View Controller
Originally, the detail view controller only displays the name of recipe. We’re going to
revamp it to show users more information about a recipe. First, download this image pack
(https://dl.dropboxusercontent.com/u/2857188/photo-frame.zip) and add the images to
the projects.

Figure 13-2. Adding the photo frame images to the Project

What’s @2x image?


Nowadays, all models of iPhone available from Apple Store come with retina display. It’s
not the case two or three years ago. The retina display was first introduced with the release
of iPhone 4. In order to cater for both non-retina and retina display, your app should
support two types of screen resolutions (i.e. 320×480 for older generation of iPhone and
640×960 for iPhone 4 or later). Apple has made it fairly easy for iOS developers to support
multiple resolutions. Applications should include two separate files for each image
resource. One file provides a standard-resolution version of a given image, and the second
provides a high-resolution version of the same image. The naming conventions for each
pair of image files is as follows:

Standard: <ImageName>.<filename_extension>
High resolution: <ImageName>@2x.<filename_extension>

The UIImage class handles all of the work needed to load high-resolution images into your
application. When you load an image with UIImage, it automatically determine the screen
resolution and loads the corresponding image file. For instance,

158
UIImage* anImage = [UIImage imageNamed:@"photo-frame"];

The above code will load “[email protected]” on device with high resolution. While on
the standard screen resolution, it loads up the “photo-frame.png”.

Select the Main.storyboard in project navigator and locate the “Recipe Detail View
Controller”. First and foremost, delete the “Label” component and add an image view to the
detail view. Set the width and height of the image view to 297 and 199 respectively.

Figure 13-3. Replace label with image view

Select the attribute inspector and set the image to “photo-frame.jpg”. As you set the image,
it’ll be automatically loaded and displayed in the Storyboard. This image is used to display a
photo frame for the recipe photo.

Next, we add another image view that serves as a placeholder of the recipe photo. Drag the
image view from the Object Library and place it over the photo frame like the screen shown
in Figure 13-4.

159
Figure 13-4. Image View for Recipe Photo

Now, add a label and change the text to “Ingredients”. In the attribute inspector, you can
change the font size and type by altering the “Font” option. Just use the default system font
or pick the one you like. Next, add another label for preparation time. Lastly, drag the Text
View object to the view. The Text View is an UI element for displaying multiple lines of text.
We’ll use it to display the list of ingredients. You should end up with a similar design as
illustrated in figure 13-5.

160
Figure 13-5. Revamped detail view for recipe

Establish the Connection Between Variables and UI


Elements
With the redesigned interface, we’ll establish the connection between our code and the UI
elements. By now you should know how to declare an outlet variable in

161
RecipeDetailViewController.h and establish
the connection with the visual element in
storyboard.

This time, let me show you another way to


establish the connection between outlet
variables and UI elements in Xcode.
Figure 13-6. Show Assistant Editor
In the storyboard, select the “Recipe Detail
View Controller” and click the assistant editor icon to show the Assistant Editor.

Once you’re in the assistant editor mode, you’re able to view your code and storyboard at
the same time. Make sure you select the RecipeDetailViewController.h in the code editor.

Figure 13-7. Assistant Editor Mode

Now, press and hold the control key, click the image view and drag it towards the
“RecipeDetailViewController.h” (see figure 13-8).

162
Figure 13-8. Insert an outlet

As you place the pointer between the “@interface” and “@end” keywords, you should see a
prompt that allows you to insert an outlet. Name the outlet variable as “recipeImageView”.

Figure 13-9. Name the outlet as recipeImageView

163
Once you click the Connect button, Xcode automatically inserts a property in the
RecipeDetailViewController.h.

Repeat the same step for the “PrepTime” label and Text View. Name the outlet of PrepTime
label as “prepTimeLabel” and that of text view as “ingredientsTextView”.

Lastly, remove the original recipeLabel and recipeName properties, which are obsolete.
Instead we’ll declare a new property for storing the recipe object. This property allows other
controllers to pass the recipe details. After all the changes, the
RecipeDetailViewController.h should like this:
#import <UIKit/UIKit.h>
#import "Recipe.h"

@interface RecipeDetailViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *recipeImageView;


@property (weak, nonatomic) IBOutlet UILabel *prepTimeLabel;
@property (weak, nonatomic) IBOutlet UITextView *ingredientsTextView;

@property (nonatomic, strong) Recipe *recipe;

@end

In the viewDidLoad: method of the RecipeDetailViewController.m, change it to the


following code:
- (void)viewDidLoad
{
[super viewDidLoad];

self.title = self.recipe.name;
self.prepTimeLabel.text = self.recipe.prepTime;
self.recipeImageView.image = [UIImage imageNamed:self.recipe.image];

NSMutableString *ingredientsText = [NSMutableString string];


for (NSString* ingredient in self.recipe.ingredients) {
[ingredientsText appendFormat:@"%@\n", ingredient];
}
self.ingredientsTextView.text = ingredientsText;
}

Here, we add a few lines of code to setup the Recipe Detail View. Line 5 of the code sets the
title of navigation bar as the name of recipe. Line 6 and 7 configure the preparation time
label and set the recipe image.

Line 9 to 13 turns the ingredients array into multiple lines of text for the ingredients text
view. The “\n” character is an escape character. In plain English, it means “Put a carriage
return here” or “Start a new line from here”.

Passing Recipe to Detail View Controller


Okay, we’re almost done. Here comes to the last part.

164
As we’ve learnt in the earlier chapter, segue manages the transition between view
controllers. When a segue is triggered, before the visual transition occurs, the storyboard
runtime invokes the prepareForSegue:sender: method of the current view controller. By
implementing this method, we can pass any needed data to the view controller that is about
to be displayed. Here, we’ll pass the selected recipe object to the detail view controller.

In the prepareForSegue: method of RecipeTableViewController.m, change the code to:


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
RecipeDetailViewController *destViewController = segue.destinationViewController;

Recipe *recipe = [recipes objectAtIndex:indexPath.row];


destViewController.recipe = recipe;
}
}

The change is very straightforward as highlighted in bold. Previously, we only pass the
name of the recipe to the detail view controller. This time, we’ll pass the recipe information
as a Recipe object.

Run the App


Wow! You’ve made it! The final step is to execute the app
and test it out. If your app runs properly, it should give
you a much polished recipe detail.

Figure 13-10. Recipe app with


improved detail view

165
What’s Coming Next?
I hope you love this chapter and love the app you just created. Congratulation if you made
this far. You’ve built a polished Recipe app. It’s not a complex app but you manage to use
some of the most common components such as text view, navigation controller and tab bar
controller in iOS.

Don’t stop here. Before reading the next chapter, I encourage you to tweak the app and
make it even better. Say, use a table view (instead of text view) to list out all ingredients or
add a new tab item for showing whatever information you like. As I mentioned throughout
the book, you can’t become a competent developer by just reading a book. You have to
explore, make mistake and most importantly code! So base on what you’ve learnt, tweak the
app and turn it into your own.

In the next chapter, let’s see how to add a search bar for the app.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/1lpt073ij73d90e/RecipeAppImproved.zip.

166
14

ADDING SEARCH BAR


In most of the iOS apps using table view, it is common to have a search bar at the very top
of the screen. How can you implement a search bar for data searching? In this chapter, we
will see how to add a search bar to the recipe app. With the search bar, we’ll enhance the
recipe app to let users search through the recipe list by specifying a search term.

Well, it’s not difficult to add a search bar but it takes a little bit of extra work. We’ll continue
to work on the Xcode project we developed in previous chapter. If you haven’t gone through
the chapter, go back and take some time to check it out.
For your reference, you can download the previous Xcode project from https://www.dropbox.com/s/1lpt073ij73d90e/RecipeAppImproved.zip.

167
Figure 14-1. Enhancing recipe app with a search bar

Understanding Search Display Controller


You can use search display controller (i.e. UISearchDisplayController class) to manage
search in your app. A search display controller manages display of a search bar and a table
view that displays the results of a search of data.

When a user starts a search, the search display controller will superimpose the search
interface over the original view and display the search results. Interestingly, the results are
displayed in a table view that’s created by the search display controller.

168
Figure 14-2. Search Results Table View

Like other view controllers, you can either programmatically create the search display
controller or simply add it into your app using Storyboard. In this chapter, we’ll use the
later approach.

Adding a Search Display Controller in Storyboard


In Storyboard, drag and add the “Search Bar and Search Display Controller” right below the
navigation bar of Recipe Book View Controller. If you’ve done correctly, you should have a
screen similar to the below:

169
Figure 14-3. Adding Search Display Controller

Before proceeding, try to run the app and see how it looks. Without implementing any new
code, you already add a search bar. Tapping the search bar will bring you to the search
interface. However, the search is not working yet.

170
Figure 14-4. Search Bar in Table View App (but it’s not working yet)

Why Search Results Show All Recipes?


As mentioned earlier, the search results are displayed in a table view created by the search
display controller. When developing the recipe app in earlier chapter, we implemented the
UITableViewDataSource protocol so as to tell the table view the total number of rows to
display and the data in each row.

Like UITableView, the table view bundled in the search display controller adopts the same
approach. According to the official documentation of UISearchDisplayController (https://
developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/
UISearchDisplayController_Class/Reference/Reference.html), here are the available
delegates that let you interact with the search result and search bar:

• The search results table view’s data source, which is needed to provide the data for search
result table.
• The search result table view’s delegate, which is used to respond to the user’s selection of
a search item.
• The search display controller’s delegate, which responds to the events such as when the
search starts or ends and when the search interface is displayed or hidden.

171
• The search bar’s delegate, which is responsible for responding to changes in the search
criteria.
Generally, the original view controller is used as the source object for the search results data
source and delegate. You do not need to manually link up the data source and delegate with
the view controller. As you insert the search bar into the view of Recipe Table View
Controller, the appropriate connections for the search display controller are automatically
configured. You can right click on the “Search Display Controller” icon in the dock to reveal
the connections.

Figure 14-5. Search Display Controller Connections

172
Both table views (i.e. the table view in recipe table view controller and the search result
table view) shares the same view controller to handle data population. If you refer to the
code, these are the two methods being invoked when displaying table data:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [recipes count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
RecipeTableCell *cell = (RecipeTableCell *)[self.tableView
dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...


if (cell == nil) {
cell = [[RecipeTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// Display recipe in the table cell


Recipe *recipe = [recipes objectAtIndex:indexPath.row];
cell.nameLabel.text = recipe.name;
cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
cell.prepTimeLabel.text = recipe.prepTime;

return cell;
}

This explains why the search results show a full list of recipes regardless of your search
term.

But why is the height of table row in search result table different from that in the recipe
table? The original recipe table view was designed in storyboard. We set the height of the
prototype cell right within the storyboard. The table view in the search display controller,
however, has no idea about the change of cell height. To fix it, we have to change the row
height programmatically. Add the following code into RecipeTableViewController.m:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 71;
}

You can change the row height by implementing the tableView:heightForRowAtIndexPath:


method of the UITableViewDelegate protocol. Here, we set the height to 71 points.

If you compile and run the app again, the table row in the search result should look better
now. Yet, the search still doesn’t work and this is what we’re going to discuss in the next
section.

173
Figure 14-6. Search Result after row change

Implementing Search Filter


Obviously, to make the search work, there are a couple of things we have to implement/
change:

1. Implement methods to filter the recipe names and return the correct search results
2. Change the data source methods to differentiate the table views. If the tableView being
passed is the table view of Recipe Table View Controller, we show all recipes. On the
other hand, if it’s a search result table view, only the search results are displayed.

First, we’ll show you how to implement the filter. Here we have an array to store all recipes.
We create another array to store the search results. Let’s name it as “searchResults”.
@implementation RecipeTableViewController
{
NSArray *recipes;
NSArray *searchResults;
}

Next, add a new method to handle the search filtering. Filtering is one of the common tasks
in iOS app. The straightforward way to filter the list of recipes is to loop through all the

174
names and filter the result with the if-statement. There is nothing wrong with such
implementation. But iOS SDK offers a better way known as Predicate (https://
developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/
predicates.html#//apple_ref/doc/uid/TP40001798-SW1) to handle search queries. By
using NSPredicate, which is an object representation of a predicate, you write less code.
With just two lines of code, it searches through all recipes and returns the matched results.
Add the following code to RecipeTableViewController.m:
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope
{
NSPredicate *resultPredicate = [NSPredicate predicateWithFormat:@"name contains[c] %@",
searchText];
searchResults = [recipes filteredArrayUsingPredicate:resultPredicate];
}

Basically, a predicate is an expression that returns a Boolean value (true or false). You
specify the search criteria in the format of NSPredicate and use it to filter data in the array.
As the search is on the name of recipe, we specify the predicate as “name contains[c] %@”.
The “name” refers to the name property of the Recipe object. NSPredicate supports a wide
range of filters including:

• BEGINSWITH
• ENDSWITH
• LIKE
• MATCHES
• CONTAINS
Here we choose to use the “contains” filter. The operator “[c]” means the comparison is
case-insensitive.

With the predicate defined, we use the filteredArrayUsingPredicate: method of NSArray


that returns a new array containing objects that match the specified predicate.

To learn more about Predicate, check out Apple’s official documentation (https://
developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/
predicates.html).

Implementing Search Display Controller Delegate


Now we’ve created a method to handle the data filtering. But when should it be called?
Apparently the filterContentForSearchText: method is invoked when user keys in the
search term. The UISearchDisplayDelegate protocol defines a
shouldReloadTableForSearchString: method which is automatically called every time

175
when the search string changes. Therefore implement the following method in the
RecipeTableViewController.m:
-(BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
[self filterContentForSearchText:searchString
scope:[[self.searchDisplayController.searchBar scopeButtonTitles]
objectAtIndex:[self.searchDisplayController.searchBar
selectedScopeButtonIndex]]];

return YES;
}

When user keys in the search term in the search bar, this method is invoked automatically
with the specified search term. We then call up the filterContentForSearchText: method to
do the search.

Displaying Search Results


The last part of the puzzle is to display the search result in the table view of search display
controller. As explained earlier, we have to change the data source methods to differentiate
the table view in Recipe Table View Controller and the search result table view. Here are the
updates of the two data source methods (changes are highlighted in bold):
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (tableView == self.searchDisplayController.searchResultsTableView) {
return [searchResults count];

} else {
return [recipes count];
}
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
RecipeTableCell *cell = (RecipeTableCell *)[self.tableView
dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...


if (cell == nil) {
cell = [[RecipeTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// Display recipe in the table cell


Recipe *recipe = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
recipe = [searchResults objectAtIndex:indexPath.row];
} else {
recipe = [recipes objectAtIndex:indexPath.row];
}

cell.nameLabel.text = recipe.name;
cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
cell.prepTimeLabel.text = recipe.prepTime;

176
return cell;
}

The comparison is pretty straightforward. You can simply compare the tableView object in
the data source methods against the searchResultsTableView of the search display
controller. If the comparison is positive, we display the search results instead of all recipes.

Cool! It Works!
Once you complete the above changes, test your app again. Now the search bar should work
properly.

Figure 14-7. Recipe app with search

Handling Selection in Search Results


Despite the search works, it doesn’t respond to your selection correctly. No matter what the
search results are, the app always displays “Egg Benedict” in the detail view.

Referring to the prepareForSegue: method, we use the indexPathForSelectedRow: method


to retrieve the indexPath of the selected row. As mentioned earlier, the search results are
displayed in a separate table view. But in our original prepareForSegue: method, we always
retrieve the selected row from the table view of Recipe Book View Controller. That’s why we

177
got the wrong recipe in detail view. To make the selection of search results properly, we
have to tweak the prepareForSegue: method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipeDetail"]) {
NSIndexPath *indexPath = nil;
Recipe *recipe = nil;

if (self.searchDisplayController.active) {
indexPath = [self.searchDisplayController.searchResultsTableView
indexPathForSelectedRow];
recipe = [searchResults objectAtIndex:indexPath.row];
} else {
indexPath = [self.tableView indexPathForSelectedRow];
recipe = [recipes objectAtIndex:indexPath.row];
}

RecipeDetailViewController *destViewController = segue.destinationViewController;


destViewController.recipe = recipe;
}
}

We first determine if the user is using search by using a built-in active property in
UISearchDisplayController. In case of search, we retrieve the indexPath from
searchResultsTableView and get the recipe from the searchResults array. Otherwise, we just
get the indexPath from the table view of Recipe Table View Controller.

That’s it. Run the app again and the search selection should work as expected.

178
Figure 14-8. Recipe app displays the correct recipe in detail view

What’s Coming Next


By now, you should know how to implement a search bar in iOS app. In this chapter, we
made the Recipe app even better by enhancing it with the search feature. The search feature
is particularly important when you need to display tons of information in table view. So
make sure you understand the materials we’ve discussed in this chapter. Otherwise, start
over and work on the project again.

In the next chapter, we’ll discuss something new - UICollectionView.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/4h553i9jevjc8xu/RecipeAppSearchBar.zip.

179
15

GRID LAYOUT USING


UICOLLECTIONVIEW
A while back, we built a Recipe app. You’ve learnt how to display a list of recipes by using
UITableView. Wouldn’t be great if it can display the recipe in a nice grid view?

Since the release of iOS 6, the SDK added a new class called UICollectionView (http://
developer.apple.com/library/ios/#documentation/UIKit/Reference/
UICollectionView_class/Reference/Reference.html) that allows developers to create grid-
like layout out of the box.

180
If you have no idea about grid-like layout, just take a look at the built-in Photos app. The
app presents your photos in grid format. Before the introduction of UICollectionView, you
have to write lots of code or make use of third-party libraries in order to build a similar
layout. The UICollectionView, in my opinion, is one of the most spectacular API in iOS
SDK. Not only it simplifies the way to arrange visual elements in grid layout, it even lets
developers customize the layout (e.g. circular, cover flow style layout) without changing the
data.

In this chapter, we will build a simple app to display a collection of recipe photos in grid
layout. Here are what you’re going to learn:

1. Introduction to UICollectionView
2. How to Use UICollectionView to build a simple Grid-based layout
3. Customizing the Collection Cell Background

UICollectionView Basics
The UICollectionView operates in a similar way to UITableView.
While UITableView manages a collection of data items and
displays them on screen in a single-column layout, the
UICollectionView class offers developers flexibility to present
items using customizable layouts. You can present items in multi-
column grids, tiled layout, circular layout, etc. By default, the SDK
comes with a UICollectionViewFlowLayout class that organizes
items into a grid with optional header and footer views for each
section. Later, we’ll use the layout class to build the demo app.

The UICollectionView is composed of several components:

• Cells – instances of UICollectionViewCell. Like UITableViewCell,


a cell represents a single item in the data collection. The cells are
the main elements organized by the associated layout. If a
UICollectionViewFlowLayout is used, the cells are arranged in Figure 15-1. Photos app showing
photos in grid layout
a grid-like format.
• Supplementary views – Optional. It’s usually used to
implement the header or footer views of sections. (We’ll cover this in the next chapter)
• Decoration views – think of it as another type of supplementary view but for decoration
purpose only. The decoration view is unrelated to the data collection. We simply create
decoration views to enhance the visual appearance of the collection view. (We’ll cover this
in the next chapter)

181
Create Simple App with Grid Layout
To better understand how UICollectionView work, let’s get some hand-on experience and
build a simple app. Open Xcode and create a new project using the “Single View
Application” template. Name the project as “RecipePhoto”, click “Next” to continue and
save the project.

Figure 15-2. RecipePhoto – New Xcode Project

Designing the Collection View


Go to Storyboard and delete the default view controller. Alternatively, add a Collection View
Controller from Object Library.

182
Figure 15-3. Add a Collection View Controller

Under the “Size Inspector”, change various size-related attributes:

• Alter the size of the cell and change it to 100 by 100 pixels.
• Change the min spacing for both cells and lines to 10.
• Set the top value of section insets to 20.

183
Figure 15-4. Change the size of Collection View Cell

Next, select the collection view cell and set the identifier as “Cell” in the Attribute Inspector.

Figure 15-5. Cell Reuse Identifier

Beside, add an image view to the cell. Xcode automatically resizes the image view and make
it fit into the cell. Under the “Attribute Inspector”, set the tag value to 100 for later
reference.

184
Figure 15-6. Add Image View to Cell

Lastly, embed the collection view controller in a navigation controller. In the top menu,
select Editor > Embed In > Navigation Controller. That’s it.

Figure 15-7. Embed collection view controller in a navigation controller

185
Under the Attribute Inspector of navigation item, set the title as “Recipe Photo”.

Figure 15-8. Changing the title of navigation bar

Coding the Collection View


In Project Navigator, right-click and select “New File”. Create a new class and name it as
RecipeCollectionViewController. Remember to make it as a subclass of
UICollectionViewController.

Figure 15-9. Create the RecipeCollectionViewController class

186
Go back to the storyboard and assign it as the custom class of the Collection View
Controller.

Figure 15-10. Assign RecipeCollectionViewController as the custom class

As said before, the UICollectionView operates very much like the UITableView. To populate
data in UITableView, what we have to do is to implement two methods defined in the
UITableViewDataSource protocol. Much like the UITableView, the
UICollectionViewDataSource protocol defines data source methods for providing the data
of the collection view. As a minimum requirement, you have to implement both the
collectionView:numberOfItemsInSection: and collectionView:cellForItemAtIndexPath:
methods.

Let’s move on to code the RecipeCollectionViewController class. First, download this image
pack (https://dl.dropbox.com/u/2857188/RecipePhotoImagePack.zip), unzip it and add all
the images into the Xcode project.

In RecipeCollectionViewController.m, declare an array for the image files:


@interface RecipeCollectionViewController () {
NSArray *recipeImages;
}

And initialize it in viewDidLoad method:


- (void)viewDidLoad
{
[super viewDidLoad];

// Initialize recipe image array


recipeImages = [NSArray arrayWithObjects:@"angry_birds_cake.jpg", @"creme_brelee.jpg",
@"egg_benedict.jpg", @"full_breakfast.jpg", @"green_tea.jpg", @"ham_and_cheese_panini.jpg",
@"ham_and_egg_sandwich.jpg", @"hamburger.jpg", @"instant_noodle_with_egg.jpg",

187
@"japanese_noodle_with_pork.jpg", @"mushroom_risotto.jpg", @"noodle_with_bbq_pork.jpg",
@"starbucks_coffee.jpg", @"thai_shrimp_cake.jpg", @"vegetable_curry.jpg",
@"white_chocolate_donut.jpg", nil];
}

Next, implement the two mandatory methods of UICollectionViewDataSource protocol:

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:


(NSInteger)section {
return recipeImages.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView


cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = @"Cell";

UICollectionViewCell *cell = [collectionView


dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages objectAtIndex:indexPath.row]];

return cell;
}

The collectionView:numberOfItemsInSection: method


returns the number of recipe images.

The cellForItemAtIndexPath: method provides the data


for the collection view cells. We first define a cell
identifier and then request the collectionView to
dequeue a reusable cell using that reuse identifier. The
dequeueReusableCellWithReuseIdentifier: method will
automatically create a cell or return you with a cell from
re-use queue. Finally, we get the image view by using the
tag value and assign it with a recipe image.

Now compile and run the app. You should have a grid-
based photo app.

Customizing the Collection Cell


Background
Isn’t UICollectionView great? With a few lines of code,
you can create a grid-based photo app. But what if you Figure 15-11. RecipePhoto App
want to add a picture frame to the photos? Like other UI with Grid-based Layout
elements, the design of the collection view cell lets
developers customize the background easily.

The UICollectionViewCell is actually comprised of three

188
different views including background, selected background
and content view (see figure 15-12):

• Background View – background view of the cell


• Selected Background View – the background view when
the cell is selected. When user selects the cell, this
selected background view will be layered above the
background view.
Figure 15-12. RecipePhoto App
• Content View – obviously, it’s the cell content.
with Grid-based Layout
We have already used the content view to display the recipe
image. We’ll make use of the background view to show the picture frame. In the image pack
you downloaded earlier, it includes a file named “photo_frame.png”, which is the picture
frame. The size of the frame is 100 by 100 pixel. In order to frame the recipe photo, we’ll
first resize the image view of cell and re-arrange its position.

Go to the Main.storyboard and select the image view. Set X to 5 and Y to 8. The width and
height should be changed to 90 and 72 pixels respectively.

Figure 15-13. Resize image view to fit into the photo frame

Next, go back to the RecipeCollectionViewController.m. In the cellForItemAtIndexPath:


method, add the following line of code before “return cell”:
cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"photo-
frame.png"]];

We simply load the photo frame image and set it as the background of the collection view
cell. Now compile and run the app again. Your app should look like the one shown in figure
15-14.

189
Figure 15-14. RecipePhoto App with Photo Frame

What’s Coming Next


Cool, right? That’s why I said the UICollectionView is a great API in iOS SDK. In this
chapter, we just explore the basics of the class and use the default grid-based layout (i.e.
UICollectionViewFlowLayout). By developing custom layout, you can organize the
collection of data in coverflow style or even more stylish format.

As an exercise, try to modify the Recipe app that we built in early chapter and convert it to
use UICollectionView.

In the upcoming chapter, we’ll further see how to add header and footer to the collection
view.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/ky6sirmey8ju9qw/RecipePhoto.zip.

190
C H A P T E R 16

UICOLLECTIONVIEW
PART-2
“Design is a funny word. Some people
think design means how it looks. But of
course, if you dig deeper, it's really how it
works.”
~ Steve Jobs

Previously, we covered the basics of UICollectionView and showed you how to present
items in grid layout. It’ll be interesting to spilt recipes into different sections. Let’s say, the
first section contains recipes for lunch/dinner, while the other section contains recipes for
drinks and desserts.

As you’ve learnt, every collection view must have a data source object providing it with
content to display. Its responsibility is to provide the collection views with the following:

• The number of sections in the collection view


• The number of items in each section
• The cell view for a particular data item
Obviously, the RecipePhoto app we developed contains one section only. In this chapter,
we’ll continue to work on the app and show you how to group the items into different
sections. On top of that, you’ll also learn how to add header or footer view to the collection
view.

191
Split the Recipe Images into Two Sections
In the RecipePhoto app, the RecipeCollectionViewController is the data source object of the
collection view. In order to split the recipes into two sections, there are a number of
changes to be made.

Originally, the recipeImages array stores the image names for all recipes. As we’d like to
split the recipes into two groups, we’ll modify our code and use nested arrays to store
different groups of recipes. The term nested arrays may be new to some of you if you do not
have much programming experience. Figure 1 depicts how we use nested arrays to store the
data.

Figure 16-1. Nested Array

The first group contains images of main dish, while the other stores images of drink and
dessert. The top-level array (i.e. recipeImages) contains two arrays representing the
sections. For each section array, it contains the data items (i.e. image name of recipe) for
that particular section.

Let’s go back to the source code. In the RecipeCollectionViewController.m, change the


initialization of “recipeImages” array in the viewDidLoad: method to the following:
- (void)viewDidLoad
{
[super viewDidLoad];

192
// Initialize recipe image array
NSArray *mainDishImages = [NSArray arrayWithObjects:@"egg_benedict.jpg",
@"full_breakfast.jpg", @"ham_and_cheese_panini.jpg", @"ham_and_egg_sandwich.jpg",
@"hamburger.jpg", @"instant_noodle_with_egg.jpg", @"japanese_noodle_with_pork.jpg",
@"mushroom_risotto.jpg", @"noodle_with_bbq_pork.jpg", @"thai_shrimp_cake.jpg",
@"vegetable_curry.jpg", nil];
NSArray *drinkDessertImages = [NSArray arrayWithObjects:@"angry_birds_cake.jpg",
@"creme_brelee.jpg", @"green_tea.jpg", @"starbucks_coffee.jpg", @"white_chocolate_donut.jpg",
nil];

recipeImages = [NSArray arrayWithObjects:mainDishImages, drinkDessertImages, nil];


}

The above code splits the recipe images into two groups. Next, modify the
numberOfItemsInSection: method to return the number of items for each section:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:
(NSInteger)section {
return [[recipeImages objectAtIndex:section] count];
}

Beside, modify the cellForItemAtIndexPath: method to the following:


- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
cellForItemAtIndexPath:(NSIndexPath *)indexPath{
static NSString *identifier = @"Cell";

UICollectionViewCell *cell = [collectionView


dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
UIImageView *recipeImageView = (UIImageView *)[cell viewWithTag:100];
recipeImageView.image = [UIImage imageNamed:[recipeImages[indexPath.section]
objectAtIndex:indexPath.row]];

cell.backgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"photo-


frame.png"]];

return cell;
}

If you compare the code with the one we implemented previously, the line highlighted in
bold is the only change. We first retrieve the array by using the section number (i.e.
indexPath.section) and then get the specific items from that section.

Lastly, we have to tell the collection view that we have two sections. This can be done by
implementing a method called numberOfSectionsInCollectionView: in
RecipeCollectionViewController.m, which returns the total number of section in the
collection view:
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return [recipeImages count];
}

Now compile and run the app, you should now have a screen similar to Figure 16-2.

193
Figure 16-2. Recipe App with Two Sections

Tweak the Margin of Your Content using Section Insets


The app works but it doesn’t look good. The last row of images in the first section is too
close to the first row of the second section. You can use section insets to add space around
the side of the content. Figure 16-3 illustrates how insets affect the content:

Figure 16-3. Adding margin using section insets

194
You can use UIEdgeInsetsMake to create an inset:

inset = UIEdgeInsetsMake(top, left, bottom, right);

For the RecipePhoto app, we only want to add space between sections. In the viewDidLoad:
method of RecipeCollectionViewController.m, add the following code:
// Adding section spacing
UICollectionViewFlowLayout *collectionViewLayout =
(UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
collectionViewLayout.sectionInset = UIEdgeInsetsMake(20, 0, 20, 0);

The above code creates and assigns the insets to the collection view. Now compile and run
the app again. As you can see from the below screenshot, we’ve added space between the
sections.

Figure 16-4. Recipe Collection Views with Insets

This is not the only way to change the spacing between sections. Alternatively, you can
change the section insets directly in storyboard (see figure 16-5).

195
Figure 16-5. Setting Insets in Storyboard

Adding Header and Footer Views


Now let’s further tweak the app to make it even better. We’ll add header and footer views
for each section of recipes. The UICollectionViewFlowLayout, that our app is using, already
provides optional header and footer views for each section. Here the header and footer
views can be referred as the supplementary views of the flow layout. By default, these views
are disabled in the flow layout. There are a couple of things to configure the header/footer
view:

Enable the section header/footer view in Storyboard (We try to keep thing simple. It’s not a
must to enable the header/footer through Storyboard. Programmatically you can do this by
implementing the appropriate delegate methods or by assigning appropriate values to the
headerReferenceSize and footerReferenceSize properties.)

Implement the collectionView:viewForSupplementaryElementOfKind: method as


required by UICollectionViewDataSource protocol. By implementing the method, you
provide a supplementary view to display in the collection view.

196
Designing Section Header/Footer in Storyboard
First, download the background images (https://dl.dropboxusercontent.com/u/2857188/
collection-view-section-bg.zip) and add them into the Xcode project.

Go back to Storyboard. Select “Collection View” of the Collection View controller. Under
Attributes inspector, check both “Section Header” and “Section Footer” checkboxes next to
Accessories. Once enabled, you should see the header and footer views appeared on screen.

Figure 16-6. Enables section header and footer in Storyboard

Both header and footer views are blank by default. We’re going to design the views using
Storyboard. The header view is designed to display a section title, while the footer view only
shows a static banner image. In Storyboard, drag an image view from Object Library to the
header view and add a label on top of it. Set the font colour to white. For the footer, simply
add an image view.

197
Figure 16-7. Designing header and footer views

Select the image view of the footer view, assign the “collection-view-footer.png” as the
background image in the Attributes inspector.

Figure 16-8. Assign background image for the footer view

198
Repeat the same procedure to set the background image for header view. Select the image
view of the header view, assign the “collection-view-header.png” as the background image
in the Attributes inspector.

Most importantly, we have to assign an identifier for the header and footer view. The
identifier will be used in code for identifying the view. Select the Collection Reusable View
of the header, set the identifier as “HeaderView” in the Attributes inspector. For the
Collection Reusable View of the footer, set the identifier as “FooterView”.

Figure 16-9. Assigning identifier for header and footer view

New Class for Header View


By default, both header and footer views are associated with the UICollectionReusableView
class. To display our own title in the header view, we have to create a custom class which is
a subclass of UICollectionReusableView. Let’s name the new class as
“RecipeCollectionHeaderView”.

199
Figure 16-10. Create a new class for header view

In Storyboard, select the Collection Reusable View of the header, set the custom class as
“RecipeCollectionHeaderView” in Identify inspector. Switch to the assistant editor mode.
Press and hold the control key, click the label in the header and drag it towards the
RecipeCollectionHeaderView.h to insert an Outlet variable. Name the variable as
“titleLabel”.

Figure 16-11. Establish connection with RecipeCollectionHeaderView class

200
Implementing viewForSupplementaryElementOfKind
Method
If you try to build and run the app, you will not see the header and footer as we haven’t
implemented the “viewForSupplementaryElementOfKind:” method. Select the
“RecipeCollectionViewController.m” and add the following import statement statement at
the very beginning:
#import "RecipeCollectionHeaderView.h"

Then implement the viewForSupplementaryElementOfKind: method by using the


following code:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView
viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *reusableview = nil;

if (kind == UICollectionElementKindSectionHeader) {
RecipeCollectionHeaderView *headerView = [collectionView
dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader
withReuseIdentifier:@"HeaderView" forIndexPath:indexPath];
NSString *title = [[NSString alloc]initWithFormat:@"Recipe Group #%i", indexPath.section
+ 1];
headerView.titleLabel.text = title;

reusableview = headerView;
}

if (kind == UICollectionElementKindSectionFooter) {
UICollectionReusableView *footerview = [collectionView
dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter
withReuseIdentifier:@"FooterView" forIndexPath:indexPath];

reusableview = footerview;
}

return reusableview;
}

The above code tells the collection view which header/footer view should be used in each
section. We first determine if the collection view asks for a header or footer view. This can
be done by using the kind variable. For header view, we dequeue the header view (by using
dequeueReusableSupplementaryViewOfKind: method) and set an appropriate title. As you
can see from line 6 and 14 of the code, we use the identifier that we’ve assigned earlier to get
the header/footer view.

Now compile and run the app again. Your app should display the header and footer in each
section.

201
Figure 16-12. Recipe App with Header and Footer

What’s Coming Next?


I hope you enjoy the chapter. We’ve gone through the basics of supplementary views and
showed you how to add header/footer in collection view.

In the next chapter, we’ll continue to discuss collection view and show you how to interact
with the collection cell. It’s going to be fun.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/4d7tan35q4sv7mm/RecipePhotoWithSection.zip.

202
C H A P T E R 17

UICOLLECTIONVIEW
PART-3
“I fear not the man who has practiced
10,000 kicks once, but I fear the man who
has practiced one kick 10,000 times.”
~ Bruce Lee

In the past two chapters, we covered the basics of UICollectionView and header/footer
customization. You should already know how to display items using UICollectionView.
However, we haven’t covered how to interact with the collection view cell.

As mentioned before, the collection view works in a way pretty much like table view. To give
you a better idea, we’ll cover how to interact with the collection items such as the ways to
handle single and multiple item selection. To provide you with a working example of how
the item selection works, we’ll continue to improve the RecipePhoto app. Here are what
we’re going to implement:

1. When user taps a recipe photo, the app will bring up a modal view and display the photo
in larger size.
2. We’ll also implement Facebook sharing in the app in order to show you multiple item
selection. Users are allowed to select multiple photos and share them on Facebook.

203
Handling Single Selection
First, we’ll improve the RecipePhoto app to handle
single selection. When user taps any of the recipe
photo, the app will bring up a modal view to display
the photo in higher resolution.

Designing the User Interface


To begin, let’s design the view controller for
displaying the recipe photo. Go to Main.storyboard,
drag a View Controller from Object library. Then add
an Image View to it and set the width and height to
320 and 200 respectively. Lastly, add a navigation
bar to the top of view and assign it with a Bar Button
Item. Change the title of the navigation bar to
“Photo”. Under the Attribute Inspector, set the
identifier of the button item to “Done”. Your View
Controller should look similar to the below one:

Figure 17-1. Managing


selections in RecipePhoto App

204
Figure 17-2. Designing the Modal View Controller

As we want to display the view controller when user taps any of the recipe photo in the
collection view, we’ll connect the collection view with the view controller using a segue.
Press the control button, click on the “Collection View Cell” and drag it towards the view
controller just added. Select “modal” for the style and set the segue identifier to
“showRecipePhoto”.

205
Figure 17-3. Segue Connection

If you compile and run the app, you’ll end up with an empty view when selecting any of the
recipe photo. As we haven’t written up the code, the modal view controller has no idea of
the selected recipe photo. So, create a new class and name it as “RecipeViewController”.
Make it a subclass of UIViewController.

In Storyboard, select the view controller we just created and set the custom class to
RecipeViewController.

206
Figure 17-4. Set the custom class as RecipeViewController

Next, we’ll establish a connection between the image view and the RecipeViewController.h.
Switch to Assistant Editor mode. Press and hold the control key, click the image view and
drag it towards the “RecipeViewController.h”. Name the variable as “recipeImageView”.

Figure 17-5. Defining the outlet

Repeat the same procedure and establish a connection with “action” type for the Done
button. Name it as close.

207
Figure 17-6. Defining the action method for the Done button

Into the Code


In order to let other controllers pass the image name, we’ll also add a “recipeImageName”
property for this purpose. After the changes, here is the code for the
RecipeViewController.h:
@interface RecipeViewController : UIViewController

@property (weak, nonatomic) IBOutlet UIImageView *recipeImageView;


- (IBAction)close:(id)sender;

@property (weak, nonatomic) NSString *recipeImageName;

@end

When displayed, the RecipeViewController will load the specified recipe image in the image
view. In the RecipeViewController.m, change the viewDidLoad method to the following:
- (void)viewDidLoad
{
[super viewDidLoad];

self.recipeImageView.image = [UIImage imageNamed:self.recipeImageName];


}

Now the RecipeViewController should be able to load the recipe image in the image view.
But there is still one thing left. How can we identify the selected item of the collection view
and pass the image name to the RecipeViewController?

If you understand how the data passing works via segue, you know we’ll need to implement
the prepareForSegue:sender: method in the RecipeCollectionViewController, which is the

208
source view controller of the segue. Select the “RecipeCollectionViewController.m” and add
the following code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"showRecipePhoto"]) {
NSArray *indexPaths = [self.collectionView indexPathsForSelectedItems];
RecipeViewController *destViewController = segue.destinationViewController;
NSIndexPath *indexPath = [indexPaths objectAtIndex:0];
destViewController.recipeImageName = [recipeImages[indexPath.section]
objectAtIndex:indexPath.row];
[self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
}
}

Just like UITableView, the UICollectionView class provides the


indexPathsForSelectedItems: method that returns the index paths for the selected items.
You may wonder why there are multiple index paths returned. The reason is that
UICollectionView supports multiple selections. Each of the index path corresponds to a
particular selected item. As for this demo, we only have single item selection. Therefore,
we’ll pick the first index path and retrieve the selected image.

In collection view, when user taps on a collection cell, the cell changes to the highlighted
state and then to the selected state. The last line of code is to deselect the selected item once
the image is displayed in the modal view controller.

Don’t forget to add the import statement at the very beginning of


RecipeCollectionViewController.m, otherwise, your code won’t compile properly:
#import "RecipeViewController.h"

Now, let’s build and run the app. After the app is launched, tap any of the recipe and you
should see a modal view showing the selected recipe image.

209
Figure 17-7. Recipe App – Single Selection

If you’ve tried to close the modal view, it doesn’t work properly. Obviously, we leave out the
implementation of the “close” method in the RecipeViewController. Simply change the
close: method in the RecipeViewController.m to the following:
- (IBAction)close:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
}

This dismissViewControllerAnimated: method tells the view controller to dismiss. Compile


and run the app again. The close button should now work.

Handling Multiple Selections


UICollectionView supports both single and multiple selections. However, user is allowed to
select single item by default. The allowsMultipleSelection property of UICollectionView
class controls whether multiple items can be selected simultaneously. To enable multiple
selection, the trick is to set the property to YES.

To give you a better idea of how multiple selection works, we’ll continue to tweak the
RecipePhoto app. User are allowed to select multiple recipes and share them on Facebook
in the following ways:

210
1. User taps the “Share” button in the navigation bar of the main interface. Once the sharing
starts and the button title is automatically changed to “Upload”.
2. User selects the recipe photos to share.
3. After the selection, user taps the “Upload” button. The app will bring up the Facebook
dialog to share the photos on Facebook.

Designing the User Interface


We’ll first add the “Share” button in the navigation bar of Recipe Collection View
Controller. Go to Storyboard, drag a Bar Button Item from Object library and add it to the
navigation bar of Recipe Collection View Controller.

211
Figure 17-8. Adding the Share Button

As usual, establish a connection between the Share button and


RecipeCollectionViewController.h. Name the property as “shareButton”. Also add an action
method named “ShareButtonTapped”. The method will be called when the Share button is
tapped.

212
Figure 17-9. Establish Connection with the Share Button

Your code of RecipeCollectionViewController.h should look like this:


@interface RecipeCollectionViewController : UICollectionViewController

@property (weak, nonatomic) IBOutlet UIBarButtonItem *shareButton;


- (IBAction)shareButtonTapped:(id)sender;

@end

Dive Into the Code


The Recipe app now offers two modes: single selection and multiple selection. When user
taps the “Share” button, the app will go into the multiple selection mode that allows user to
select multiple photos for sharing. To support the multiple selection mode, we’ll add two
variables to the RecipeCollectionViewController.m:

• shareEnabled – it’s a boolean variable to indicate the selection mode. If it’s set to YES, it
indicates the “Share” button is tapped and multiple selection is enabled.
• selectedRecipes – it’s an array to store the selected recipes
Your code should look like below:

@interface RecipeCollectionViewController () {
NSArray *recipeImages;
BOOL shareEnabled;
NSMutableArray *selectedRecipes;
}

In addition, add the following line of code in viewDidLoad method to initialize the array:
selectedRecipes = [NSMutableArray array];

213
Managing Item Selection and Deselection
The UICollectionViewDelegate protocol defines methods that allow you to manage the
selection and highlighting of items in a collection view. When user selects an item, the
collectionView:didSelectItemAtIndexPath: method will be called. We’ll implement this
method and add the selected items into the selectedRecipes array. Place the following code
before the “@end” statement:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath
*)indexPath
{
if (shareEnabled) {
// Determine the selected items by using the indexPath
NSString *selectedRecipe = [recipeImages[indexPath.section] objectAtIndex:indexPath.row];
// Add the selected item into the array
[selectedRecipes addObject:selectedRecipe];
}
}

The UICollectionViewCell class provides a property named selectedBackgroundView for


setting the background view of a selected item. To indicate a selected item, we’ll change the
background image of collection cell to a different image when user selects a recipe photo.
You can download the photo-frame-selected.png from https://dl.dropboxusercontent.com/
u/2857188/photo-frame-selected.png and add it into the RecipePhoto project. Edit the
collectionView:cellForItemAtIndexPath: method and add the following line of code:
cell.selectedBackgroundView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"photo-
frame-selected.png"]];

Figure 17-10. Changing the background image for selected cell

Now when user selects a recipe item, the background of the selected cell will be altered (see
Figure 17-10).

214
Not only we have to handle item selection, we also need to cater for deselection. For any
reasons, user may deselect an item from the collection view. When an item is deselected, it
should be removed from the selectedRecipes array. So place the following code after the
above method:
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath
*)indexPath
{
if (shareEnabled) {
NSString *deSelectedRecipe = [recipeImages[indexPath.section]
objectAtIndex:indexPath.row];
[selectedRecipes removeObject:deSelectedRecipe];
}
}

Next, we’ll move onto the implementation of shareButtonTouched: method, which is called
when user taps the Share button. Edit the method with the following code:
- (IBAction)shareButtonTapped:(id)sender {
if (shareEnabled) {

// Post selected photos to Facebook


if ([selectedRecipes count] > 0) {
if([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
SLComposeViewController *controller = [SLComposeViewController
composeViewControllerForServiceType:SLServiceTypeFacebook];

[controller setInitialText:@"Check out my recipes!"];


for (NSString *recipePhoto in selectedRecipes) {
[controller addImage:[UIImage imageNamed:recipePhoto]];
}

[self presentViewController:controller animated:YES completion:Nil];


}
}

// Deselect all selected items


for(NSIndexPath *indexPath in self.collectionView.indexPathsForSelectedItems) {
[self.collectionView deselectItemAtIndexPath:indexPath animated:NO];
}

// Remove all items from selectedRecipes array


[selectedRecipes removeAllObjects];

// Change the sharing mode to NO


shareEnabled = NO;
self.collectionView.allowsMultipleSelection = NO;
self.shareButton.title = @"Share";
[self.shareButton setStyle:UIBarButtonItemStylePlain];

} else {

// Change shareEnabled to YES and change the button text to Upload


shareEnabled = YES;
self.collectionView.allowsMultipleSelection = YES;
self.shareButton.title = @"Upload";
[self.shareButton setStyle:UIBarButtonItemStyleDone];

}
}

215
To help you understand the above, let’s take a look at the code line by line:

Line 5-16: In sharing mode, after user taps the “Upload” button, we’ll bring up Facebook
composer. iOS SDK allows you to easily bundle social sharing feature in your app via the
SLComposeViewController class. The SLComposeViewController comes with some built-in
methods to compose a post for various social networking services such as Facebook and
Twitter. Here we use some of its built-in methods to upload multiple photos to Facebook.
We first Use the isAvailableForServiceType: class method to check if Facebook is configured
on the user’s iOS device. If it’s configured, we create a Facebook composer and attach the
selected images using “addImage” method. Finally, we call up the presentViewController:
method to display the composer on screen.

Line 19-24: After uploading the photos to Facebook, we deselect the selected items and
remove them from the selectedRecipes array.

Line 27-30: Lastly, we switch back to the single selection mode and change the button title.

Line 35-38: If sharing mode is originally disabled, we’ll put the app into sharing mode and
enables multiple selections. At the same time, we change the title of button to “Upload”.

As “SLComposeViewController” is a class provided by the Social framework, remember to


import the Social.h file at the very top of the “RecipeCollectionViewController.m”:
#import <Social/Social.h>

We’re almost done. However, if you run the app now, you’ll end up with a bug. After
switching to the sharing mode, the modal view still appears when you select any of the
recipe photos. The result is not what we expect. The reason is that the segue is invoked
every time when the collection cell is tapped. Obviously, we don’t want to trigger the segue
when the app is in sharing mode.

The shouldPerformSegueWithIdentifier: method allows you to control it. We only want to


trigger the segue when it’s in single selection mode. Place the following code before the
“@end” statement:
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
if (shareEnabled) {
return NO;
} else {
return YES;
}
}

216
Ready to Test Your App
Great! Now compile and run the app again. Tap the Share button, select a few recipe photos
and tap the Upload button to share them over Facebook. If the app can’t bring up the
Facebook composer, you probably forget to set up your Facebook account in the Simulator.
Go back to home screen, select Settings and then Facebook. Sign in with your Facebook
account. Once configured, re-run the app and try it out again.

Figure 17-11. Recipe App – Multiple Selection and Facebook Sharing

What’s Coming Next


I hope you love this chapter. It’s a long chapter but isn’t it fun? Congratulation if you made
this far. Not just you learn how to handle selection in collection view, you should now know
how to share photos on Facebook. It’s always great to bundle some social sharing features
in your apps.

217
By now, after going through a series of Collection View chapter, you should know how to
arrange data items in grid view, add header/footer view and interact with the collection
cells. As an exercise, try to tweak the RecipePhoto app to support Twitter sharing. One hint
is to read the documentation of SLComposeViewController (https://developer.apple.com/
library/ios/documentation/NetworkingInternet/Reference/
SLComposeViewController_Class/Reference/Reference.html).

In the coming chapter, we’ll look into something new and show you how to use Core Data.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/j85v19s4ycajg74/RecipePhotoSelection.zip.

218
C H A P T E R 18

INTRODUCTION TO
CORE DATA
“The most effective way to do it, is to do
it.”
~ Amelia Earhart

Earlier, you’ve learnt how to save data in a plist file. File is one of the ways to save data
persistently. In this chapter, we’ll talk about Core Data and how to use it to save persistent
information.

When building app, sometimes you’d like to save data in a persistent storage such as file or
database instead of just holding it in memory. By saving the data to a database, for example,
you’ll not lose the data even the app quits or crashes. There are multiple ways to store data
in iOS devices. The property file that was covered previously is one of the ways for storing
persistent data. However, due to the nature of property file, it is often used to store a small
amount of data such as application settings. Database is more suitable to handle large set of
data. In this chapter, we’ll see how to use Core Data to manage data in database.

The focus of the chapter is to provide a practical introduction of Core Data framework. I
expect you’ve already gone through the chapters about Storyboard and UITableView. I will
not give in-depth explanation about how to create view controller in storyboard or table
view. Refer to the earlier chapters if you haven’t equipped yourself with the knowledge.

219
Core Data is Not a Database
When we talk about persistent data, people probably think of database. If you are familiar
with Oracle or MySQL, you know that relational database stores data in the form of table,
row and column, and it usually facilitates access through what-so-called SQL query.
However, don’t mix up Core Data with database. Though SQLite database is the default
persistent store for Core Data on iOS, Core Data is not a relational database. It is actually a
framework that lets developers interact with database (or other persistent storage) in an
object-oriented way. In other words, you don’t need to use SQL to talk to the database. With
Core Data, you can simply map the objects in your apps to the table in the database without
even knowing any SQL.

As usual, to illustrate the concept, let’s create an iPhone app using Core Data. This app is
called RecipeStore. It is very similar to the Recipe app we developed before, but the recipe
data will be saved in the database using Core Data. Figure 1 shows a sample screenshot of
the app.

Figure 18-1. Recipe App using Core Data

220
Creating a Sample App with Core Data
First, create a project using the “Empty Application” template. You may wonder why we use
a different template to create the project. Not all project templates provide the Core Data
option, the option is only available on Master-Detail Application and Empty Application
templates.

Figure 18-2. Create a New Project with Empty Application Template

In the next screen, set the name of the project as RecipeStore, select iPhone in Devices
family and set the class prefix as RecipeStore. Lastly, remember to check the “Use Core
Data” option.

221
Figure 18-3. Set up Xcode Project Options

Once the project is created, Xcode automatically generates a RecipeStore.xcdatamodeld file.


Later we’ll use this file to create the entities in the model.

However, for project created using the Empty Application template, it doesn’t include the
storyboard. So click the “RecipeStore” folder in project navigator and select “New File...”.
Select “Storyboard” template and click “Next”. Name the storyboard file as Main.

222
Figure 18-4. Create a storyboard file

After creating the storyboard file, assign it as the main interface of the app. Simply select
the RecipeStore project in project navigator > RecipeStore target. You can set the main
interface under the Deployment Info section.

Figure 18-5. Assigning the storyboard file as main interface

223
Lastly, select the RecipeStoreAppDelegate.m and update the
didFinishLaunchingWithOptions: to the following:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary


*)launchOptions
{
return YES;
}

Core Data Stack


Before we start working on the project, you first have to understand the Core Data Stack:

Managed Object Model – It describes the schema that you use in the app. If you have a
database background, think of this as the database schema. However, the schema is
represented by a collection of objects (also known as entities). In Xcode, the Managed
Object Model is defined in a file with the extension .xcdatamodeld. You can use the visual
editor to define the entities and their attributes, as well as, relationships.

Persistent Store Coordinator – SQLite is the default persistent store in iOS. However,
Core Data allows developers to setup multiple stores containing different entities. The
Persistent Store Coordinator is the party responsible to manage different persistent object
stores and save the objects to the stores. Forget about it you don’t understand what it is.
You’ll not interact with Persistent Store Coordinator directly when using Core Data.

Managed Object Context – Think of it as a “scratch pad” or temporary memory area


containing objects that interacts with data in persistent store. Its job is to manage objects
created and returned using Core Data. Among the components in the Core Data Stack, the
Managed Object Context is the one you’ll work with for most of the time. In general,
whenever you need to fetch and save objects in persistent store, the context is the first
component you’ll talk to.

The below illustration can probably give you a better idea about the Core Data Stack:

224
Figure 18-6. Core Data Stack

Defining Managed Object Model


Let’s move on to build the app. The first step is to open the data model named
RecipeStore.xcdatamodeld and define the object model. Here we’ll define a Recipe
entity that will be used to store the recipe information to database. To create an entity, click
the + button in the bottom-left of the editor view and name the entity as Recipe.

225
Figure 18-7. Adding Recipe entity in the model

Next we’ll add attributes to the Recipe entity. You can think of attributes as a counterpart of
properties of a class. You can simply click the + button under the attributes section to
create a new attribute. Add three attributes including name, image and prepTime. Set the
type as String.

226
Figure 18-8. Adding Attributes to the Recipe entity

Designing the User Interface


Go to Storyboard and create the user interface like below:

Figure 18-9. MyStore App – Storyboard

While we encourage you to build the user interface, you can also skip the procedures and
download the project template from https://dl.dropboxusercontent.com/u/2857188/
RecipeStoreTemplate.zip. The template already comes with the Storyboard and set up all
the view controller classes for you. This gives you a good starting point to work on Core
Data. If you use the template, you can skip this section and go directly to the “Diving Core
Data” section.

227
If you design to create the user interface on your own, here are the stuffs you need to set up:

• First, delete the default view controller and drag a Table View Controller and embed it in a
Navigation Controller.
• Next, drag a bar button item to the top-right part of navigation bar and set the identifier
as “Add”. This will automatically change the button to a “+” button. Next, select the
prototype cell, change its style to “Subtitle” and set the identifier as “Cell”. Also change the
title of navigation bar as “Recipe Store”.

Figure 18-10. Creating the Table View Controller

228
• Drag a View Controller to the Storyboard and embed it in a Navigation Bar. Next, drag
two bar button items into the navigation bar. Set one’s identifier as “Cancel” and the other
one as “Save”. In the content view, add three text fields and name the placeholder
attributes as “Name”, “Image Filename” and “Preparation Time”.
• This view controller will be shown when user taps the “+” button in the table view
controller. So finally, press and hold the Control key, click the “+” button and drag
towards the navigation controller that embeds the view controller. Select “Modal” as the
Segue action to connect the + button and the view controller.

Figure 18-11. Designing the View Controller for adding recipes

Creating View Controller Classes


Create a new class by right-clicking on the RecipeStore folder and select New File. Use
Objective-C class template and name the class as AddRecipeViewController. Make it as a
subclass of UIViewController. Navigate to the Storyboard, select the View Controller and
associate it with the AddRecipeViewController class.

Once done, create another class named RecipeStoreTableViewController and make it a


subclass of UITableViewController. Again, go to Storyboard and set the custom class of the
table view controller as the RecipeStoreTableViewController.

Lastly, wire up the UITextFields to the AddRecipeViewController.h file. Also create two
action methods for the save and cancel buttons and name the methods as save: and cancel:
respectively.

229
Figure 18-12. Creating IBOutlet and Action Methods

After the change, your code should like this:


@interface AddRecipeViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *nameTextField;
@property (weak, nonatomic) IBOutlet UITextField *imageTextField;
@property (weak, nonatomic) IBOutlet UITextField *prepTimeTextField;
- (IBAction)save:(id)sender;
- (IBAction)cancel:(id)sender;

@end

Diving into Core Data


With the user interface, it’s time to go into the details of Core Data. Apparently, there are a
couple of areas we have to implement:

1. Save recipe information in the AddRecipeViewController


2. Fetch recipe information from the database and populate the data into Table View
Controller

We’ll look into the implementation one by one.

Creating Managed Objects


First, we’ll implement the AddRecipeViewController to let user add a recipe into the
database. Open the AddRecipeViewController.m file and add the following code after
@implementation AddRecipeViewController:
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}

230
In brief, what we did in the code is to retrieve a managed object context of the application.
You can get the application’s context directly from the application delegate. In order to save
data to the persistent store, the very first thing to do is to get the context. You can consider
the managed object context as the gateway to the rest of the Core Data architecture. Its
primary responsibility is to manage a collection of managed objects. Through the context,
you can fetch or make changes to the managed objects. Later we’ll use the above method to
get the context.

Next, we’ll implement the save: and cancel: method. Update both methods using the
following code:
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];

// Create a new managed object


NSManagedObject *newRecipe = [NSEntityDescription insertNewObjectForEntityForName:@"Recipe"
inManagedObjectContext:context];
[newRecipe setValue:self.nameTextField.text forKey:@"name"];
[newRecipe setValue:self.imageTextField.text forKey:@"image"];
[newRecipe setValue:self.prepTimeTextField.text forKey:@"prepTime"];

NSError *error = nil;


// Save the object to persistent store
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}

[self dismissViewControllerAnimated:YES completion:nil];


}

- (IBAction)cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}

For the save: method, we first grab the managed object context and then create a
NSManagedObject for the new recipe. To use Core Data Framework, the model objects
should be instances of NSManagedObject or instances of classes that inherit from
NSManagedObject. Recalled that we’ve created an entity named Recipe in the data model,
we use the managed objects to associate with the entity. In other words, a managed object
provides a representation of a record in a persistent store under a given context.

To create a new instance of NSManagedObject for the Recipe entity, we use the
insertNewObjectForEntityForName: method of the NSEntityDescription class to create a
managed object. NSManagedObject works much like a dictionary. Once you created the
managed object (i.e. newRecipe), you can set the value using the entity properties as key.
Lastly, we call up the save: method of the context to save the object into database. Always
remember the change of the model objects will not be save to the persistent store until the
save: method is called.

231
Finally, it’s the cancel: method. For the method, we simply invoke the
dismissViewControllerAnimated: method to dismiss the current view controller when user
taps the cancel button.

You can now hit the Run button to try out your app. Tap the “+” button to bring up the Add
Recipe View and save a new recipe. However, the new recipe is not yet displayed in the
table. Let’s move on to see how you can fetch the information from database.

Fetching Managed Objects


In project navigator, select RecipeStoreTableViewController.m, first declare a mutable
array with the name recipes:
@implementation RecipeStoreTableViewController {
NSMutableArray *recipes;
}

Again, add the following code right after “@implementation


RecipeStoreTableViewController” for grabbing the managed object context:
- (NSManagedObjectContext *)managedObjectContext {
NSManagedObjectContext *context = nil;
id delegate = [[UIApplication sharedApplication] delegate];
if ([delegate performSelector:@selector(managedObjectContext)]) {
context = [delegate managedObjectContext];
}
return context;
}

Next, add a viewDidAppear: method:


- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];

// Fetch the recipes from persistent data store


NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Recipe"];
recipes = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];

// Reload table data


[self.tableView reloadData];
}

In order to retrieve the data from persistent store (i.e. database), what we just need to do is
to get the context, create a fetch request and specify the entity to fetch. Therefore, like what
we did in the Add Recipe View Controller, we first grab the managed object context. To
fetch recipe information from database, we create an instance of NSFetchRequest with the
entity Recipe and invokes the executeFetchRequest: method to retrieve all the recipes from
the database. Optionally, you can specify the search criteria via a set of methods in
NSFetchRequest class. If you are familiar with relational databases, this instance works like
a SELECT clause.

232
Why use viewDidAppear: method instead of viewDidLoad:? If you’re new to the
viewDidAppear: method, it is a method that is called when a view is actually visible. While
the viewDidLoad: method is called once when the controller is loaded into the memory, the
viewDidAppear: method is called every time when the view is displayed. We make use of
this method to reload the recipe data after a new recipe is created.

Note: This isn’t the best way to handle the data reload. The method gets called even user
cancels the recipe creation. Obviously, you only want to reload the data whenever a recipe is
successfully added. In later chapter, we’ll show you how to use NSNotificationCenter to
coordinate the data reload between two view controllers. Meanwhile, let’s keep it simple
and use the viewDidAppear: method.

Populating Recipes into Table View


You should know how to popular the recipes into the table view. I’ll not go into the details
of the code. What you’ll need to do is to implement the required methods of
UITableViewDelegate protocol:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section


{
// Return the number of rows in the section.
return [recipes count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

// Configure the cell...


NSManagedObject *recipe = [recipes objectAtIndex:indexPath.row];
cell.textLabel.text = [recipe valueForKey:@"name"];
NSString *description = [[NSString alloc] initWithFormat:@"%@ - %@", [recipe
valueForKey:@"image"], [recipe valueForKey:@"prepTime"]];
cell.detailTextLabel.text = description;

return cell;
}

You should be very familiar with the above code, which is the simple way to display data
into the table view. As you can see from the code in the cellForRowAtIndexPath: method,
you’ll notice the NSManagedObject is pretty much like NSDictionary. You can simply use
the valueForKey: method to retrieve the attribute value of a specific key. Here we display

233
the name of recipe as the cell title. For the subtitle, it is combination of image filename and
preparation time.

That’s it. Let’s try to run the app and test it. If everything is okay, your app should launch
properly. For the very first time you open it, the table view is empty. Try to add some
recipes and they should be populated automatically in the table view.

Figure 18-12. RecipeStore App using Core Data

What’s Coming Next


It’s a lengthy tutorial but I try to elaborate the implementation as detail as possible so
everyone can follow. As you can see, with Core Data, you can easily save and retrieve data
to/from database without knowing any SQL. Everything is done behind the scene. This
chapter kicks off the first part of Core Data series. In the next chapter, we’ll talk more about
update and deletion of managed objects.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/1yvci2bqcaqkvwo/RecipeStore.zip.

234
C H A P T E R 19

CORE DATA
PART-2
“Nothing is impossible, the word itself
says 'I'm possible'!”
~ Audrey Hepburn

This is the second chapter for our Core Data series. Previously, we gave you a brief
introduction of Core Data and created a simple app to store recipes in database. However,
we only showed you how to insert records into database through Core Data Framework and
left out the update & delete operations.

In this chapter, we’ll continue to work on the app and focus on the following areas of Core
Data:

• Updating and deleting a managed object using Core Data


• Viewing the raw SQL statement for debugging purpose

Updating and Deleting a Managed Object


If this is the first time you learn about Core Data, we recommend you to read the previous
chapter and build the demo app from scratch. In case you do not want to start from the very
beginning, you can download this Xcode project from https://dl.dropboxusercontent.com/
u/2857188/RecipeStore.zip to start with.

235
In the last chapter, we already discussed how to fetch and save a managed object using Core
Data. So how can you update or delete an existing managed object from database?

Deleting a Managed Object


Let’s talk about the delete operation first. Deleting a managed object is very
straightforward. You can simply call up the deleteObject: method of its managed object
context and pass the object you want to delete as argument.

Okay, let’s continue to develop the demo app. To allow user to delete a record from the table
view, as you know, we need to implement the canEditRowAtIndexPath: and
commitEditingStyle: methods of the UITableViewDataSource protocol. Add the following
code to the RecipeStoreTableViewController.m:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
// Return NO if you do not want the specified item to be editable.
return YES;
}

- (void)tableView:(UITableView *)tableView commitEditingStyle:


(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
NSManagedObjectContext *context = [self managedObjectContext];

if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[recipes objectAtIndex:indexPath.row]];

NSError *error = nil;


if (![context save:&error]) {
NSLog(@"Can't Delete! %@ %@", error, [error localizedDescription]);
return;
}

// Remove recipe from table view


[recipes removeObjectAtIndex:indexPath.row];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}

I’ll not go into the details about how to remove a row from table view as we’ve gone through
it in earlier chapter. Let’s walk through the code in the commitEditingStyle: method. Like
creating managed object, we first grab the manage object context. The context provides the
deleteObject: method that allows you to delete the specific object from database. However,
the deletion will not occur until the save: method is called. So remember to invoke the
method to commit the change. Right after the removal of the object from database, we also
remove the record from the table view.

Now, you’re ready to run the app and try to remove a record from database. Your app
should look similar to the following:

236
Figure 19-1. Delete a recipe from database

Updating a Managed Object


Just like deletion, it’s pretty easy to update a managed object. But before diving into the
update operation, let’s enhance the UI of app to let user update the recipe. Go to the
Storyboard and add a new segue for the table cell. This segue is used to connect a table cell
and the navigation controller. When user selects a recipe in the table view, the Add Recipe
View Controller will be displayed and show the recipe information.

237
Figure 19-2. Adding a new segue for updating device

To differentiate the segue from the one for adding a new device, we set an identifier as
UpdateRecipe.

Next, insert the following import statement at the very beginning of


RecipeStoreTableViewController.m :
#import "AddRecipeViewController.h"

and add the prepareForSegue: method in the same file:


- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:@"UpdateRecipe"]) {
NSManagedObject *selectedRecipe = [recipes objectAtIndex:[[self.tableView
indexPathForSelectedRow] row]];
UINavigationController *destViewController = segue.destinationViewController;
AddRecipeViewController *recipeViewController =
(AddRecipeViewController*)destViewController.topViewController;
recipeViewController.recipe = selectedRecipe;
}
}

When user selects a specific recipe in the table view, it’ll go through the “UpdateRecipe”
segue. There is a little trick here. The destination view controller is actually a navigation
controller as we’ve embedded the “Add Recipe View Controller” in it. The

238
topViewController property allows you to get the view controller at the top of the
navigation stack. In this case, it is the AddRecipeViewController. Lastly, we pass the
selected recipe to the view controller for display.

You’ll see some compilation errors when you add the above code as we haven’t added the
“recipe” property in the AddRecipeViewController.h. Go ahead and add the property:
@property (strong) NSManagedObject *recipe;

Obviously, to display the details of the selected recipe, we’ll load the information in the
viewDidLoad: method:
- (void)viewDidLoad
{
[super viewDidLoad];

if (self.recipe) {
[self.nameTextField setText:[self.recipe valueForKey:@"name"]];
[self.imageTextField setText:[self.recipe valueForKey:@"image"]];
[self.prepTimeTextField setText:[self.recipe valueForKey:@"prepTime"]];
}
}

Let’s stop here and run the app. As your app launches, tap any of the recipes and its
information should appear in the modal view.

239
Figure 19-3. Displaying the selected recipe

The app works but it is not finished yet. If you try to edit an existing recipe, the app will not
update its information properly. Go back to the AddRecipeViewController.m and modify
the save: method:
- (IBAction)save:(id)sender {
NSManagedObjectContext *context = [self managedObjectContext];

// Create a new managed object


if (self.recipe) {
[self.recipe setValue:self.nameTextField.text forKey:@"name"];
[self.recipe setValue:self.imageTextField.text forKey:@"image"];
[self.recipe setValue:self.prepTimeTextField.text forKey:@"prepTime"];

} else {
NSManagedObject *newRecipe = [NSEntityDescription
insertNewObjectForEntityForName:@"Recipe" inManagedObjectContext:context];
[newRecipe setValue:self.nameTextField.text forKey:@"name"];
[newRecipe setValue:self.imageTextField.text forKey:@"image"];

240
[newRecipe setValue:self.prepTimeTextField.text forKey:@"prepTime"];
}

NSError *error = nil;


// Save the object to persistent store
if (![context save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}

[self dismissViewControllerAnimated:YES completion:nil];


}

In the above code, we’ll update the recipe only if it is selected. If there is no selected recipe,
it is considered as a new recipe. As you can see, it is pretty straightforward to update a
managed object (i.e. the recipe). Just like creating a managed object, you simply use the
setValue: method to assign a value for a specific key. The managed object will not be saved
to the persistent store until the save: method is called.

Try to test the app again. The update feature should now work properly.

Figure 19-4. Updating an existing recipe

241
Viewing the Raw SQL Statement
One of the benefits of Core Data is that it shields developers from learning SQL. You can
interact and save data into database without writing a line of SQL. However, for those with
database background, you may want to know the exact SQLs executed behind the scene.
Sometimes, it would be very useful for debugging.

Xcode allows developers to enable SQL output for debugging purpose. To enable the option,
click “RecipeStore” (right next to the stop button) and select “Edit Scheme”.

Figure 19-5. Edit scheme in Xcode project

Select the “Arguments” tab. Under “Argument Passed on Launch” section, click the “+”
button and add the “-com.apple.CoreData.SQLDebug 1″ parameter.

Figure 19-6. Add SQL Debug Parameter

242
Click “OK” to confirm. Now run your app again and you’ll see the raw SQL statement (e.g.
SELECT and UPDATE) displayed in the output window (see figure 19-7). Here is a sample
output:
2014-01-15 14:19:56.581 RecipeStore[34035:70b] CoreData: annotation: Connecting to sqlite
database file at "/Users/simon/Library/Application Support/iPhone Simulator/7.0.3/Applications/
0949D316-2675-41FF-B1E3-5328649AFDC9/Documents/RecipeStore.sqlite"
2014-01-15 14:19:56.636 RecipeStore[34035:70b] CoreData: sql: SELECT TBL_NAME FROM SQLITE_MASTER
WHERE TBL_NAME = 'Z_METADATA'
2014-01-15 14:19:56.690 RecipeStore[34035:70b] CoreData: sql: pragma journal_mode=wal
2014-01-15 14:19:56.691 RecipeStore[34035:70b] CoreData: sql: pragma cache_size=200
2014-01-15 14:19:56.691 RecipeStore[34035:70b] CoreData: sql: SELECT Z_VERSION, Z_UUID, Z_PLIST
FROM Z_METADATA
2014-01-15 14:19:56.734 RecipeStore[34035:70b] CoreData: sql: SELECT 0, t0.Z_PK, t0.Z_OPT,
t0.ZIMAGE, t0.ZNAME, t0.ZPREPTIME FROM ZRECIPE t0
2014-01-15 14:19:56.755 RecipeStore[34035:70b] CoreData: annotation: sql connection fetch time:
0.0121s
2014-01-15 14:19:56.755 RecipeStore[34035:70b] CoreData: annotation: total fetch execution time:
0.0244s for 3 rows.
2014-01-15 14:20:12.784 RecipeStore[34035:70b] UITextField -webView called. This method is no
longer supported with the new text architecture
2014-01-15 14:20:16.697 RecipeStore[34035:70b] CoreData: sql: BEGIN EXCLUSIVE
2014-01-15 14:20:16.700 RecipeStore[34035:70b] CoreData: sql: UPDATE ZRECIPE SET ZPREPTIME = ?,
Z_OPT = ? WHERE Z_PK = ? AND Z_OPT = ?
2014-01-15 14:20:16.703 RecipeStore[34035:70b] CoreData: sql: COMMIT

Figure 19-7. SQL statement for Core Data Debugging

If you’re new to SQL and database, you may not understand the debugging information.
SQL is a language for interacting with relational database. The SELECT statement is used

243
for querying and selecting data from database. When you need to update an existing record
in database, you use the UPDATE statement.

Still confused? Don’t worry, just ignore the SQL statements. This is why iOS SDK comes
with Core Data to shield you away from the internals.

What’s Coming Next


Well, you should congratulate yourself on making an app using Core Data. At this point,
you should know how to retrieve and update data in a persistent data store. Try to apply
what we’ve learnt in your next app.

Core Data is a powerful framework for working with persistent data especially for those who
do not have any database knowledge. Our Core Data series ends here. However, what you
just learnt is the basics of Core Data. So don’t stop learning and exploring. You can further
check out Apple’s official reference to learn more about Core Data (https://
developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/
cdProgrammingGuide.html).

Enough with Core Data, right? Let’s move on and take a look at localization in the next
chapter.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/07c0na5nmget2na/RecipeStoreUpdate.zip.

244
20

LOCALIZATION
In this chapter, let’s talk about localization. The iOS devices including iPhone and iPad are
available globally. Obviously, iOS users are from different countries and speak different
languages. If you just release your app in one language, it may be ignored by user from
other parts of the world. To deliver a great user experience, you may want to make your app
available in multiple languages. The process of adapting an app to support a particular
language is usually known as localization.

Chapter image courtesy of woodleywonderworks: http://www.flickr.com/photos/wwworks/4759535950/in/photostream/


245
Xcode has the built-in support of localization. So
it’s fairly easy for developer to internationalize an
app through the localization feature and some
API calls.

Some people think of localization as the same


process of translation. That’s partially correct.
Translating static or visible text is just part of the
localization process. Localization involves other
elements such as images, graphics and sound.
You’ll also need to handle the different display
format of numeric values, as well as, date and
time.

In this chapter, we’ll show you the step-by-step


on how to localize the storyboard file, strings,
images and app name. As usual, we’ll
demonstrate the process by building a simple app
and make it available in Chinese.
Figure 20-1. Localization
Localization Overview settings in iOS
We are going to create a simple app called “Book
Store”, which will displays the details of a book with a cover image, title, author and
description. The original app is in English. We’ll localize the app together and make it
available in Chinese. At the end of the chapter, you’ll have an app that supports two
localizations: English and Chinese.

246
Figure 20-2. Demo app for localization

To make the app available in another language, we’ll demonstrate how to localize:

• the text in the storyboards


• the cover image
• the dynamic message displayed by code
• the name of the app as displayed in home screen

247
For those of you who don’t know how an app selects a localization, it refers to the language
settings of iOS (General > International > Language). An app uses this preference as the key
for accessing the localized resources for the requested language.

To keep the project as simple as possible, the app do not have any functionalities and the
book data is static.

Getting Started
It’s time to set up the project by creating a new project based on the Single View Application
template. In the project options, set the name and the class prefix as BookStore. Create and
save the project.

Figure 20-3. Creating BookStore project

Open the Storyboard, design the user interface as shown in Figure 20-4.

248
Figure 20-4. Storyboards of BookStore

You’re encouraged to build the interface from scratch. It’s not difficult to implement.
However, the focus of this tutorial is on localization. To save you time from setting up the
project, you can download this project template from https://dl.dropboxusercontent.com/
u/2857188/BookStoreProjTemplate.zip to start with. The template already pre-built the
user interface.

Localizing the Storyboard


Okay, let’s start to localize the project. In Xcode, click on BookStore in the project navigator
and select the Info tab of the project. Look for the localizations section in the Info tab. By
default, you’ll find two localizations: Base and English. And, the “Use Base Internalization”
checkbox is enabled.

249
Figure 20-5. Storyboards of BookStore

First, what the heck is base internationalization? The concept of base internationalization
was first introduced in Xcode 4.5. Before Xcode 4.5, there is no such concept of base
internationalization. Xcode replicates the whole set of storyboards for each localization.
Say, your app is localized into 5 languages. Xcode generates 5 sets of storyboards for
localization purpose. There is a major drawback for this localization process. When you
need to add a new UI element in the storyboards, you’ll need to add the same element for
each localization. That’s a tedious process. This is the reason why Apple introduced base
internationalization in Xcode 4.5.

In Xcode 5, an app has just one set of storyboards that is localized to the default language.
This storyboard is known as the base internationalization. Later when we create a
localization, Xcode automatically generates a strings file containing all the text in the base
storyboard. You’ll get a better idea of the string file when we continue to work on the
project.

Okay, let’s create the Traditional Chinese localization. Click the + button at the bottom of
the Localizations section and choose Traditional Chinese from the popup list.

250
Figure 20-6. Adding a new localization

Next, you’ll be asked to choose the resource files and language for creating the localization.
You can refer to the below screenshot for the selected options. Leave all the files checked
and click Finish.

251
Figure 20-7. Selecting files for localization

Once done, you should notice in the project navigator that there is now a triangle symbol
next to the MainStoryboard.storyboard. Expand it and you’ll find both the base storyboard
and a strings file of the storyboard for Traditional Chinese.

Figure 20-8. String file generated for Traditional Chinese

Select the Main.strings (Chinese (Traditional)) file and you should find a list of strings in
key/value pairs:

252
/* Class = "IBUINavigationItem"; title = "Book Store"; ObjectID = "48s-f4-d43"; */
"48s-f4-d43.title" = "Book Store";

/* Class = "IBUILabel"; text = "Simon Ng"; ObjectID = "drd-3m-dzh"; */


"drd-3m-dzh.text" = "Simon Ng";

/* Class = "IBUILabel"; text = "The book is designed to help you jump start the iOS game
development. Unlike other books in the market, the book will teach you how to build a complete
memory game. The book comes with full source code of a memory game for both iPhone and iPad and
we'll explain in details how the code works. Not only you can learn iOS game programming, you'll
have a real game to sell it on App Store after finishing the book. Great, right?"; ObjectID =
"ppG-7e-B24"; */
"ppG-7e-B24.text" = "The book is designed to help you jump start the iOS game development. Unlike
other books in the market, the book will teach you how to build a complete memory game. The book
comes with full source code of a memory game for both iPhone and iPad and we'll explain in
details how the code works. Not only you can learn iOS game programming, you'll have a real game
to sell it on App Store after finishing the book. Great, right?";

/* Class = "IBUIButton"; normalTitle = "Buy"; ObjectID = "x8B-oQ-A12"; */


"x8B-oQ-A12.normalTitle" = "Buy";

/* Class = "IBUILabel"; text = "Beginning iOS Game Programming"; ObjectID = "zcL-xW-VLQ"; */


"zcL-xW-VLQ.text" = "Beginning iOS Game Programming";

When we add a new localization, Xcode scans through the base storyboard, extracts those
textual items to be localized and put them into the strings file. As you can see above, the
visible strings of storyboards such as label, title of the navigation bar and button title are
put into the strings file. All entries are in key/value pairs. The first part of key is the object
ID of the UI item. You can find the object ID of the UI object under Identity Inspector. For
instance, the object ID of the Buy button is shown here:

Figure 20-9. Object ID of Buy button

With the strings file generated, your job is to translate all the values into Traditional
Chinese. The contents of the strings file for the Traditional Chinese localization will look
like this:

/* Class = "IBUINavigationItem"; title = "Book Store"; ObjectID = "48s-f4-d43"; */


"48s-f4-d43.title" = " ";

/* Class = "IBUITextView"; text = "The book is designed to help you jump start the iOS game
development. Unlike other books in the market, the book will teach you how to build a complete
memory game. The book comes with full source code of a memory game for both iPhone and iPad and

253
we'll explain in details how the code works. Not only you can learn iOS game programming, you'll
have a real game to sell it on App Store after finishing the book. Great, right?"; ObjectID =
"8Xf-VJ-vyb"; */
"8Xf-VJ-vyb.text" = " iOS
iOS
App App Store ";

/* Class = "IBUILabel"; text = "Simon Ng"; ObjectID = "drd-3m-dzh"; */


"drd-3m-dzh.text" = " ";

/* Class = "IBUIButton"; normalTitle = "Buy"; ObjectID = "x8B-oQ-A12"; */


"x8B-oQ-A12.normalTitle" = " ";

/* Class = "IBUILabel"; text = "Beginning iOS Game Programming"; ObjectID = "zcL-xW-VLQ"; */


"zcL-xW-VLQ.text" = "iOS ";

Localizing Image
It’s very easy to localize image using Xcode. As a demo, we will localize the cover image.
Select the “cover.png” in the project navigator. Next, bring up the file inspector, and locate
the localization section, in which you’ll see a “Localize…” button. Click the button and you’ll
be prompted for confirmation. Choose Base and click the “Localize” button to confirm.

Figure 20-10. Localizing Cover Image

254
After that, you’ll see the Chinese (Traditional) option in the localization section. Select the
Chinese (Traditional) localization and you’ll find two versions of cover.png in project
navigator.

Figure 20-11. Adding Traditional Chinese Localization for Image

Switch back to the finder and local the project directory. You’ll find two folders: en.lproj
and zh-Hant.lproj. Both folders are automatically generated by Xcode for localization. The
en.lproj folder stores resource files for English localization, while zh-Hant.lproj folder is for
Traditional Chinese localization.

Go to images folder. You’ll also see two localization folders: Base.proj and zh-Hant.lproj. If
you look into both folders, each one contains the cover.png file. Download the Traditional
version of the cover image from https://dl.dropboxusercontent.com/u/2857188/cover.png
(or use whatever image you like). Copy the cover image you just downloaded and replace
the one in zh-Hant.lproj folder.

Now it’s time to test out the app. If you’ve got everything right, here are what you’ll see in
English and Traditional Chinese.

255
Tip: To change the language in iPhone Simulator, click home button. Select Settings >
General > International > Language and select .

Figure 20-12. BookStore app in English and Chinese (Traditional)

256
Localizing Dynamic Strings
So far we just demonstrated the localization of static items. Commonly, you create NSString
instances dynamically or display string literals to users. These strings will also need to be
localized in a localized application.

The project template already associated the “Buy” button with the buy: method of
BookStoreViewController class. To demo how to localize dynamic text, edit the method with
the following lines of code:
- (IBAction)buy:(id)sender {
[[[UIAlertView alloc] initWithTitle:@"Confirmation"
message:NSLocalizedString(@"BOOK_PURCHASED", @"Message")
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
}

The code is simple. It just shows up an alert displaying the “Thanks for Your Purchase!”
message. But instead of using a hardcoded message, we use the NSLocalizedString macro to
get the string. The NSLocalizedString macro fetches a localized string from the
Localizable.strings file for the current localization. It takes two arguments: a key and a
comment. At runtime, the macro returns the value for the key in the localization that
corresponds to the user’s preferred language. The comment is for your own reference only.

If you run the app and tap the Buy button, you’ll only see an alert view with
“BOOK_PURCHASED” message as we haven’t created the Localizable.strings file. This is
the default file for storing localized strings that are used by code.

Now let’s create the Localizable.strings file. In the project navigator, create a new file under
the Supporting Files folder. Select the Strings file template and click the Next button.

257
Figure 20-13. Creating a strings file

When prompted, save the file as Localizable.strings. Next, we’ll use the similar procedures
to localize the file. Select the Localizable.strings and click the “Localize…” button in the File
Inspector. When the confirmation dialog appears, make sure that Base is selected and then
click the Localize button. Next, select the Chinese (Traditional) checkbox to add a
Localizable.strings file to the Traditional Chinese localization. If you’ve got everything
correct, you should have two version of Localizable.strings file.

Figure 20-14. Localizable.strings File in English and Chinese (Traditional)

Put the following message in the English version of Localizable.strings file:


"BOOK_PURCHASED" = "Thanks for the Purchase!";

Translate the message and put it in the Traditional Chinese version of Localizable.strings
file:

258
"BOOK_PURCHASED" = " ";

You’re done. Compile and run the app again. Depending on the language setting, you
should get different alert message when tapping the Buy button.

Figure 20-15. The alert message is localized

259
Localizing the App Name
In some cases, you may also want to localize the app name. When we create the localization,
Xcode already generates the Traditional Chinese version of InfoPlist.strings. You can put
key/value pairs in the InfoPlist.string file to override the values stored in Info.plist file.

So to display a different app name in the French version, simply add the following string in
the InfoPlist.string (Chinese (Traditional)) file:
"CFBundleDisplayName" = " ";

This will change the name of app as appeared on the springboard. Compile and run the app
again to test it out.

Figure 20-16. App Name in Traditional Chinese

Tip: Try to clean the build and reset the iPhone Simulator if the app name doesn’t change.

Summary
By far, you should have a basic idea of localizing an iPhone app. In this chapter, we covered
the localization process in iOS programming. You’ve learnt how to localize text and image
in storyboard using the built-in localization support of Xcode. You should also know how to
programmatically localize strings by using NSLocalizedString macro, as well as,
customizing your app name based on the language setting.

Your apps are marketed to a global audience. Users prefers to have an app interface in their
own language. By localizing your app, you’ll be able to reach more users around the world
and attract more downloads.

In the next chapter, we’ll take a look how to build a setting screen using static table view.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/c1nto0e3alv27b5/BookStore.zip.

260
21

STATIC TABLE VIEW


If you’ve studied the first few chapters, you should have a basic understanding about table
view. So far, the table views that we’ve covered are of dynamic content. However, it’s not a
must to build a dynamic table view. Sometimes, you may just want to create a static table
such as a Settings screen. Xcode provides an easy way to create static table view with
minimal code. In this chapter, we’ll show you how it can done using Storyboard.

261
To illustrate how easy you can use Storyboard to implement static table view, we’ll build a
simple Setting screen. Static table views are ideal in situations where a pre-defined number
of data items to be displayed. A setting screen is an obvious use of static table view.

Creating Xcode Project


To get started, launch Xcode and create a new project using the Single View Application
template. Name your project as “StaticTableDemo”.

Figure 21-1. Xcode Project Settings

262
Designing Static Table View
In Storyboard, first delete the default view controller and drag a Table View Controller from
the Object library.

Figure 21-2. Drag a Table View Controller to Storyboard

By default, Xcode sets the table view for dynamic content. You can easily change the table
view from dynamic to static cells by altering the content property.

263
Figure 21-3. Set the Content Property to Static Cells

264
Once changed, you’ll have a table view with three empty static cells.

Figure 21-4. Static Cells

Our Settings screen organizes various settings into group. To get the desired view of the
Settings screen, change the table style from “Plain” to “Grouped” in the Attributes inspector
of the table view.

Figure 21-5. Changing the style from Plain to Grouped

You have two ways to change the number of table row from 3 to 2. The easiest way is to
select one of the table rows and press delete button to remove it. Alternatively, select the

265
Table View Section in the document navigator and change the row number under Attribute
Inspector.

Figure 21-6. Customizing the Table View Section

You can use header and footer to display addition information for the table section. Set the
header and footer to “Notification” and “Set your notification preferences” respectively.
Once the header and footer are set, you should see the changes on-screen.

Next we’ll design the table view cell with the following steps:

• Drag a label object into the table view cell and adjust its size to 17. Set the label to “Mobile
Notification”.
• Drag another label object into the table view cell and place it under the previous label.
Adjust its size to 10 and set the label to “Do you want to receive push notification?”.
• Drag a Switch object into the table view cell.
• Repeat the above procedures to implement the “Email Notification” cell.
When complete, your notification section should look like the below screen.

Figure 21-7. Designing table view cells

266
Okay, we’ve created the first table section. To add the second section, select the Table View
in Document Outline and change the “Sections” value in Attributes inspector to 2. As soon
as you update the section number, Xcode adds a new section and the same content of the
first section is automatically copied.

Figure 21-8. Changing the section number in Attributes inspector

The rest should be very straightforward. Select the new table section. Change the number of
row to 3, as well as, its header and footer.

Figure 21-9. Changing the section number in Attributes inspector

Next, simply change the section and the table cells to make them look like the below screen.
For the switch control, you can easily change its state to OFF in the Attribute Inspector.

267
Figure 21-10. Synchronization Section

To complete the layout of the Settings screen, embed the table view controller in a
navigation controller. Select the table view controller and select Editor -> Embed in ->
Navigation Controller from the Xcode menu. Set the title of navigation bar to “Settings”.

Figure 21-11. Embed table view controller into navigation controller

Compile and Run the App


That’s it! Finally compile and run the app in Simulator. You should have a Settings screen
look like this:

268
Figure 21-13. StaticTableDemo App

Summary
In this chapter, we demonstrate how you can create a static table view using Storyboard.
While you can use dynamic table to display data from data source, static table view provides
a great way to display a finite quantity of data that are already known beforehand. Settings
screen is a common example that you can make use of the static table view.

It’s a pretty short chapter. Next up, we’ll take a look at something new and build a RSS
reader app. Sounds great? Let’s move on.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/vam12zl5osi2xo0/StaticTableDemo.zip.

269
C H A P T E R 22

BUILDING A SIMPLE RSS


READER APP
“It is always the simple that produces the
marvelous.”
~ Amelia Barr

So far everything we have worked on happened locally within an app. The data are either
stored in a file or an array. In this chapter, we are going to see how to load data from a
remote RSS feed. On top of that, you’ll learn how to parse XML using NSXMLParser class.
Of course, we’ll go through the concept by building a simple RSS reader app. Isn’t it cool?

The app that we’re going to build is very simple. It’ll load a RSS feed and display the article
items on a table view. When user taps on any of the article, it’ll navigate to load the article
in a web view. Figure 22-1 shows a sample screenshot of the RSS Reader app.

270
Figure 22-1. RSS Reader App

Creating the Xcode Project


Open Xcode and create a new Project using the Single View Application template. Name the
project as RssReader and set the class prefix to the same value. Click Next and save the
project.

271
Figure 22-2. Creating a new Xcode Project

Designing the User Interface


Once the project is created, let’s first design the user interface. Go to
MainStoryboard.storyboard and create a storyboard similar to figure 22-3.

272
Figure 22-3. RSS Reader Storyboard

I’ll not go into the details step by step as you should be able to create the interface if you
understand the materials we’ve covered in earlier chapter. In brief, here are the things you
need to set up:

• Remove the original view controller and replace it with a table view controller. Under the
Attributes Inspector of the prototype cell, set the identifier to “Cell” and change the
accessory of the table view cell to “None”. Also change the cell’s style to “Subtitle”.
• Create a new class RssReaderTableViewController and make it as a subclass of
UITableViewController. Then assign it as the custom class of the Rss Reader Table View
in storyboard.
• Embed the table view controller in a navigation controller. Change the title of navigation
bar to RSS Reader.
• Add another view controller and drag a web view into the view controller.
• Connect the prototype cell of the table view controller with the view controller using a
“Push” segue. Set the identifier of the segue as “showWebPage”.
• Create a new class named WebPageViewController and associate it with the view
controller by setting the custom class in Identity Inspector.
• Create an outlet variable named webView in WebPageViewController.h and establish a
connection with the web view

273
If you don’t want to build the app from scratch, you can also download the project template
(https://www.dropbox.com/s/a32ujz0s9xynxx1/RssReaderTemplate.zip) to start with. The
template already pre-built the storyboard and WebPageViewController class for you.

Loading and Parsing the RSS Feed


The core task of the RssReader app is load the RSS feed, which is essentially a XML
formatted plain text file, from a remote location and parse its content. In the app, we’ll use
http://images.apple.com/main/rss/hotnews/hotnews.rss as the demo feed. You’re free to
use other Rss feeds (e.g. http://feeds.feedburner.com/Mobilecrunch) if you like.

The iOS SDK provides the NSXMLParser class for loading and parsing a XML file whether
it’s stored locally or remotely. It’s pretty simple to load a remote XML file. Here are the
lines of code you need to load a Rss feed:
NSURL *url = [NSURL URLWithString:@"http://images.apple.com/main/rss/hotnews/hotnews.rss"];
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

[rssParser setDelegate:self];
[rssParser parse];

In short, you just need to call up the initWithContentsOfURL: method of the NSXMLParser
class to initialize the remote feed and the parser will handle the rest such as establishing the
network connection and downloading the XML file.

The loading of XML file is pretty easy. However, you may need to take some time to
understand how XML parsing works, especially you’re new to XML. First, let’s take a look at
the demo feed. If you point the feed in your web browser, you’ll get the content of the feed
in XML format. Depending on the web browser you’re using, you may need to use the
View Source option to view the actual file content.

Here is an excerpt of the demo feed:

<rss version="2.0">
<channel>
<title>Apple Hot News</title>
<link>http://www.apple.com/hotnews/</link>
<description>Hot News provided by Apple.</description>
<language>en-us</language>
<copyright>Copyright 2013, Apple Inc.</copyright>
<pubDate>Thu, 19 Dec 2013 12:27:30 PST</pubDate>
<lastBuildDate>Thu, 19 Dec 2013 12:27:30 PST</lastBuildDate>
<category>Apple</category>
<generator>In house</generator>
<docs>http://blogs.law.harvard.edu/tech/rss/</docs>

<item>
<title>iPad Transforms Napa Winery’s Production Process</title>

274
<link>
http://www.apple.com/ipad/life-on-ipad/the-modern-making-of-a-vintage/?
sr=hotnews.rss
</link>
<description>
At Palmaz Vineyards in Napa, California, iPad has improved the way the facility
operates, from its vineyard to its tanks. Winemaker Christian Gastón Palmaz uses
iPad to manage the vineyard and gather data that helps him make informed vine-
tending decisions. iPad also has replaced complex industrial controls inside the
winery. Palmaz uses an enterprise-level app that automates the labor-intensive,
conventional fermentation process. “iPad removes the burden of data collecting and
frees up our brilliant winemakers to focus on the art of winemaking,” says Palmaz.
</description>
<pubDate>Thu, 19 Dec 2013 10:30:19 PST</pubDate>
</item>
<item>
<title>All-New Mac Pro Available Starting Dec. 19</title>
<link>http://www.apple.com/mac-pro/?sr=hotnews.rss</link>
<description>
Apple has announced the all-new Mac Pro will be available to order starting
Thursday, December 19. The Mac Pro features the latest Intel Xeon processors, dual
workstation-class GPUs, PCIe-based flash storage, and ultra-fast ECC memory.
Designed around a unified thermal core, the Mac Pro packs unprecedented performance
into an aluminum enclosure that is 9.9 inches tall and one-eighth the volume of the
previous generation. The Mac Pro is available starting at $2,999.
</description>
<pubDate>Wed, 18 Dec 2013 10:04:55 PST</pubDate>
</item>

........

</channel>
</rss>

As said before, RSS feed is essentially XML formatted plain text. It’s human readable. Every
RSS feed should conform to a certain format. We’ll not go into the details of RSS format.
The part that we’re particularly interested are those elements within the item tag. You can
think of an item as a news article. Each item basically includes its title, description,
publishing date and link. Our job is to parse the XML file and get all the items so as to
display them in the RssReader app.

There are two general approaches to XML parsing: Tree-based and Event-driven. The
NSXMLParser class adopts the event-driven approach. In other words, it generates a
message for each type of parsing event to its delegate, which adopts the

275
NSXMLParserDelegate protocol. To better elaborate the concept, let’s consider the
following XML content:

<item>
<title>The Hour of Code Is Here</title>
<link>http://www.code.org</link>
</item>

While parsing the above XML, the NSXMLParser would inform its delegate the following
events:

Event No. Event Description Invoked method of the delegate

1 Started parsing the XML parserDidStartDocument:


document

2 Found the start tag for element parser:didStartElement:


item

3 Found the start tag for element parser:didStartElement:


title

4 Found the characters The Hours parser:foundCharacters:


of Code is Here

5 Found the end tag for element parser:didEndElement:


title

6 Found the start tag for element parser:didStartElement:


link

7 Found the characters http:// parser:foundCharacters:


www.code.org

8 Found the end tag for element parser:didEndElement:


link

9 Found the end tag for element parser:didEndElement:


item

10 Ended parsing the XML parserDidEndDocument:


document

It’s up to the delegate to decide how to handle the parsing. For example, in our case, we’ll
save the title and link for later usage.

By now, you should have a better understanding about how NSXMLParser works. Let’s get
back to code. First, create a new class named RssItem (a subclass of NSObject) with the
title, link, description and pubDate properties. The class is used to represent an item in the
RSS feed.
@interface RssItem : NSObject

@property (strong, nonatomic) NSString *title;


@property (strong, nonatomic) NSString *link;
@property (strong, nonatomic) NSString *description;

276
@property (strong, nonatomic) NSString *pubDate;

@end

Next , declare the several instance variables in the @interface declaration of


RssReaderTableViewController.m:
@interface RssReaderTableViewController () {
NSXMLParser *rssParser;
NSMutableArray *rssItems;

NSMutableString *title, *link, *description, *pubDate;


NSString *currentElement;
RssItem *currentRssItem;
}

Here we declare several variables: rssParser is the object that is for downloading and
parsing the RSS XML file; rssItems is a mutable array that contains the list of article
items; currentElement is a temporary variable for storing the current parsing element;
title, link, description and pubDate are used to temporarily save the content of title,
link, description and pubDate during the parsing. The currentRssItem is another
temporary variable for storing the current Rss item.

In the viewDidLoad: method, we’ll initialize the NSXMLParser with the feed and start the
parsing:
- (void)viewDidLoad
{
[super viewDidLoad];

rssItems = [[NSMutableArray alloc] init];


NSURL *url = [NSURL URLWithString:@"http://images.apple.com/main/rss/hotnews/hotnews.rss"];
rssParser = [[NSXMLParser alloc] initWithContentsOfURL:url];

[rssParser setDelegate:self];
[rssParser parse];
}

As the RssReaderTableViewController acts as the delegate of the NSXMLParser, remember


to implement the NSXMLParserDelegate protocol in the RssReaderTableViewController.h:
@interface RssReaderTableViewController : UITableViewController <NSXMLParserDelegate>

Next, we’ll implement the methods of NSXMLParseDelegate protocol to parse the XML
document. If you fully understand how the parsing works, you know we need to at least
implement the following methods in order to get the content we need:

• parser:didStartElement:
• parser:foundCharacters
• parser:didEndElement:

277
Add the following code in RssReaderTableViewController.m:
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:
(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict
{

currentElement = elementName;

if ([currentElement isEqualToString:@"item"]) {

RssItem *rssItem = [[RssItem alloc] init];


currentRssItem = rssItem;

title = [[NSMutableString alloc] init];


link = [[NSMutableString alloc] init];
description = [[NSMutableString alloc] init];
pubDate = [[NSMutableString alloc] init];
}

- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {

if ([currentElement isEqualToString:@"title"]) {
[title appendString:string];
} else if ([currentElement isEqualToString:@"link"]) {
[link appendString:string];
} else if ([currentElement isEqualToString:@"description"]) {
[description appendString:string];
} else if ([currentElement isEqualToString:@"pubDate"]) {
[pubDate appendString:string];
}

- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString


*)namespaceURI qualifiedName:(NSString *)qName {

if ([elementName isEqualToString:@"item"]) {

currentRssItem.title = [title
stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
currentRssItem.link = [link
stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
currentRssItem.description = [description
stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];
currentRssItem.pubDate = [pubDate
stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet];

[rssItems addObject:currentRssItem];

Whenever the start tag is found, the didStartElement: method is called. Here, we simply
initialize the variables when the current element is set to “item”.

The parser then parses the content of the element and the foundCharacter: method of the
delegate is called. In the method, depending on the current element we’re parsing, we save
the content in the temporary variables. You may wonder why we use NSMutableString to
store the string content. Ideally, you’ll get the full content of the element when the

278
foundCharacter: method is called. In reality, it may not be the case. The parser only buffers
part of the content. Every time the method is invoked, you’ll only get part of the content for
the particular element. The foundCharacter: method may be invoked multiple times before
you get the full content. For example, let’s consider the following element:

<title>The Hour of Code Is Here</title>

When the foundCharacter: method is first called, you may only have the string “The Hour
of”. In the consequent call, you may get “ Code Is Here”. Obviously, you need to concatenate
both strings. That’s why we use NSMutableString and append the strings.

When the end tag is found, the parser calls up the didEndElement: method of the delegate.
Here we save the RssItem object with the content just parsed and add it to the array.

In short, you’ll have an array of RssItem objects after parsing the RSS feed.

Displaying the RSS Items


It’s great we finish the parsing. The next thing is to put them on the table view. You should
be very familiar with the implementation. Add the following code in the
RssReaderTableViewController.m:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return rssItems.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath {

UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Cell"


forIndexPath:indexPath];

RssItem *item = [rssItems objectAtIndex:indexPath.row];


cell.textLabel.text = item.title;
cell.detailTextLabel.text = item.pubDate;

return cell;
}

Nothing new here. We simply set the cell title and subtitle to the title of the RSS item and
pubDate correspondingly.

To ensure the table data is displayed correctly, we’ll inform the table view to reload the data
when it comes to the end of the XML document. Add the following code in the
RssReaderTableViewController.m:
- (void)parserDidEndDocument:(NSXMLParser *)parser {

[self.tableView reloadData];

279
Though the app is not fully implement, you can now
compile and have a try. You should be able to display the
list of articles for the demo feed.

Displaying the Link in


Web View
Isn’t it cool? But there is still one thing left. Presently, you
can display any web page when you taps any of the articles.
We haven’t implemented the WebPageViewController yet.
Let’s finish the app quick.

First, add a property named link in the


WebPageViewController.h:
@property (strong, nonatomic) NSString *link;

The property is used for data passing from


RssReaderTableViewController. Next, add the following
Figure 22-4. RssReader App
code to load the link in the viewDidLoad: method of
WebPageViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];

NSURL *url = [NSURL URLWithString:self.link];


NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
}

The code shouldn’t be new to you if you’ve read the web view chapter. We simply create a
NSURL object with the link and load the request using UIWebView.

The final part is the data passing. We need to inform the WebPageViewController the link
to display. Apparently, we have to implement the prepareForSegue: method in the
RssReaderTableViewController.m:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([[segue identifier] isEqualToString:@"showWebPage"]) {

NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];


RssItem *item = [rssItems objectAtIndex:indexPath.row];
[[segue destinationViewController] setLink:item.link];

}
}

280
Compile and run the app again. If you make no mistakes, you should be able to load the link
of article in the RssReader app.

Figure 21-5. Web Page Loaded in the WebPageViewController

Summary
This brings us to the end of our discussion of NSXMLParser. You should now have a basic
idea of loading RSS feed from a remote URL and parsing XML content. You’ve created a
simple RssReader app to consume a RSS feed. As always, I encourage to tweak the app and
play around with other RSS feeds.

In the next chapter, you’ll learn how to build a simple Camera app using
UIImagePickerController. I believe you’ll love it.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/dugumd0w8d17ntw/RssReader.zip.

281
C H A P T E R 23

BUILDING A SIMPLE
CAMERA APP
“Look and think before opening the
shutter. The heart and mind are the true
lens of the camera.”
~ Yousuf Karsh

Previously, we covered how to use the built-in APIs to read a RSS feed and build a simple
RSS Reader app. In this chapter, we are going to learn how to use the built-in camera of the
iPhone (or the iPod or iPad, if they have one) to take a photo. Also, we’ll see how to access
the internal photo library and allow user to pick a photo. The iOS library provides the class
UIImagePickerController which is the built-in user interface for managing user interaction
with the camera or with the photo library. As usual, the UIImagePickerController requires
the use of a delegate to respond to interactions.

To help you understand the usage of UIImagePickerController, we’ll build a simple camera
app. The example application is very simple:

1. A main view displaying the selected photo from camera roll or the photo just taken
2. Two buttons: one is used to take a new photo and the other one to select a photo from the
photo library.

282
Figure 23-1. A simple camera app

Creating the Xcode Project


Launch Xcode and create a new Project using the Single View Application template. Name
the project as CameraApp and set the class prefix as Camera. Click next and save the project
in your folder.

283
Figure 23-2. Creating a new Xcode project

Designing the User Interface


Go to the Main.storyboard. Select an UIImageView from the Object Library and add it to
the view controller. Resize the UIImageView to 320x320 points. Next, drag and drop a
Button object and change its title to “Select Photo”. Finally, add another Button, place it
next to the “Select Photo” button and change its title to “Take Photo”. The resulting UI
should look something like figure 23-3.

284
Figure 23-3. The User Interface Design of CameraApp

As usual, the next step is to set up the connections. You should be very familiar with the
procedures if you follow me from the very beginning. Simply change the editor to the
Assistant Editor mode (see figure 23-4). Create the following outlets and action methods:

• An outlet variable named imageView for the image view


• An action named “selectPhoto” for the “Select Photo” button
• An action named “takePhoto” for the “Take Photo” button

285
Figure 23-4. Creating the outlet and action methods

If you did everything correctly, the CameraViewController.h should be something like the
following:
@interface CameraViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIImageView *imageView;

- (IBAction)takePhoto:(id)sender;
- (IBAction)selectPhoto:(id)sender;
@end

Implementing the CameraViewController


As said before, we need a delegate to handle the user interaction with the camera or the
photo library. In order to do that we have to implement the required methods defined in the
UIImagePickerControllerDelegate protocol.

286
Since we are going to present the camera (or the camera roll) modally, we have to
implement the UINavigationControllerDelegate protocol as well. Add both protocols to the
CameraViewController.h file:
@interface CameraViewController : UIViewController <UIImagePickerControllerDelegate,
UINavigationControllerDelegate>

Next, edit the takePhoto: acton method with the following code:
- (IBAction)takePhoto:(id)sender {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{

UIImagePickerController *picker = [[UIImagePickerController alloc] init];


picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;

[self presentViewController:picker animated:YES completion:NULL];


}
}

When the user taps the “Take Photo” button, the very first thing we have to do is to check if
the device is capable to take photo. The UIImagePickerController provides a class method
isSourceTypeAvailable: to verify if a particular media source is available. Here the media
source is set to UIImagePickerControllerSourceTypeCamera.

If it’s allowed to take photo, we create an UIImagePickerController and set its delegate to
the CameraViewController class. Also, we specify the kind of image picker we want to show.
In this case, we select to use the built-in camera (i.e.
UIImagePickerControllerSourceTypeCamera) for taking photo. Lastly, we present it
modally by using the presentViewController: method.

Next, we implement the selectPhoto: action method. The code is very similar to the one just
added. However, instead of using the built-in camera as a source type, we set the source to
camera roll album (i.e. UIImagePickerControllerSourceTypeSavedPhotosAlbum).
- (IBAction)selectPhoto:(id)sender {
if ([UIImagePickerController
isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {

UIImagePickerController *picker = [[UIImagePickerController alloc] init];


picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

[self presentViewController:picker animated:YES completion:NULL];


}
}

If you compile and run the app now, it will only work partially. You can bring the camera
roll album when tapping the Select Photo button. However, if you pick a photo in the

287
album, the photo won’t be shown in the image view of the app. That’s what we’ll implement
next.

Implementing the Delegate Methods of


UIImagePickerController
When the user takes a photo with the camera or pick a photo from camera roll, the
didFinishPickingMediaWithInfo: method is called. The second parameter (i.e. info) is a
NSDictionary object which contains, among other things, the original image and the edited
image which can be accessible through the tag UIImagePickerControllerEditedImage. We
use the edited image instead of the original one as the app allows user to edit the photo.
Alright, let’s add the following code in the CameraViewController.m:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:
(NSDictionary *)info {

UIImage *chosenImage = info[UIImagePickerControllerEditedImage];


self.imageView.image = chosenImage;

[picker dismissViewControllerAnimated:YES completion:NULL];

It might happen that the user cancels the operation by tapping the “Cancel” button of the
image picker view. In that case the picker will call the imagePickerControllerDidCancel:
method. The implementation of this method is very simple as what we have to do is just
remove the picker controller. Here is the final code you need to add:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

[picker dismissViewControllerAnimated:YES completion:NULL];

Test the App Using a Real Device


Cool! Let’s test the app. You should be able to run the app via iPhone Simulator. However,
you can only test the “Select Photo” feature. The Simulator doesn’t come with camera
support. In order to test photo taking, you must load the app and test it on a physical
iPhone (or other iOS devices). If everything is smooth, you should end up with a simple
camera app that lets you take photo and access the Camera Roll album.

288
Figure 23-5. Camera App

What’s Coming Next?


The iOS SDK makes it so simple for developer to access the built-in camera and photo
library. In this chapter, you’ve learnt how to use the UIImagePickerController class to take
photo and access the internal camera roll. The sample camera app is not perfect but it gives
you an idea how the UIImagePickerController works.

In the next chapter, we’ll see how to record and play video.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/09edb5gdnv037vb/CameraApp.zip.

289
C H A P T E R 24

VIDEO RECORDING AND


PLAYBACK
“Brick walls are there for a reason. The
brick walls aren't there to keep us out.
The brick walls are there to show us how
badly we want things.”
~ Randy Pausch

Previously, we covered how to create a simple camera app. In this chapter, we’re going to
build a similar app but for video recording and playback.

The iOS API for recording and playing videos can be a little bit confusing for newcomers, as
there are several options available. If you just want to play a video, you can use the
MediaPlayer framework, which allows us to play a video stored locally in our device, or
from a remote location. However, if you need advanced features such as media asset
management, media editing, track management, and others, you have to use the
AVFoundation framework. We’ll keep thing simple and start off by covering the
MediaPlayer framework.

The MediaPlayer framework brings us two main classes to display videos or movies. If you
want to display a video immediately and inline (say, a subview smaller than the full screen),
you can use the MPMoviePlayerController (http://developer.apple.com/library/ios/
#documentation/MediaPlayer/Reference/MPMoviePlayerController_Class/Reference/
Reference.html). By using MPMoviePlayerController, playback occurs in a view owned by
the movie player. You can incorporate a movie player’s view into a view owned by your app.

290
On the contrary, if you want to play a full screen video, for example by presenting the video
modally, you should use the MPMoviePlayerViewController class (http://
developer.apple.com/library/ios/#documentation/mediaplayer/reference/
mpmovieplayerviewcontroller_class/Reference/Reference.html). The
MPMoviePlayerViewController class is designed to present a simple view controller for
displaying full-screen movies.

In this chapter, we will focus on the MPMoviePlayerController. If you grasp the basics,
however, you should have no problem utilizing the MPMoviePlayerViewController class.

Demo App Overview


Like any chapter of this book, we’ll build a simple demo
app to walk you through the concept. The demo app (see
Figure 24-1) is very simple without fancy user interface.
Once opened, the app displays a screen with a single
“Capture” button. When you tap the button, it’ll bring up
the video camera for video recording. Once finished the
recording, the video is automatically shown in the main
screen. Users are allowed to play back the video inline.
Pretty simple, right?

Figure 24-1. Simple Video App

291
Creating Xcode Project
Open Xcode and create a new Project, choose the Single View Application template. Name
the project as VideoApp and set the class prefix as Video. Click next to create and save the
project.

Figure 24-2. Creating the VideoApp Project

Designing User Interface


Next, let’s design the user interface of the app. Go to the Main.storyboard. Add a button and
place it at the bottom of the screen centered. Change its title to “Capture”. Your design
should look similar to Figure 24-3.

292
Figure 24-3. Adding a Capture Button

Again, the next thing to do is to establish a connection between the Capture button and
code. In order to do that, switch to the Assistant Editor mode. Create an action method in
the VideoViewController.h for the Capture button. Name the method as “captureVideo”.

293
Figure 24-4. Associate the button with code

Implementing the VideoViewController


Open the VideoViewController.h file and add the following code to include the necessary
header files:
#import <MediaPlayer/MediaPlayer.h>
#import <MobileCoreServices/MobileCoreServices.h>

In the previous chapter, we use the UIImagePickerController class to take static photo. The
same class is also capable to capture video. If you’re not forgetful, you should still
remember how to use UIImagePickerController. Here, the VideoViewController has to
implement both the UIImagePickerControllerDelegate and
UINavigationControllerDelegate protocols.

294
On top of that, we’ll add two properties (videoURL and videoController) in the
VideoViewController.h. The videoURL property stores the URL of the current video, while
the videoController property is dedicated for video play back.

If you did everything correctly, your VideoViewController.h file should be something like
below:
#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <MobileCoreServices/MobileCoreServices.h>

@interface VideoViewController : UIViewController <UIImagePickerControllerDelegate,


UINavigationControllerDelegate>

@property (strong, nonatomic) NSURL *videoURL;


@property (strong, nonatomic) MPMoviePlayerController *videoController;

- (IBAction)captureVideo:(id)sender;

@end

Implementing Picker Controller for Video Recording


When the user taps the “Capture” button, the captureVideo: method is called. This method
is responsible to create, configure and display the image picker for video recording. We’ll
implement the method using the below code:
- (IBAction)captureVideo:(id)sender {
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])
{

UIImagePickerController *picker = [[UIImagePickerController alloc] init];


picker.delegate = self;
picker.allowsEditing = YES;
picker.sourceType = UIImagePickerControllerSourceTypeCamera;
picker.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeMovie, nil];

[self presentViewController:picker animated:YES completion:NULL];


}
}

If you’ve read the previous chapter, you should have a basic idea of
UIImagePickerController. The above code shouldn’t be new to you. It’s almost the same as
the one we use for taking photo, except that we set the picker’s media types to
kUTTypeMovie (the line of code highlighted in bold). Depending on the media types, the
picker displays different interface for photos or videos. By default the media type is set to
kUTTypeImage, which designates the photo camera interface. As we need the picker to
launch the video capture interface, we set the media type to kUTTypeMovie.

295
Implementing Video Playback
Once user finishes recording and confirm to save the video, the app will automatically play
back the video in the main screen. In order to implement the video playback, there are a few
things to be done:

1. Get the system URL of the video just captured


2. Remove the UImagePickerController
3. Play the video by using the MPMoviePlayerController class, which is a built-in class for
the playback of a video from a file (or a network stream)

As you know, the didFinishPickingMediaWithInfo: method will be called when user


confirms to use the video. The file URL of the video is bundled in the info parameter. So in
this method, we first get the URL of the video (please note that this video is not saved in the
photo library unless we do it explicitly). Secondly, we dismiss the picker. Lastly, we
instantiate the MPMoviePlayerController class and pass it with the video URL for playback.
We also change the size of the view to leave some free space for the “Capture” button. And
finally, we present the view and play the video.
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:
(NSDictionary *)info {

self.videoURL = info[UIImagePickerControllerMediaURL];
[picker dismissViewControllerAnimated:YES completion:NULL];

self.videoController = [[MPMoviePlayerController alloc] init];

[self.videoController setContentURL:self.videoURL];
[self.videoController.view setFrame:CGRectMake (0, 0, 320, 460)];
[self.view addSubview:self.videoController.view];

[self.videoController play];

There is still one thing left. We should implement the imagePickerControllerDidCancel: method. We
mentioned this method in the previous chapter. The method is invoked when user cancels the video
recording. In this case, we simply dismiss the picker.
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {

[picker dismissViewControllerAnimated:YES completion:NULL];

296
Compile and Test the App
OK, it is time to compile and test our application. Like the Camera app we developed in the
previous chapter, you can’t test it using the built-in iPhone Simulator. You have to use a real
device for the testing as the iPhone simulator doesn’t have a camera.

If you compile and run the app on your iPhone, you should be able to bring up the video
camera interface after tapping the “Capture” button. Once capturing the video, it’s
automatically played back in the main screen.

Using the Movie Player Notifications


A nice feature of the MPMoviePlayerController is that it has a collection of notifications that
we can use to control the video playback. For example, when the video has finished playing,
the MPMoviePlayerPlaybackDidFinishNotification will be sent. Add the following code to
the didFinishPickingMediaWithInfo: method, just before the [self.videoController play]
statement:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(videoPlayBackDidFinish:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:self.videoController];

You may be new to NSNotificationCenter. Simply think of it as a centralized hub of


notifications within an app. Through it, any part of your app can notify or be notified by
other parts of the app. In the above code, it tells the NSNotificationCenter to listen for the
MPMoviePlayerPlaybackDidFinishNotification and calls up the videoPlayBackDidFinish:
method accordingly.

In this case, the videoPlayBackDidFinish: method will stop the video, remove its view and
display an alert message. Of course, we’ll remove the notification. Otherwise, it will be
called twice! Add the following code in the VideoAppController.m:
- (void)videoPlayBackDidFinish:(NSNotification *)notification {

[[NSNotificationCenter defaultCenter]removeObserver:self
name:MPMoviePlayerPlaybackDidFinishNotification object:nil];

// Stop the video player and remove it from view


[self.videoController stop];
[self.videoController.view removeFromSuperview];
self.videoController = nil;

// Display a message
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Video Playback" message:@"Just finished the
video playback. The video is now removed." delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];

297
}

Now compile and test the app again. Try to record a video. The app will automatically play
back the video but it’s removed the player from the view once the play back completes.

Figure 24-5. VideoApp Demo

Summary
By now, you should have a better idea about implementing video recording and playback in
iOS. The UIImagePickerController makes it simple to bundle video capturing feature in
your app. With a few lines of code, you can bring up the built-in video camera and capture
video. On the other hand, the MediaPlayer framework provides handy facilities for video or
movie playback. The use of framework is not limited to video playback. It allows developers

298
to access the iPod library, play music, podcast and audio book files. So don’t stop here,
check out Apple’s official reference (https://developer.apple.com/library/ios/
documentation/MediaPlayer/Reference/MediaPlayer_Framework/_index.html#//
apple_ref/doc/uid/TP40006952) to learn more about the framework.

In the next chapter, we’ll move onto local notification. You’ll learn how to build a To-Do
app. It’ll definitely be another fun project.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/95uqqcz01fzjnie/VideoApp.zip.

299
25

LOCAL NOTIFICATION
Before we dive into the local notification tutorial, let’s first talk about the history.

Way back in iOS 3.0, Apple introduced the Push Notification Service (APNS) to bring the
multitasking support to its mobile operating system. At that time, due to the nature of iOS,
only one application is allowed to run in the foreground. Push notification changes the
game by allowing applications to receive updates even it’s not actively running. When a
notification comes in, iOS displays the notification in the form of on-screen alerts or sounds
and it’s up to the user to decide whether to launch the application.

300
Push notification provides an effective way to support multitasking. The catch is it only
works if the device is connected to the Internet. And I forgot to mention you have to
develop some server programs to interact with APNS. Considered you’re developing a To-
Do app, the app notifies users about a to-do item at a specific time. In order to push out
such notification, you have to build a server program to talk with APNS and host it
somewhere. This is too complicated for most developers particularly for those without
server side programming experience.

Since the release of iOS 4, Apple introduced a new type of notification called Local
Notification. This makes pushing a notification much simpler. No more server side
programming. No more Internet connection is required. You can schedule a notification
with simple API and it’ll be fired up at a proper time.

Okay, that’s enough for the history and background. Let’s see how to implement Local
Notification and build a simple To-Do app as demo.

The To-Do App with Notifications


To demonstrate the usage of local notification, we’ll build a simple To-Do app together. The
app lets users put in any to-do items and preset a reminder. At a specific time, the app fires
up a notification and reminds users about the to-do item.

Figure 25-1 Local Notification Demo App

301
Creating Xcode Project and Design the UI
Okay, let’s begin. First, launch Xcode and create a new project using the Single View
Template. Name the project as ToDoApp (or whatever you like). In the Storyboard, design
the user interface similar to the below:

Figure 25-2. Local Notification Demo – Storyboard

The focus of this tutorial is on the implementation of local notifications. So to save your
time from setting up the project and user interface, you can download the
LocalNotificationTemplate.zip from https://dl.dropboxusercontent.com/u/2857188/
LocalNotificationTemplate.zip to start with.

Local Notification at a Glance


In general, to setup a location notification, all you need is just a few lines of code:
UILocalNotification* localNotification = [[UILocalNotificationalloc] init];
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:60];
localNotification.alertBody = @"Your alert message";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

When you create a local notification, you must specify when the system should deliver the
notification. That is the fireDate property. Optionally, you can set the time zone. The
notification can be displayed as an alert message which is assigned by using the “alertBody”

302
property. Once you configure the instance of UILocalNotification, you schedule it by using
the scheduleLocalNotification: of UIApplicationsharedApplication class.

Let’s go back to the demo app implementation. We’ll first implement the
“AddToDoViewController.m” to schedule a notification. The “Add To-Do Item” view allows
user to add a to-do item and set the time of the reminder using a data picker. When the
“save” button is tapped, the “save:” method of AddToDoViewController will be invoked.
Add the following code in the “save:” method:
[self.itemText resignFirstResponder];

// Get the current date


NSDate *pickerDate = [self.datePicker date];

// Schedule the notification


UILocalNotification* localNotification = [[UILocalNotification alloc] init];
localNotification.fireDate = pickerDate;
localNotification.alertBody = self.itemText.text;
localNotification.alertAction = @"Show me the item";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication]
applicationIconBadgeNumber] + 1;

[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];

// Request to reload table view data


[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadData" object:self];

// Dismiss the view controller


[self dismissViewControllerAnimated:YES completion:nil];

The above code is very straightforward. We first hide the on-screen keyboard and get the
preset date from the date picker. Next, we create the local notification with the date we just
retrieve from the date picker. We also set the alert body by using the to-do item text. Lastly,
we increase the existing icon badge number by 1. With everything configured, we fire up
“scheduleLocalNotification:” method to schedule the local notification.

Before we end the method, we notify the ToDoListViewController to refresh the table data.

303
Displaying a List of Local Notification
In the main screen of the app, we display a list of local notifications that are scheduled in
the table view. It’s pretty easy to retrieve the current scheduled local notification. Simply
make the following call and you’ll get an array of local notifications:
[[UIApplication sharedApplication] scheduledLocalNotifications];

For the demo To-do app, select the “ToDoListViewController.m” and replace the
numberOfRowsInSection: and cellForRowAtIndexPath: methods of the
UITableViewDataSource protocol with the following code:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [[[UIApplication sharedApplication] scheduledLocalNotifications] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

// Get list of local notifications


NSArray *localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
UILocalNotification *localNotification = [localNotifications objectAtIndex:indexPath.row];

// Display notification info


[cell.textLabel setText:localNotification.alertBody];
[cell.detailTextLabel setText:[localNotification.fireDate description]];

return cell;
}

I’ll not go into the details of the code line by line. You should be very familiar with them if
you have go over the UITableView chapter.

The above simply uses the method call:

[[UIApplication sharedApplication] scheduledLocalNotifications];

to retrieve a list of notifications and display its alertBody, as well as, fire date in the table
list.

304
Okay, let’s compile and run
the app. Tap the “+” button in
the navigation bar and add a
to-do item. Set the date to a
future date (say, 5 minutes
later). Once done, tap “save”
button to schedule the
notification. As soon as you go
back to the table view, you’ll
find the notification just
added. Go back to home
screen. Wait for a couple of
minutes (depending on your
preset schedule), you should
see a notification banner. Or if
you’re in lock screen, you’ll see
a notification alert.

Handling
Notifications
So far we just create and
schedule the notification. But
how can we handle the
notification when it’s fired up?

When a notification is fired


up, your app may be either
running in the foreground or
background. In the worst case, Figure 25-3. Local Notification Demo
the app is not running at all. You’ll have to handle these situations and let’s talk about them
one by one.

Application is NOT Running


When the app is not running, users see notifications in the following ways, depending on
the notification settings:

• Displaying an alert or banner


• Badging the app icon

305
• Playing a sound
By tapping on action button of the notification, users will launch the app. In this case, the
application:didFinishLaunchingWithOptions: method of the application delegate is called.

Change the method to the following code:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{
// Override point for customization after application launch.

// Handle launching from a notification


UILocalNotification *locationNotification = [launchOptions
objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
// Set icon badge number to zero
application.applicationIconBadgeNumber = 0;
}

return YES;
}

In the above code, we use the launchOptions dictionary and see if it contains a local
notification object. For the sake of simplicity, we just reset the icon badge number when
there is a local notification.

Application is Running in Foreground


If the app is running while the notification is delivered, there is no alert displayed on
screen. The application automatically calls its delegate’s
application:didReceiveLocalNotification: method.

In the AppDelegate.m, add the following method:


- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification
*)notification
{
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Reminder"
message:notification.alertBody
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}

306
// Request to reload table view data
[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadData" object:self];

// Set icon badge number to zero


application.applicationIconBadgeNumber = 0;
}

As we’ll discuss in the following section, the “didReceiveLocalNotification:” method will


also be called when application is running in background. Here we first determine the
application state and only display an alert when application is running foreground. We also
inform the view controller to reload the table date and reset the application’s icon badge
number.

Figure 25-4. Display an alert while application is running

307
Application is Running in Background
The app has been launched before but users switch to another app. When the notification is
fired, users normally see an alert banner at the top of the screen. When it’s tapped, the app
will be brought to the foreground. Similar to the case that the app is running in foreground,
the application automatically calls its delegate’s application:didReceiveLocalNotification:
method.

Figure 25-5. Display a location notification as banner

Summary
In this chapter, we give you a basic idea about local notifications. It’s much simpler than
remote notification. If you are developing an app, try to add some local notification
features. One common application of local notification is to remind users to check out your
app. Say, your app can display a local notification if users haven’t launched it for more 3
days. It is one of the techniques commonly used in most of the apps.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/xdv1q3d41r9w8cx/LocalNotificationDemo.zip.

308
C H A P T E R 26

INTRODUCTION TO
UIPAGEVIEWCONTROLLER
“People are always looking for the single
magic bullet that will totally change
everything. There is no single magic
bullet.”
~ Temple Grandin

For the very first time you launch an app, you probably find a series of walkthrough (or
tutorial) screens to give you a brief introduction of the features. It's a common practice to
explain how the app works. In this chapter, we'll show you how to build a similar type of
walk through screens by using UIPageViewController.

The UIPageViewController class was first introduced in iOS 5 SDK that lets developers
build pages of content, where each page is managed by its own view controller. The class
was further improved in iOS 6 to support the scrolling transition. With page view, users can
easily navigate between multiple pages through simple gesture. The page view controller is
not limited to create walkthrough screens. You can find examples of page view
implementation in games like Angry Birds to show the available levels or book apps to
display pages of content.

309
Figure 26-1. Sample Walkthrough Screens from UltraVisual

The UIPageViewController is a highly configurable class. You're allowed to define:

1. the orientation of the page views – vertical or horizontal


2. the transition style – page curl transition style or scrolling transition style
3. the location of the spine - only applicable to page curl transition style
4. the space between pages - only applicable to scrolling transition style to define the inter-
page spacing

We'll create a simple app together in order to demonstrate how UIPageViewController


works. However, we'll not demonstrate every option of UIPageViewController. We'll just
use the scrolling transition style to display a series of walkthrough screens. Don't worry.
With the basic understanding of the UIPageViewController, I believe you should be able to
explore other features in the page view controller.

Let's get started.

A Glance at the Demo App


The demo app we are going to create is very simple. It displays 4 pages of screens to give
users a brief introduction to the user interface. User can navigate between pages by swiping
through the screen. Whenever user taps the "Start again" button to go back to the first page
of tutorial. This type of walkthrough/tutorial screens shouldn't be new to you as they are
commonly found in apps such as Snapguide and Airbnb.

310
Figure 26-2. Walkthrough Screens of our Demo App

Creating the Project


Launch Xcode and create a new Project by using the Single View Application template. It
might seem a little bit strange to select the Single View Application template as Xcode
already comes with a Page-Based Application template, which contains a fully functional
app based on the UIPageViewController. However, this template is a little bit complex and
it will take us more time to clean-up the code of the template than to start from scratch.
Needless to say, we can better grasp the concept behind the UIPageViewController when we
start from scratch.

311
Figure 26-3. Creating a new project using Single View Application template

Okay, let’s move on. In the next screen enter PageViewDemo as the product name and set
com.appcoda in the company identifier field. Select iPhone for the Devices option. Press
next and create the project.

312
Figure 26-4. Creating a new project using Single View Application template

Creating Page View Controller in Storyboard


Next, select the Main.storyboard. As usual, you should find a default view controller
generated by Xcode. Leave it as it is. Drag a Page View Controller from the Object Library
into the storyboard. Then add another View Controller and put it in the same storyboard.

313
Figure 26-5. Adding Page View Controller in Storyboard

For this project, the original view controller is used as the root view controller for holding
the page view controller. The view controller you’ve just added will be used for displaying
the page content. Throughout the chapter, we refer the original view controller as the root
view controller and the other view controller as page content controller.

314
You may wonder why we just add a single view controller for 4 pages of content. Shouldn’t
we use four view controllers instead of one? As you can see from the final deliverable, the
walkthrough screens are very similar. It’s better to share the same view controller for
different screens.

Next, assign a Storyboard ID for the page view controller and the page content controller.
You can simply select the controller and set the ID under Identity Inspector. Set the
Storyboard ID of the page view controller as “PageViewController” and name the ID of the
page content controller as “PageContentController”. Later we’ll refer to these IDs in our
code.

Figure 26-6. Assigning Storyboard IDs

By default, the transition style of the page view controller is set as Page Curl. The page curl
style is perfect for book apps. For walkthrough screens, we prefer to use scrolling style. So
change the transition style to Scroll under Attribute Inspector.

315
Figure 26-7. Changing Transition Style

We’ll design the user interface of page content view controller. Drag an image view and a
label into the controller. You’re free to change the font type and size. But your view
controller should be similar to the screenshot shown in figure 26-8.

316
Figure 26-8. Page Content View Design

For the default view controller, add a “Start again” button and put it at the below of the
screen.

Figure 26-9. Adding a button

317
Creating View Controller Class
The next step is to create view controller class and associate it with the corresponding view
controller. From the menu, select File -> New -> File … and choose the “Objective-C class”
template. Name the class as PageContentViewController and make it a subclass of
UIViewController.

Figure 26-10. Creating view controller

Go back to Storyboard. Select the page content view controller and set the custom class to
PageContentViewController under Identify Inspector.

Figure 26-11. Setting the custom class

318
Next, we’ll create outlets for the image view and label. Switch to the Assistant Editor and
make sure the PageContentViewController.h is opened. Control and drag from the image
view to the PageContentViewController.h and create an IBOutlet. Set the name as
backgroundImageView for the image view. For the label, set the name of the outlet as
titleLabel.

Figure 26-12. Creating outlet for Page Content View Controller

After the change, the PageContentViewController.h should look like this:


#import <UIKit/UIKit.h>

@interface PageContentViewController : UIViewController


@property (weak, nonatomic) IBOutlet UIImageView *backgroundImageView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;

@end

Next, select the root view controller and make sure the ViewController.h is opened. Create
an action for the “Start again” button and name the action as “startWalkthrough”.

319
Figure 26-13. Creating outlet for Root View Controller

Okay, we’ve completed the design of user interface and created all the outlets. Let’s move
onto the implementation of the view controller classes.

Implementing the Page Content View Controller


It’s very straightforward to implement the page content view controller. First, add the
following properties in PageContentViewController.h:
@property NSUInteger pageIndex;
@property NSString *titleText;
@property NSString *imageFile;

The pageIndex stores the current page index (or page number). The view controller is
designed to display an image and a title. So we create two parameters for passing the title
text and image file. Next, open PageContentViewController.m and change the
viewDidLoad: method:

320
- (void)viewDidLoad
{
[super viewDidLoad];

self.backgroundImageView.image = [UIImage imageNamed:self.imageFile];


self.titleLabel.text = self.titleText;

Implementing the Page View Controller


The UIPageViewController class is classified as a container controller. The container
controller is used to contain and manage multiple view controllers shown in the app, as well
as, controlling the way one view controller switches to another. Here the
UIPageViewController is the container controller that lets the user navigate from page to
page, where each page is managed by its own view controller object. The following
illustration depicts the relationship between the page view controller and the page content
view controller.

Figure 26-14. Relationship between Page View Controller and Page Content View Controller

321
In order to make UIPageViewController work, we must adopt the
UIPageViewControllerDataSource protocol. The data source for a page view controller is
responsible for providing the content view controllers on demand. By implementing the
data source protocol, we tell the page view controller what to display for each page.

In this case, we use the ViewController class as the data source for the
UIPageViewController instance. Therefore it is necessary to declare the ViewController
class as implementing the UIPageViewControllerDataSource protocol.

The ViewController class is also responsible to provide the data of the page content (i.e.
images and titles). Open the ViewController.h and import the
PageContentViewController.h. Modify the @interface declaration, add a new property to
hold the UIPageViewController, as well as, properties for both images and titles:
#import <UIKit/UIKit.h>
#import "PageContentViewController.h"

@interface ViewController : UIViewController <UIPageViewControllerDataSource>

- (IBAction)startWalkthrough:(id)sender;
@property (strong, nonatomic) UIPageViewController *pageViewController;
@property (strong, nonatomic) NSArray *pageTitles;
@property (strong, nonatomic) NSArray *pageImages;

@end

In the ViewController.m, initialize the pageTitles and pageImages in the viewDidLoad:


method:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
_pageTitles = @[@"Over 200 Tips and Tricks", @"Discover Hidden Features", @"Bookmark Favorite
Tip", @"Free Regular Update"];
_pageImages = @[@"page1.png", @"page2.png", @"page3.png", @"page4.png"];
}

Note: You can download image files from https://www.dropbox.com/s/70x8ut4jkbd0ktk/


pageimages.zip and add them into the Xcode project.

We have created the data model for the page content. Next, we have to implement at least
two methods of the UIPageViewControllerDataSource protocol:

• viewControllerAfterViewController – provides the view controller after the current view


controller. In other words, we tell the app what to display for the next screen.
• viewControllerBeforeViewController – provides the view controller before the current
view controller. In other words, we tell the app what to display when user switches back to
the previous screen.

322
Add the following lines of code before the end of the ViewController.m file:
#pragma mark - Page View Controller Data Source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController


viewControllerBeforeViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

if ((index == 0) || (index == NSNotFound)) {


return nil;
}

index--;
return [self viewControllerAtIndex:index];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController


viewControllerAfterViewController:(UIViewController *)viewController
{
NSUInteger index = ((PageContentViewController*) viewController).pageIndex;

if (index == NSNotFound) {
return nil;
}

index++;
if (index == [self.pageTitles count]) {
return nil;
}
return [self viewControllerAtIndex:index];
}

The above methods are very straightforward. First, we get the current page index.
Depending the method, we simply increase/decrease the index number and return the view
controller to display. Of course, we have to verify if we have reached the boundaries of the
pages and return nil in that case.

As you may notice, we haven’t created the viewControllerAtIndex: method. It is a helper


method that is designed to create the page content view controller on demand. It takes in
the index parameter and creates the corresponding page content controller.

In the ViewController.m, add the helper method:


- (PageContentViewController *)viewControllerAtIndex:(NSUInteger)index
{
if (([self.pageTitles count] == 0) || (index >= [self.pageTitles count])) {
return nil;
}

// Create a new view controller and pass suitable data.


PageContentViewController *pageContentViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@"PageContentViewController"];
pageContentViewController.imageFile = self.pageImages[index];
pageContentViewController.titleText = self.pageTitles[index];
pageContentViewController.pageIndex = index;

return pageContentViewController;

323
}

Recalled that we have set a storyboard ID for the view controllers when designing the user
interface. The ID is used as reference for creating the view controller instance. To
instantiate a view controller in storyboard, you can use the
instantiateViewControllerWithIdentifier: method with a specific storyboard ID.
PageContentViewController *pageContentViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@"PageContentViewController"];

To display a page indicator, you have to tell iOS the number of pages (i.e. dots) to display in
the page view controller and which page must be selected at the beginning. Add the
following two methods at the end of the ViewController.m file:
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return [self.pageTitles count];
}

- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}

Again the above code is very straightforward. We simply tell iOS that we have the total
number of pages to display in the page view controller and the first page should be selected
by default.

Please note that you must implement both methods in order to display the page indicator.
Also the page indicator only works in scroll transition mode.

Initializing the UIPageViewController


The final step is to create and initialize the UIPageViewController. The best place to do that
is in the viewDidLoad method. Open the ViewController.m file and change the method to:
- (void)viewDidLoad
{
[super viewDidLoad];
// Create the data model
_pageTitles = @[@"Over 200 Tips and Tricks", @"Discover Hidden Features", @"Bookmark Favorite
Tip", @"Free Regular Update"];
_pageImages = @[@"page1.png", @"page2.png", @"page3.png", @"page4.png"];

// Create page view controller


self.pageViewController = [self.storyboard
instantiateViewControllerWithIdentifier:@"PageViewController"];
self.pageViewController.dataSource = self;

PageContentViewController *startingViewController = [self viewControllerAtIndex:0];


NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];

// Change the size of page view controller

324
self.pageViewController.view.frame = CGRectMake(0, 0, self.view.frame.size.width,
self.view.frame.size.height - 30);

[self addChildViewController:_pageViewController];
[self.view addSubview:_pageViewController.view];
[self.pageViewController didMoveToParentViewController:self];

Let’s see what the method does. We first create the PageViewController instance. Next we
specify the data source, in this case it is the class itself. We then create the first page
content controller, add it to an array of controllers and assign it to the page view controller
for display.

Lastly, we change the size of the page view controller and add the page controller view to
the current view.

Customize the Page Indicator


If you compile and run the app now, your app should run properly but you may find the
page indicator missing. Actually the page indicator is there but the color of the dots is the
same as the color of the view. So let’s change its color.

In the AppDelegate.m, add the following lines of code in the


didFinishLaunchingWithOptions: method:
UIPageControl *pageControl = [UIPageControl appearance];
pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
pageControl.currentPageIndicatorTintColor = [UIColor blackColor];
pageControl.backgroundColor = [UIColor whiteColor];

Compile and Run the App


And there we go, start the application and see how the UIPageViewController works. You
should be able to load the page view controller by using the iPhone Simulator. Try to swipe
through the screen to navigate between pages.

325
Figure 26-15. PageView Demo App

Back to the First Page


There is still one thing left. The “Start again” is not yet implemented. When tapped, we
expect the page view controller will scroll back to the first page. You can use the
setViewControllers: method of the UIPageViewController to switch page. To go back to the
first page of the page view controller, change the startWalkthrough: method of
ViewController.m:
- (IBAction)startWalkthrough:(id)sender {
PageContentViewController *startingViewController = [self viewControllerAtIndex:0];
NSArray *viewControllers = @[startingViewController];
[self.pageViewController setViewControllers:viewControllers
direction:UIPageViewControllerNavigationDirectionReverse animated:NO completion:nil];
}

Run the app again. The app will bring you back to the first page when tapping the “Start
again” button.

Summary
In this chapter, we cover the basics of UIPageViewController and demonstrate how to
implement the controller by using Storyboard. The UIPageViewController is a very handy
class for implementing walkthrough screens in your app. That said, the usage of
UIPageViewController is unlimited. You can use it to display whatever information as you
like such as pages of web view. The UIPageViewController is highly configurable. This
chapter only covers the scroll transition style. But don’t you know that you can easily use
the class to build a simple book app? Simply change the transition style from scroll to page

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/p0zvfd1lyfcqynv/PageViewDemo.zip.

326
curl and see what you’ll get. So don’t stop here. Try to change the available options and
learn about UIPageViewController.

327
27

SLIDE-OUT SIDEBAR
MENU
If you read this book from the very beginning and understand the materials thoroughly,
you’re now ready to move onto something more advanced. In this chapter, we’ll show you
how create a slide-out navigation menu similar to the one you find in the Facebook app. If
you’re unfamiliar with slide out navigation menu, take a look at figure 27-1. And Ken
Yarmost (http://kenyarmosh.com/ios-pattern-slide-out-navigation/) gave a good
explanation and defined it as follows:

328
Slide-out navigation consists of a panel that “slides out” from underneath the left or
the right of the main content area, revealing a vertically independent scroll view that
serves as the primary navigation for the application.
Ken Yarmost

Since Facebook app introduced the slide-out sidebar design, it quickly becomes a standard
way to implement navigation menu. Nowadays, you can easily find this design pattern in
most of the popular content-related apps such as Foursquare, Mailbox, Gmail, Bloomberg
Businessweek, etc.

Figure 27-1. Sample slide-out sidebar menu from Foursquare, Digg, Facebook and Gmail (left to right)

The slide-out design pattern lets you build a navigation menu in your apps but without
wasting the screen real estate. Normally, the navigation menu is hidden behind the front
view. The menu can then be triggered by tapping a list button in the navigation bar. Once
the menu is expanded and becomes visible, users can close it by using the list button or
simply swiping left on the content area.

With so many free pre-built solution on GitHub, we’re not going to build the slide-out
navigation menu from scratch. Instead, we’ll make use of a library called
SWRevealViewController (https://github.com/John-Lluch/SWRevealViewController).
Developed by John Lluch, this excellent library provides a quick and easy way to put up a
slide-out navigation menu and it’s available for free.

329
A Glance at the Demo App
As usual, we’ll build a demo app to show you how to apply the SWRevealViewController.
The app is very simple but not fully functional. The primary purpose of the app is to walk
you through the implementation of slide-out navigation menu. The navigation menu will
work like this:

• User triggers the menu by tapping the list button at the top-left of navigation bar.
• User can also bring up the menu by swiping right on the main content area.
• Once the menu appears, user can close it by tapping the list button again.
• User can also close the menu by dragging left on the content area.

Figure 27-2. Slide-out Navigation Menu

Creating the Xcode Project


With a basic idea about what we’ll build, let’s move on. You can create the Xcode project
from scratch and design the user interface similar to figure 27-3.

330
Figure 27-3. Storyboard of the SidebarDemo App

The focus of this chapter is on the sidebar implementation. So to save your time from
setting up the project, you can download the Xcode project template (https://
dl.dropboxusercontent.com/u/2857188/SidebarDemoTemplate.zip) to start with.

331
The project already comes with:

• A pre-built storyboard with all the view controllers needed


• A pre-built view controller classes including MapViewController and
PhotoViewController
• The MapViewController is implemented to display a map
• The PhotoViewController is implemented to display a photo in an image view
• All icons and images needed for the app (credit: thanks for the free icon from Pixeden
http://www.pixeden.com/media-icons/flat-design-icons-set-vol1)

Importing the SWRevealViewController Library


As mentioned, we’ll use the free SWRevealViewController library to implement the slide-
out menu. So, first download the library from GitHub (https://github.com/John-Lluch/
SWRevealViewController/archive/master.zip) and extract the zipped file.

After you extract the file, you should find “SWRevealViewController.h” and
“SWRevealViewController.m”. Import both files into the Xcode project and put them under
“Library”.

Figure 27-4. Import SWRevealViewController into Library

Associate the Front View and Rear View Controller


One of the beauties of the SWRevealViewController library is it provides the built-in
support of Storyboard. Of course, if you prefer, you can also use Interface Builder to create

332
the sidebar menu. But as our focus in this book is on Storyboard, we’ll see how to use the
storyboarding way to create the slide-out sidebar.

When implementing sidebar menu using SWRevealViewController, developers have to


associate the SWRevealViewController with a front and a rear view controller using segues.
The front view controller is the main controller for displaying content. In our storyboard,
it’s the navigation controller which associates with another view controller for presenting
news content. Apparently, the rear view controller is the controller for showing the
navigation menu. Usually the menu is implemented by using UITableViewController.

Figure 27-5. Front and Rear View of SWRevealViewController

In the Xcode project template, we’ve pre-built the front and rear view controllers for you.
What you have to do is to define segues between the SWRevealViewController and front/
rear view controller.

First, select the empty UIViewController and change its class to “SWRevealViewController”.

333
Figure 27-6. Changing the class to SWRevealViewController

Next, press and hold the control key. Click on the SWRevealViewController and drag it to
the Sidebar view controller. After releasing the button, you’ll see a context menu for segue
selection. In this case, select “reveal view controller”. This defines a custom segue by using
“SWRevealViewControllerSegue”.

Repeat the same procedures to connect SWRevealViewController with the navigation


controller.

334
Figure 27-7. Defining Segues for SWRevealViewController

Select the segue between SWRevealViewController and the navigation controller. In the
attributes inspector, set the segue identifier to “sw_front”. This is the default identifier to
indicate a transition of front view controller.

For the segue between SWRevealViewController and the sidebar view controller, set the
segue identifier to “sw_rear”. This identifier tells SWRevealViewController that the
controller represents the rear view (i.e. the sidebar menu).

335
Figure 27-8. Setting the segue identifier for front and rear view controllers

If you compile and run the app, you’ll see an app displaying the “News Frontpage”. But
that’s it. You won’t see the sidebar menu when tapping the list button. We haven’t
implemented the action method yet.

Open “MainViewController.m”, which is the controller class of “News Frontpage”. Add the
following import statement:
#import "SWRevealViewController.h"

Next, add the following code in the viewDidLoad: method:


// Change button color
_sidebarButton.tintColor = [UIColor colorWithWhite:0.1f alpha:0.9f];

// Set the side bar button action. When it's tapped, it'll show up the sidebar.
_sidebarButton.target = self.revealViewController;
_sidebarButton.action = @selector(revealToggle:);

// Set the gesture


[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];

336
We first change the list button color and then assign the list button with an action. The
SWRevealViewController provides the revealToggle: method to handle the expansion and
contraction of the sidebar menu. Lastly, we also add a gesture recognizer. Not only you can use the
list button to bring out the sidebar menu, user can swipe the content area to activate the sidebar.

Try to compile and run the app in the iPhone simulatr. Tap the list button and the sidebar
menu should appear. Tap the button again to close it. You can also swipe right on the
content area to open the menu just like Figure 27-9.

Figure 27-9. Slide-out Sidebar Menu

Before moving on, add the same code snippet in the viewDidLoad: method of both
PhotoViewController.m and MapViewController.m. The app should show up the sidebar
when user taps the list button in these two view controllers.

Adding the Menu Items in Navigation Menu


With just a few lines of code, you already implement the slide-out navigation menu. Cool, right?

337
However, the menu is now empty. We’ll now add a few items and show you the transition
from one item to another. The sidebar view controller is just a simple table view controller.
For sake of simplicity, we’ll design the sidebar menu right in the storyboard.

The first table cell of the Sidebar View Controller is defined with the title “APPCODA”. If
you don’t like it, just change it to whatever you prefer. The only thing you have to ensure is
to keep the cell identifier as “title”, which we’ll refer it later in our code.

Okay, let’s add a few more menu items. To begin, select the prototype cell and change the
number of prototype cells to 8 in the attributes inspector. You should end up with a
screenshot similar to below:

Figure 27-10. Changing the number of prototype cells

Change the “APPCODA” label of the second cell to “News”. Optionally, you can change the
color of label to dark gray and set the font to “Helvetica Light” in the attributes inspector.
Next, drag a image view object from the object library to the cell. Set the size of image view
to 38×38 and change the image to “news.png”.

Next, select the cell and set the cell identifer as “news” in the attributes inspector. You
should end up with a cell similar to figure 27-11.

338
Figure 27-11. News cell item with news identifier

Repeat the above procedures to add the following menu items:

• 3rd cell - Comments (set the images as comments.png and cell identifier as comments)
• 4th cell - Map (set the images as map.png and cell identifier as map)
• 5th cell - Calendar (set the images as calendar.png and cell identifier as calendar)
• 6th cell - Wishlist (set the images as wishlist.png and cell identifier as wishlist)
• 7th cell - Bookmark (set the images as bookmark.png and cell identifier as bookmark)
• 8th cell - Tag (set the images as tag.png and cell identifier as tag)
If you’ve done everything correct, your sidebar view controller should look the one shown in
Figure 27-12.

339
Figure 27-12. Menu items in sidebar view controller

After completing the user interface, let’s implement the code for displaying the table cell.
Select the “SidebarViewController.m” and add the following import statement:
#import "SWRevealViewController.h"

Next, declare the menuItems variable to store the cell identifier of the menu items:
@implementation SidebarViewController {
NSArray *menuItems;
}

In the viewDidLoad: method, add the following code:


- (void)viewDidLoad
{
[super viewDidLoad];

menuItems = @[@"title", @"news", @"comments", @"map", @"calendar", @"wishlist", @"bookmark",


@"tag"];
}

The above code is very straightforward. We simply initialize the menu item array with the
values of cell identifier. Then edit the numberOfRowsInSection: method to return the
correct number of table row:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.

340
return [menuItems count];
}

Lastly, change the “cellForRowAtIndexPath” method to the following. The code simply gets
the cell identifier of the specified table cell from the menuItems array for display.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
NSString *CellIdentifier = [menuItems objectAtIndex:indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier
forIndexPath:indexPath];

return cell;
}

Now compile and run the app again. Tap the list button and you’ll find a slide-out
navigation menu with menu items.

Figure 27-13. SidebarDemo now shows the menu items

341
Implementing Menu Item Selection
You’ve already built a visually appealing sidebar menu. However, there is still one thing left.
As of now, we haven’t defined any segues for the menu item. When you select any of the
menu items, it won’t transit to the corresponding view.

To make the demo app simple, we’ll only connect the menu item with three view
controllers. I think this should give a pretty good demonstration to show you how it works.
Here are what we’re going to do:

• Connect the “News” cell item with the main view controller using a reveal view controller
segue
• Connect the “Map” cell with the map view controller using a reveal view controller segue
• For the rest of the menu items, they will be associated with the photo view controller
using the same type of segue. But we’ll display different photos for different menu items.
Okay, go back to storyboard. First, select the map cell. Press and hold the control key and
click on the map cell. Drag to the map view controller. Select the “reveal view controller”
under Selection Segue when prompted.

Figure 27-14. Connecting the map item with map view controller

342
Repeat the above procedure for the “News” cell item, but connect it with the main view
controller.

For the rest of menu items including comments, calendar, wishlist, bookmark and tag,
connect them one by one with the photo view controller and set the segue identifier as
“showPhoto”. We’ll use this identifier to differentiate the segue from the previous two we
created.

Figure 27-15. Defining showPhoto as the segue identifier

Once you complete the configuration of segues in storyboard, open the


“SidebarViewController.m”. Add the prepareForSegue: method to manage the transition.
- (void) prepareForSegue: (UIStoryboardSegue *) segue sender: (id) sender
{
// Set the title of navigation bar by using the menu items
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
UINavigationController *destViewController =
(UINavigationController*)segue.destinationViewController;
destViewController.title = [[menuItems objectAtIndex:indexPath.row] capitalizedString];

// Set the photo if it navigates to the PhotoView


if ([segue.identifier isEqualToString:@"showPhoto"]) {
PhotoViewController *photoController =
(PhotoViewController*)segue.destinationViewController;
NSString *photoFilename = [NSString stringWithFormat:@"%@_photo.jpg", [menuItems
objectAtIndex:indexPath.row]];
photoController.photoFilename = photoFilename;
}

343
if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] ) {
SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;

swSegue.performBlock = ^(SWRevealViewControllerSegue* rvc_segue, UIViewController* svc,


UIViewController* dvc) {

UINavigationController* navController =
(UINavigationController*)self.revealViewController.frontViewController;
[navController setViewControllers: @[dvc] animated: NO ];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated:
YES];
};

Let us go through the above code line by line:

• Line 4 to 6 are used to set the title of the navigation bar. We simply use the cell identifier
as title.
• Line 9 to 13 are only for those segues with “showPhoto” identifier. The photo view
controller will only display a single photo. Here we set the file name of the photo to be
displayed. Say, when user taps the “Comments” item, we’ll show the
“comments_photo.jpg”.
• Line 15 to 25 are the code to manage the view transition and tell SWRevealViewController
the new front view controller for display. We reuse the navigation controller and replace
the view controller with destination view controller.
Lastly, don’t forget to add the following import statement in the same file. Otherwise, your
code will not be compiled properly.
#import "PhotoViewController.h"

Compile and Test the Final App


Now compile and test the app again. Open the sidebar menu and tap the map item. You’ll
be brought to the map view. Try to test other menu items and see what you get.

344
Figure 27-16. Sidebar Demo App with menu selection implemented

Summary
In this chapter, you’ve learnt how to apply a third party library called
SWRevealViewController to implement a slide-out navigation menu similar to the one in
the Facebook app. The slide-out sidebar lets you offer an easy-to-access menu in your apps
but without wasting the screen real estate. When you’re going to create your next app,
consider to apply what you learnt in the chapter and add a slide-out navigation menu.

You’re not limited to use SWRevealViewController to create a sidebar menu. If you do a


search in GitHub, you can find other sidebar solutions such as GHSidebarNav (https://
github.com/gresrun/GHSidebarNav) and ViewDeck (http://www.verious.com/article/
view-deck/). You’re free to explore other libraries and see which is the best fit for your app.

In the next chapter, you’ll learn how to integrate with a cloud backend in your app.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/uiah8m3x9bdhnsr/SidebarDemo.zip.

345
C H A P T E R 28

ADDING A CLOUD
BACKEND USING PARSE
“They don't call it the Internet anymore,
they call it cloud computing. I'm no
longer resisting the name. Call it what
you want.”
~ Larry Ellison

In the first few chapters of the book, we built a simple Recipe app together and walk you
through the usage of table view. The initial version of Recipe app is very simple. All the
recipe data are stored locally in an array. Later, we enhance the app by putting the recipes
in a property list file. That’s better as it’s a good practice to separate static data from code.

However, both approaches face the same problem. Suppose you’ve published the Recipe
app on App Store, what if you need to add more recipes or change some of the recipes?

Obviously, you’ll need to update the code (or the property file), re-build the app and submit
to App Store again. And your users have to upgrade the app before viewing the updated
recipes. That’s a lengthy process. For your information, it normally takes a week for Apple
to review and approve your app.

A common solution is to put the recipe data into a backend database. Every time when the
app launches, it retrieves the data from the backend via web service. As the data is saved
and loaded remotely, you’re free to edit the recipe without rebuilding the app.

346
The downside is that you have to develop the backend.
Apparently, this requires a different skill set and huge
amount of work if you’re a beginner. Fortunately, several
companies have pre-built the backend for you that your
app can be easily integrated with the backend using API.
This kind of service provider is usually known as Backend
as a Service provider (or BaaS for short). Most of them
offer the service for free to get you started. You only need
to pay when the requests reach a certain volume.

In this chapter and the upcoming one, we’ll see how to


integrate the Recipe app with the Parse cloud (http://
parse.com/). The reason why I choose Parse is that it is
one of the leading BaaS providers and now a part of
Facebook. Parse provides an easy-to-use SDK which is
another reason Parse is demonstrated in this book.

We’ll focus on data retrieval from Parse backend in this


chapter, while you’ll learn about data update and deletion
in the next chapter. Figure 28-1. Integrating with
Parse Cloud
Okay, let’s get started.

Revisiting the Recipe App


Before we go into the Parse backend, let’s revisit the Recipe app and recap what we’ve done.
The Recipe app is a simple app that displays a list of recipes. User is allowed to tap on any
of the recipes and the app will show the details.

347
Figure 28-2. Simple Recipe App

So what are we going to change?

The user interface and table layout will remain the same. After going through the chapter,
you’ll end up with an app that looks just like before. However, internally the app pulls the
recipes from the cloud backend and we’ll add “Pull to Refresh” feature to the table view as
well.

To save your time from building the Recipe app from scratch, you can download the Xcode
project from https://dl.dropboxusercontent.com/u/2857188/
RecipeAppParseTemplate.zip. Unzip it and try it out. The rest of the tutorial will be built on
this code template.

Creating Your Parse Backend and Setup the Data


First, sign up a free account on http://parse.com. During the sign-up process, you’ll be
prompted to create your first app. Simply use “RecipeApp” as the name.

348
Figure 28-3. Sign up and create your first app

Once registered, click Dashboard to access the dashboard. The dashboard provides general
and application-specific usage metrics for APIs (see Figure 28-4), a data browser for
viewing your data in the backend and interface for managing push notifications.

Figure 28-4. Parse Cloud - Dashboard

349
In this chapter, we’ll mainly deal with the data browser (see figure 28-5) that is used for
managing the recipe data. To access the data browser, you can first select the “RecipeApp”
from the selection box and then click the “Data Browser” button.

Figure 28-5. Data browser in Parse

By default, you do not have any data in the RecipeApp. You’ll need to create and upload the
recipe data on your own. Before that, you have to define a Recipe class in the data browser.
The Recipe class is just like what we have declared in our code but it’s a cloud version. Click
“New Class” to create a new class. Set the name as “Recipe” and type as “Custom”. Once
created, you should see the class under the class sidebar.

In the Recipe app, a recipe consists of the following properties:

• the name of recipe


• the recipe image
• the preparation time
• the ingredients
Each of the above properties should be mapped to a specific column of the Recipe class in
the data browser. So select the Recipe class and click “+ Col” to add a column.

350
Figure 28-6. Adding a new column in data browser by using the +Col button

When prompted, set the column name as “name” and the type as “String”. Repeat the same
procedures to add the other three columns with the following column name and type:

• recipe image – set the column name as “image” and type as “File”. The File type is used
for binary data such as image.
• preparation time – set the column name as “prepTime” and type as “String”
• ingredients – set the column name as “ingredients” and type as “Array”

You may wonder how the data are converted to the Objective-C objects. The Parse SDK is
smart enough to handle the translation of native Objective-C types. For instance, if you
retrieve a String type from Parse, it will be automatically translated into a NSString object
in the app. We’ll discuss that in a while. Let’s first add some recipe data in the data browser.

Click “+ Row” button to create a new row. Each row represents a single recipe. You only
need to upload the recipe image and fill in the name, prepTime and ingredients columns.
For the objectId, createdAt and updatedAt columns, the values will be automatically
generated by Parse.

As an example, here is the code of a Recipe object:


Recipe *recipe1 = [Recipe new];
recipe1.name = @"Egg Benedict";
recipe1.prepTime = @"30 min";
recipe1.image = @"egg_benedict.jpg";
recipe1.ingredients = [NSArray arrayWithObjects:@"2 fresh English muffins", @"4 eggs", @"4
rashers of back bacon", @"2 egg yolks", @"1 tbsp of lemon juice", @"125 g of butter", @"salt and
pepper", nil];

To put this recipe into Parse, fill in the “name” column as “Egg Benedict”, the “prepTime”
column as “30 min” and upload the “egg_benedict.jpg” for the “image” column. To fill in
the “ingredients” array, use the following format:

["2 fresh English muffins","4 eggs","4 rashers of back bacon","2 egg yolks","1 tbsp
of lemon juice","125 g of butter","salt and pepper"]

351
Once you added the first recipe, your screen should look like Figure 28-7.

Figure 28-7. Adding a new row for Recipe class

Repeat the above procedures to add the rest of recipes. You’ll end up with a screen similar
to Figure 28-8.

Figure 28-8. Recipes in data browser

Setting Up the Xcode Project for Parse


With the recipe data configured, we’ll tweak the RecipeApp to retrieve the data from the
backend. You’ll first need to configure the Xcode project to work with Parse.

To start with, download the Parse SDK for iOS (https://www.parse.com/downloads/ios/


parse-library/latest). Unzip the file and drag the Parse.framework folder into your Xcode
project folder target.

352
Figure 28-9. Adding Parse SDK into Framework

The Parse SDK depends on other frameworks in iOS SDK. You’ll need to add the following
libraries into the project:

• AudioToolbox.framework
• CFNetwork.framework
• CoreGraphics.framework
• CoreLocation.framework
• libz.1.1.3.dylib
• MobileCoreServices.framework
• QuartzCore.framework
• Security.framework
• StoreKit.framework
• SystemConfiguration.framework
Select the RecipeApp project in the project navigator. Under the “RecipeApp” targets, select
“Build Phases” and expand the “Link Binary with Libraries”. Click the “+” button and add
the above libraries one by one.

353
Figure 28-10. Adding libraries

Before your app can connect to Parse, you have to register it with the backend during
launch. Open “RecipeAppDelegate.m” and add the following header at the top of the file:
#import <Parse/Parse.h>

Next, add the following code in the didFinishLaunchingWithOptions: method:


[Parse setApplicationId:@"Yhgymoa67lRbIVSfXHvQFv8rAIx2HjetNIkzV77u"
clientKey:@"Q0rQbU4aRCAJgC09DuPdbN0IqOF4qww5BgGHh1bG"];

Your application ID and client key will be different from the above. Make sure you replace
them with your own ID and client key that can be found under “Settings” of the Parse
dashboard.

Figure 28-11. Parse Dashboard – Settings

You’ve completed the configuration with Parse. Your app should be ready to connect with
the Parse backend. Try to compile and run it. If you get everything correct, you should be
able to run the app without any error.

Next, we’ll see how to retrieve the recipe objects that are just created from Parse.

354
Using PFQueryTableViewController for Cloud Data
As of now, the app uses the standard UITableViewController to display the list of recipes.
This is a standard way in iOS for displaying data in table form. To retrieve data from
backend and populate them into the table, however, it is a different story. There are a
number of issues to consider:

1. As we need to load the data from backend over Internet, it’s required to handle any
network related errors such as connection failure.
2. We can now update the recipe in the backend. The app should provide a mean for user to
refresh the data without relaunching.
3. The existing app saves the recipes and images locally. The recipe data are ready to load
into table when the app launches. That’s a complete different story when the recipes are
retrieved from the backend. During the app launches, the recipe data may not be ready.
It’ll take time to download the recipe data and images. How can you ensure the recipes
are ready before displaying them in the table?
4. The time required to load the images varies depending on the network speed. Should
your app wait until all images are downloaded before showing the recipes in the table?
That’s an unpleasant user experience if it takes too long to load. It’s better to display the
list of recipes while loading the recipe image in background.
5. What if you have a large set of recipes? In this case you may not want to load the recipes
all at once. For the sake of performance, you may just load a subset of recipes. Your app
will only display another subset of recipes when users hit a “Load more” button.
6. For best performance, you may consider to implement data caching in the app. So once
the recipes are downloaded, they will be cached locally.

The above are some of the design considerations when developing data-driven apps with a
backend. Luckily, the Parse SDK pre-built a controller named PFQueryTableViewController
(https://www.parse.com/docs/ios/api/Classes/PFQueryTableViewController.html) that
handles the background loading, caching and error exception. This should save you a large
amount of work from creating your own solution.

With this class, you’ll be able to setup a UITableView that displays your Parse data. The
class inherits from UITableViewController but comes with a bunch of handy methods that
make it easy to query, paginate, and display objects from Parse backend.

How To Use PFQueryTableViewController

355
When using Parse, each cell of a UITableView typically represents data from a PFObject.
Basically, to display data in PFQueryTableViewController, all you have to do is to override
these two methods:

- (PFTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:


(NSIndexPath *)indexPath object:(PFObject *)object
- (PFQuery *)queryForTable

The first method is similar to the cellForRowAtIndexPath: method defined in the


UITableViewDataSource protocol. You override the method to provide your
implementation to return a custom table cell for the PFObject. The queryForTable: method
is for you to load the objects from Parse backend.

Next, we’ll tweak the RecipeApp and replace the UITableView with the
PFQueryTableViewController.

First, open RecipeTableViewController.h and change it as a subclass of


PFQueryTableViewController. Don’t forget to import the Parse header too. Your code
should like below after change:
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>

@interface RecipeTableViewController : PFQueryTableViewController

@end

Now open the RecipeTableViewController.m. As we no longer use UITableView and the


recipe array, remove the following methods and the initialization code in the viewDidLoad:
method:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [recipes count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
RecipeTableCell *cell = (RecipeTableCell *)[self.tableView
dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell...


if (cell == nil) {
cell = [[RecipeTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}

// Display recipe in the table cell


Recipe *recipe = [recipes objectAtIndex:indexPath.row];

356
cell.nameLabel.text = recipe.name;
cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
cell.prepTimeLabel.text = recipe.prepTime;

return cell;
}

And replace them with these methods:


- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
// The className to query on
self.parseClassName = @"Recipe";

// The key of the PFObject to display in the label of the default cell style
self.textKey = @"name";

// Whether the built-in pull-to-refresh is enabled


self.pullToRefreshEnabled = YES;

// Whether the built-in pagination is enabled


self.paginationEnabled = NO;
}
return self;
}

- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];

return query;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath


*)indexPath object:(PFObject *)object
{
static NSString *CellIdentifier = @"CustomTableCell";

RecipeTableCell *cell = (RecipeTableCell *)[self.tableView


dequeueReusableCellWithIdentifier:CellIdentifier];

// Configure the cell


PFFile *thumbnail = [object objectForKey:@"image"];
PFImageView *thumbnailImageView = (PFImageView*)cell.thumbnailImageView;
thumbnailImageView.image = [UIImage imageNamed:@"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];

cell.nameLabel.text = [object objectForKey:@"name"];


cell.prepTimeLabel.text = [object objectForKey:@"prepTime"];

return cell;
}

Let’s go through the above methods one by one. In the initWithCoder: method, we
configure the behaviour of the PFQueryTableView. Here we set the name of the Parse class
to retrieve, enable the built-in pull-to-refresh feature but disable the pagination.

357
If you’ve referred to the Parse doc (https://parse.com/docs/ios_guide#ui-tables/iOS), you
may find that the above code slightly differs from the sample code. As we use Storyboard to
design the table view, we use initWithCoder: instead of initWithStyle:. The initWithCode:
method is the designated initializer when using Storyboards.

We specify the objects to load into PFQueryTableView by implementing the


queryForTable: method. The PFQuery class provides a handy way to load a list of objects
from Parse. You can simply create the PFQuery object with the Parse class to query.

The cellForRowAtIndexPath: method is very similar to the one we’ve implemented in the
standard UITableView. The main difference is with the use of PFObject. In brief, storing
data on Parse is built around the PFObject. Each PFObject contains key-value pairs of
JSON-compatible data. Here is a sample PFObject of the Recipe class:

"image": {
"__type": "File",
"name": "2516c9e3-ebd6-4e11-89cf-972ad77a942b-mushroom_risotto.jpg",
"url": "http://files.parse.com/4838f7c6-c40b-4ed8-b750-a5a3d2948599/2516c9e3-
ebd6-4e11-89cf-972ad77a942b-mushroom_risotto.jpg"
},
"ingredients": [
"1 tbsp dried porcini mushrooms",
"2 tbsp olive oil",
"1 onion, chopped",
"2 garlic cloves",
"350g/12oz arborio rice",
"1.2 litres/2 pints hot vegetable stock",
"salt and pepper",
"25g/1oz butter"
],
"name": "Mushroom Risotto",
"prepTime": "30 min"

When the cellForRowAtIndexPath: method is called, you’ll get a PFObject of the Recipe
class. In other words, you get a single recipe. To get the values out of the PFObject, use the
objectForKey: method. Say, to get the values of the “name” key, we use:
cell.nameLabel.text = [object objectForKey:@"name"];

Parse stores files (such as images, audio, document) in the cloud in the form of PFFile. We
use PFFile to reference the recipe image. The Parse SDK offers a handy class named

358
PFImageView that downloads and displays remote image stored on Parse’s server. Best of
all, the download is automatically done in background.
PFFile *thumbnail = [object objectForKey:@"image"];
PFImageView *thumbnailImageView = (PFImageView*)cell.thumbnailImageView;
thumbnailImageView.image = [UIImage imageNamed:@"placeholder.jpg"];
thumbnailImageView.file = thumbnail;
[thumbnailImageView loadInBackground];

We simply specify the recipe image (i.e. thumbnail) to load by setting the “file” property of
PFImageView. As mentioned, it takes time to download the recipe image over the Internet.
The PFImageView allows you to specify a placeholder image. Before the full recipe image is
downloaded, the image view will just show the placeholder image, which is stored locally in
the project. To start downloading the recipe image from the Parse cloud, we simply call up
the loadInBackground: method.

Lastly go back to Storyboard. Change the class of the thumbnail in the Recipe Table View
Controller from UIImageView to PFImageView.

Figure 28-12. Changing the custom class from UIImageView to PFImageView

359
Now compile and run the app. If everything is correct, the Recipe app should be able to
retrieve data from Parse backend.

Figure 28-13. RecipeApp loads recipes from Parse cloud

Pull to Refresh and Pagination


The PFQueryTableView looks the same as the UITableView. But try to pull down the table
and you’ll see the “Pull To Refresh” is already built-in. Go back to the data browser and

360
upload a new recipe. When you pull to refresh the data, you’ll find the new recipe. Cool,
right? You can now manage all your data in the backend.

Figure 28-14. Pull-to-refresh

Other than pull to refresh, the PFQueryTableViewController also comes with the pagination
feature. In the above section, we disable the pagination feature. To enable it, change the
paginationEnabled property in the initWithCoder: method to YES. By default, it shows 25
objects per page. You can change the number by altering the objectsPerPage property.
self.paginationEnabled = YES;
self.objectsPerPage = 10;

361
Let’s say we limit the number of object per page to 10. Compile and run the app again. The
app will only load the first 10 recipes when it first runs. Tapping the “Load more” row will
automatically retrieve the next 10 records. The pagination feature is especially useful when
dealing with a large date set.

Figure 28-15. Pagination

Caching for Speed and Offline Access


Try to close the app and re-launch it. Every time it opens, you’ll a “Loading…” screen that
the app is downloading recipes from backend. What if there is no network access? Try to
close the app and disable the network connection of your iPhone or Simulator. When
running the app again, you’ll see an error message.

362
Figure 28-16. PFQuery detects network connection error

There is a better way to handle this situation. Parse query has a built-in support for caching
that makes it a lot easier to save query results on disk. In case there is no network access,
your app can load the result from cache. Caching also improves the app performance.
Instead of loading data from Parse every time when the app runs, it retrieves the data from
cache on startup.

By default, caching is disabled. But it can be easily enabled using a single line of code. Add
the following code in the queryForTable: method (after the initialization of PFquery):
query.cachePolicy = kPFCachePolicyCacheThenNetwork;

The Parse query supports various types of cache policy. The


kPFCachePolicyCacheThenNetwork policy is just one of them. It first loads data from the
cache, then from the network.

Try to compile and run the app again. After you run it once (with WiFi enabled), disable the
WiFi or other network connection and launch the app again. This time, your app should be
able to show the recipes even it’s offline.

363
Your Exercise
The recipe detail view doesn’t work right now. When you select any of the recipes, you’ll see
a screen without any recipe information. So far we haven’t changed any code in the
RecipeDetailViewController class. Also the prepareForSegue: method is still referring to
the recipes array that is already obsolete. I’ll leave the changes for you as an exercise.

What’s Coming Next


In this chapter, we’ve migrated all the recipes from the app to the Parse cloud backend.
With the Parse SDK, it’s pretty easy to integrate the app with Parse to retrieve the data from
the cloud. This is just the first part of the Parse cloud series. In the upcoming chapter, we’ll
see how to add and update the recipe to the backend.

Parse is just one of the BaaS providers in the market. It’s easy to integrate and works pretty
well. However, you’re free to explore other providers such as Kinvey (www.kinvey.com), Kii
Cloud (www.kii.com), MobDB (www.mobdb.net), etc. Evaluate them and pick the most
suitable one for your development. The startup cost for most of the providers are almost
zero. They’re free to use and you only need to pay when the number of requests reach a
certain volume.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/5scv8wwzx84etk6/RecipeAppParse.zip.

364
C H A P T E R 29

PARSE CLOUD
PART-2
“Some people want it to happen, some
wish it would happen, others make it
happen.”
~ Michael Jordan

This is the second chapter of the Cloud Backend series. In the previous chapter, we gave you
an introduction of Backend as a Service (BaaS for short) and transformed the Recipe app.
Instead of storing the recipes locally, we’ve migrated the recipes to the Parse backend. The
Recipe app now connects to Parse and download the recipes from the cloud.

By now, you should have a basic idea about the Parse SDK. In part 1, you’ve learnt how to
retrieve objects from Parse. In this part, you’ll learn how to save new recipe to the Parse
backend. Of course, you’ll learn how to delete the recipe, as well.

We will build on the Xcode project that you’ve done in Part 1. So if you haven’t gone
through the project and exercise, it’s highly recommended to check out the first part of the
series.

Let’s get started.

365
Getting Started
Before moving to the coding part, let’s have a quick look at the new features. Previously,
we’ve built a Recipe app to retrieve recipes from Parse and display them using
PFQueryTableViewController. In this chapter, we’ll add two new features:

• Create new recipe and save it to the cloud


• Swipe to delete existing recipe from the cloud

Figure 29-1. Saving data to the Parse cloud

Designing the User Interface


The main user interface is pretty much unchanged. However, in order to let users add new
recipe, we’ll add a new view controller. The “New Recipe” view controller is triggered by

366
tapping the “+” button of the main interface. Through the new screen, user can fill in the
recipe information including name, preparation time, ingredients and photo.

Figure 29-2. Adding a New Recipe view controller

I’ll not show you how to build the interface as this is not the focus of this tutorial and you
should be capable to create it if you thoroughly understand the stuff covered in early
chapters. The “New Recipe” view controller is a subclass of UITableViewController and
created using static table view. You’re encouraged to build the interface but to save your
time, you can download the Xcode project from https://dl.dropboxusercontent.com/u/
2857188/RecipeAppParsePart2Template.zip. Unzip it and try it out. The rest of the chapter
will be built on this code template.

367
Before proceeding, remember to change the application ID and client key to your own Parse
IDs (i.e. the ones you created in previous chapter) in the RecipeAppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary
*)launchOptions
{
// Override point for customization after application launch.
[Parse setApplicationId:@"<change to your application ID>" clientKey:@"<change to your client
key>"];

return YES;
}

Picking Recipe Photo From Photo Library


We’ll first implement the “Add Photo” function of the New Recipe view controller. When
user taps on the “Add Photo” image, the app will bring up the photo library for users to pick
a recipe photo. As we covered before, the iOS SDK provides the class
UIImagePickerController to capture image from camera and access the photo library. If
you have no idea about UIImagePickerController, go back and check out chapter 23.

Creating a Media Browser


In order to use UIImagePickerController, as you know, we have to implement both the
UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols. So
change the @interface declaration of NewRecipeTableViewController.h to the following:
@interface NewRecipeTableViewController : UITableViewController <UINavigationControllerDelegate,
UIImagePickerControllerDelegate, UITextFieldDelegate>

You may notice there is an extra protocol named UITextFieldDelegate. Just ignore it right
now. We’ll explain it in later section.

The kUTTypeImage definition that we’ll use later is defined in the MobileCoreServices
framework and we’ll use the Parse SDK. So add the following import statement at the very
beginning of NewRecipeTableViewController.m:
#import <MobileCoreServices/UTCoreTypes.h>
#import <Parse/Parse.h>

Next, add the showPhotoLibrary: method in the NewRecipeTableViewController.m:


- (void)showPhotoLibary
{
if (([UIImagePickerController isSourceTypeAvailable:
UIImagePickerControllerSourceTypeSavedPhotosAlbum] == NO)) {
return;
}

UIImagePickerController *picker = [[UIImagePickerController alloc] init];


picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;

368
// Displays saved pictures from the Camera Roll album.
picker.mediaTypes = @[(NSString*)kUTTypeImage];

// Hides the controls for moving & scaling pictures


picker.allowsEditing = NO;
picker.delegate = self;

[self presentViewController:picker animated:YES completion:NULL];


}

In the above method, we first configure the image picker and set the source type
UIImagePickerControllerSourceTypeSavedPhotosAlbum. This will present a media
browser that provides access to the camera roll album on the device. By default, the photo
library includes both saved photos and videos. In this case, the Recipe app only allows user
to select recipe photo from library. So we limit the media type to kUTTypeImage. The rest
of the code is self explanatory.

When user taps “Add Photo”, that is the first row of the static table view, we’ll call up the
showPhotoLibrary: method to display the media browser. Therefore, add the following
code in the same implementation file:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0) {
[self showPhotoLibary];
}
}

If you run the app and try to add a new recipe, the app will show you the photos from
selection. However, the app will not respond to your selection as we haven’t implemented
the UIImagePickerControllerDelegate protocol.

369
Figure 29-3. Photo Album

Tip: If you’re using the iPhone Simulator to test out the app, your photo library is probably
empty. It’s very easy to add photo into the photo album. Just drag any of your photos from
Finder to the Simulator. It’ll automatically display the photo in Safari. Simply tap and hold
the image to save it into the photo library.

Configuring the UIImagePickerController Delegate


To respond to the user’s selection, we have to implement a delegate that conforms to
UIImagePickerControllerDelegate and UINavigationControllerDelegate protocols.
Obviously, the NewRecipeViewController is set as the delegate object.

Open the NewRecipeViewController.m and add the following code:


- (void) imagePickerController: (UIImagePickerController *) picker didFinishPickingMediaWithInfo:
(NSDictionary *) info {

UIImage *originalImage = (UIImage *) [info


objectForKey:UIImagePickerControllerOriginalImage];
self.recipeImageView.image = originalImage;

370
[picker dismissViewControllerAnimated:YES completion:nil];
}

When user selects a photo from the album, the didFinishPickingMediaWithInfo method of
the delegate is called and passed an NSDictionary object containing the media (image or
video) and associated data. With editing disabled, we can obtain the selected image from
the info dictionary by using the UIImagePickerControllerOriginalImage key. The selected
image is then assigned to the image view of the new recipe.

Tip: If editing is allowed, you can obtain the edited image by using
theUIImagePickerControllerEditedImage key.

If you run the app again, you should be able to select the recipe photo.

Figure 29-4. Display your selected in app

Handling Text Field


If you’re using the source code template, the text fields are pre-built and associated with the
code for you. The text fields are used for collecting recipe information including recipe
name, preparation time and ingredients from user. The ingredients field takes in a comma-
delimited string with each ingredient separated by comma. To keep the demo app simple,
there is no validation for the input.

371
Recalled that we have declared to adopt the UITextFieldDelegate protocol in the
NewRecipeTableViewController.h, the UITextField object messages the appropriate
UITextFieldDelegate method when certain events occur. In our case, we’d like to hide the
keyboard when user hits the return key. This can be easily done by implementing the
textFieldShouldReturn: method, which is called when the user presses the return key on
the keyboard.
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}

In addition, set the delegate of the UITextField objects to self in the viewDidLoad: method:
- (void)viewDidLoad
{
[super viewDidLoad];

self.nameTextField.delegate = self;
self.prepTimeTextField.delegate = self;
self.ingredientsTextField.delegate = self;
}

Uploading Recipe to Parse Cloud


With the basic setup of the user interface, let’s move onto the core part of the chapter. Once
user hits the “Save” button, the app will call up the save: method and upload the new recipe
including the recipe image to the Parse cloud. The Parse SDK provides an easy way to
upload data. Storing data on Parse is built around the PFObject. As mentioned previously,
each PFObject contains key-value pairs of JSON-compatible data.

In the NewRecipeTableViewController.m, implement the save: method with the following


code:
- (IBAction)save:(id)sender {
// Create PFObject with recipe information
PFObject *recipe = [PFObject objectWithClassName:@"Recipe"];
[recipe setObject:self.nameTextField.text forKey:@"name"];
[recipe setObject:self.prepTimeTextField.text forKey:@"prepTime"];

NSArray *ingredients = [self.ingredientsTextField.text componentsSeparatedByString: @","];


[recipe setObject:ingredients forKey:@"ingredients"];

// Recipe image
NSData *imageData = UIImageJPEGRepresentation(self.recipeImageView.image, 0.8);
NSString *filename = [NSString stringWithFormat:@"%@.png", self.nameTextField.text];
PFFile *imageFile = [PFFile fileWithName:filename data:imageData];
[recipe setObject:imageFile forKey:@"image"];

// Show progress
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];

372
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = @"Uploading";
[hud show:YES];

// Upload recipe to Parse


[recipe saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[hud hide:YES];

if (!error) {
// Show success message
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload Complete"
message:@"Successfully saved the recipe" delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];

// Dismiss the controller


[self dismissViewControllerAnimated:YES completion:nil];

// Notify table view to reload the recipes from Parse cloud


[[NSNotificationCenter defaultCenter] postNotificationName:@"refreshTable"
object:self];

} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload Failure" message:
[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];

}];
}

To save the Recipe object to the cloud, we first create a PFObject using the “Recipe” class
name. The PFObject works pretty much like the NSMutableDictionary, as you can see from
the above code (line 3-8).

For the recipe image, you can easily store it by converting it to NSData and then using
PFFile. Here we turn the original image into JPEG data using
UIImageJPEGRepresentation. Once you configure the PFObject, you can invoke the
saveInBackground: method to save the recipe. This method will automatically upload the
image and save the recipe in background. But to better handle the upload, we use an
alternative method called the saveInBackgroundWithBlock: method. You can provide
additional logic that will execute after the save completes. In our demo, we will display an
alert to indicate the completion, followed by notifying the main view controller to reload the
recipes.

In the above code, we sent the “refreshTable” notification to trigger the refresh of table
data. The notification will be picked up by RecipeTableViewController class to handle the
reload. Open the RecipeTableViewController.m file, add the following code in the
viewDidLoad: method:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(refreshTable:)
name:@"refreshTable"

373
object:nil];
This is to register the class to listen to the “refreshTable” message. Once it observes the
message, it’ll call up the refreshTable: method. Insert the following code in the same file:
- (void)refreshTable:(NSNotification *) notification
{
// Reload the recipes
[self loadObjects];
}

In the method, we simply invoke the loadObjects: method of the


PFQueryTableViewController class to reload the table data. Lastly, add the viewDidUnload:
method to deregister the observer:
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"refreshTable" object:nil];
}

Displaying Upload Indicator with MBProgressHUB


Now you should have a better idea of how object saving works. But what are those lines of
code (i.e. line 16-20) right after the PFObject? As you know, it takes time to upload the
recipe image. It’s always good to let user know something is in progress. The iOS SDK
provides developers with UIActivityIndicatorView for displaying a spinner. Here let us
introduce an awesome class developed by Matej Bukovinski. MBProgressHUD (https://
github.com/jdg/MBProgressHUD) is an open source class that displays a translucent HUD
with an indicator. You can download the class for free from Github. If you still wonder what
the class does, just take a look at the following screens provided by the developer:

374
Figure 29-5. MBProgressHUD Samples (image from https://github.com/jdg/MBProgressHUD)
The MBProgressHUD class is very simple to use. Just add both MBProgressHUD.h and
MBProgressHUD.m to your Xcode project, and you’re ready to go. For your convenience,
we’ve bundled both classes in the project template. It only takes a few lines of code to set up
the HUD. First, import the MBProgressHUD header file:
#import "MBProgressHUD.h"

Then initialize the HUD with the current view. The MBProgressHUD class provides
different HUD modes. We just use the MBProgressHUDModeInDeterminate mode and
change the label to “Uploading”. The last line tells the HUD to show as it is hidden by
default.
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.labelText = @"Uploading";
[hud show:YES];

To hide the HUD, you can call the following code:


[hud hide:YES];

375
Testing the App
This is how we use MBProgressHUD while saving the recipe to the Parse cloud. Okay, it’s
time to build and run the app. Try add a new recipe and save it to the Parse cloud. During
the recipe upload, you should see the HUD indicating the upload is in progress. Once
complete, the table view of Recipe Table view controller will refresh and load the new recipe
from Parse.

Figure 29-6. Save and Upload a Recipe

Deleting Recipe from Parse Cloud


You should know how to delete a row from table view if you’ve followed our earlier chapter.
Simply implement the commitEditingStyle: method in the RecipeTableViewController.m:
- (void)tableView:(UITableView *)tableView commitEditingStyle:
(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath

376
{
// Remove the row from data model
PFObject *object = [self.objects objectAtIndex:indexPath.row];
[object deleteInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[self refreshTable:nil];
}];
}

Like saving data to the Parse cloud, PFObject provides various methods for object deletion.
The deleteInBackground: method lets you delete the object from the cloud in background.
In the above code, we use an alternative method called deleteInBackgroundWithBlock: that
allows to run a block when deletion completes.

Figure 29-7. Delete a recipe

Summary
Congratulation! You’ve made it! Through these two chapters, you can see how easy it is to
save data to the cloud by using the Parse SDK. If you think it’s too hard and complex to
connect your app to the cloud, think again. Parse, as well as, other BaaS providers (e.g.
Kinvey) simplify the whole development of a backend. I hope this inspires you to add a
backend in your future iOS app and serves as a starting point to explore other BaaS
solutions. I am looking forward to seeing your cloud app!

In the final chapter, we’re going to discuss a new feature in iOS 7 called AirDrop. Let’s see
how you can power your app with file sharing feature.
For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/uo7ucjh1y10rekz/RecipeAppParsePart2.zip.

377
30

AIRDROP
AirDrop is Apple’s answer to file and data sharing. Before the debut of iOS 7, users need to
rely on 3rd-party apps such as Bump to share files between iOS devices. In iOS 7, Apple
introduced a new feature called AirDrop to all iPhone 5 models, the fourth-generation iPad,
the iPad mini, and fifth-generation iPod touch models. With AirDrop, you can easily share
data with other nearby iOS devices. In brief, the feature allows you to share photos, videos,
contacts, URLs, Passbook passes, app listings on the App Store, media listings on iTunes
Store, location in Maps, etc.

378
As a developer, wouldn’t be great to incorporate the AirDrop feature in your app? So your
users can easily share photos, text file or any types of document easily with nearby devices.
The UIActivityViewController class bundled in the iOS 7 SDK makes it simple for
developers to integrate AirDrop feature in their apps. The class shields you from the
underlying details of file sharing. What you just need is tell the class the objects you want to
share and it handles the rest. In this tutorial, we’ll demonstrate the usage of
UIActivityViewController and see how you can use it to share images / documents by using
AirDrop.

Let’s get started.

AirDrop Overview
Before we step into the implementation, let’s have a quick look at AirDrop. It’s very simple
to use AirDrop. Simply bring up Control Center and tap AirDrop to enable it. You can either
select “Contact Only” or “Everyone” depending on whom you want to share the data with. If
you choose the Contact Only option, your device will only discovered by people listed in
your contacts. Obviously, your device can be discovered by anyone for the Everyone option.

Figure 30-1. AirDrop sharing

379
AirDrop uses Bluetooth to scan for nearby devices. When a connection is established via
Bluetooth, it’ll create an ad-hoc Wi-Fi network to link the two devices together, allowing for
faster data transmission. It doesn’t mean you need to connect the devices to a Wi-Fi
network in order to use AirDrop. Your WiFi simply needs to be on for the data transfer.

Say you want to share a photo in the Photos app from one iPhone to another. Assuming
you’ve enabled AirDrop on both devices, to share the photos with another device, tap the
Share button (the one with an arrow pointing up) at the lower-left of the screen.

Figure 30-2. Sharing a photo via AirDrop

In the AirDrop area, you should see the name of the devices that are eligible for sharing.
AirDrop is not available when the screen is turned off. So make sure the device on the
receiving side is switched on. You can then select the device to share the photo. On the
other device, you’ll see a preview of the photo and a confirmation request. The recipient can
accept or decline to receive the image. If you choose the accept option, the photo is then
transferred and automatically saved in the camera roll.

AirDrop doesn’t just work with the Photos app. You can also find the share option in most
of the built-in apps such as Contacts, iTunes, App Store, Safari, to name a few. If you’re new
to AirDrop, you should now have a better idea.

380
Let’s move on and see how we can incorporate AirDrop feature in your app to share various
types of data.

A Quick Look at UIActivityViewController


You may think it’ll take a lot of efforts to implement the AirDrop feature. Conversely, you
just need a few lines of code to add AirDrop support. The UIActivityViewController class
available in iOS 7 SDK makes it super easy to integrate the feature. The AirDrop has been
built into the class.

The UIActivityViewController class is a standard view controller that provides several


standard services, such as copying items to the clipboard, sharing content to social media
sites, sending items via Messages, etc. In iOS 7 SDK, the class comes with the AirDrop
feature built-in.

Say, you have an array of objects to share using AirDrop. All you need to do is to initiate a
UIActivityViewController with the array of objects and present it on screen:
UIActivityViewController *controller = [[UIActivityViewController alloc]
initWithActivityItems:objectsToShare applicationActivities:nil];
[self presentViewController:controller animated:YES completion:nil];

With just two lines of code, you can bring up the activity
view with AirDrop option. Whenever there is a nearby
device detected, the activity controller automatically
shows the device and handles the data transfer if you
choose to.

Optionally, you can exclude certain types of activities. Say,


you can just display the AirDrop activity by excluding all
other activities. Use the following code:
UIActivityViewController *controller =
[[UIActivityViewController alloc]
initWithActivityItems:objectsToShare
applicationActivities:nil];

// Exclude all activities except AirDrop.


NSArray *excludedActivities =
@[UIActivityTypePostToTwitter, UIActivityTypePostToFacebook,
UIActivityTypePostToWeibo,
UIActivityTypeMessage,
UIActivityTypeMail,
UIActivityTypePrint,
UIActivityTypeCopyToPasteboard, Figure 30-3. Activate AirDrop
UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll,

UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo];

381
controller.excludedActivityTypes = excludedActivities;

// Present the controller


[self presentViewController:controller animated:YES completion:nil];

The activity view controller now only shows the AirDrop option:

Figure 30-4. Only AirDrop option is displayed

You can use UIActivityViewController to share different types of data including NSString,
UIImage and NSURL. Not only you can use NSURL to share a link, it allows developers to
transfer any types of files by using file URL.

On the receiving side, when the other device receives the data, it’ll automatically open an
app based on the data type. Say, if an UIImage is transferred, the received image will be

382
displayed in Photos app. When you transfer a PDF file, the other device will open it in
Safari. If you just share a NSString object, the data will be presented in Notes app.

A Glance at the AirDrop Demo App


To give you a better idea about UIActivityViewController and AirDrop, we’ll build a AirDrop
demo app. The app is very simple. When it is first launched, you’ll see a table view listing a
few files including an image file, a PDF file and a text file. You can tap the file and view the
content. In the content view, there is an action button on the top-right corner of screen.
Tapping it will bring up the AirDrop option and you can share the image or document with
nearby device.

Figure 30-5. AirDrop demo app

You’re encouraged to build the demo app from scratch. But to save your time, you can
download the project template (https://dl.dropboxusercontent.com/u/2857188/
AirDropDemoTemplate.zip) to start with. When you open Xcode project, you should find
the following Storyboard:

383
Figure 30-6. User Interface in Storyboard

I have already implemented the ListTableViewController and DocumentViewController for


you. If you compile and run the app, you’ll be presented with a list of files. When you tap
any of the file, the image or document content will be displayed. But the share button is not
yet implemented and that is what we’re going to talk about.

Adding AirDrop Feature


In the project template, the ListTableViewController is used to displayed the list of files in a
table view, while the DocumentViewController presents the document content via a web
view. The action button in the document view is associated with the share: method of the
DocumentViewController class. Edit the method with the following code:
- (IBAction)share:(id)sender {
NSURL *url = [self fileToURL:self.documentName];
NSArray *objectsToShare = @[url];

UIActivityViewController *controller = [[UIActivityViewController alloc]


initWithActivityItems:objectsToShare applicationActivities:nil];

// Exclude all activities except AirDrop.


NSArray *excludedActivities = @[UIActivityTypePostToTwitter, UIActivityTypePostToFacebook,
UIActivityTypePostToWeibo,
UIActivityTypeMessage, UIActivityTypeMail,
UIActivityTypePrint, UIActivityTypeCopyToPasteboard,
UIActivityTypeAssignToContact,
UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo];
controller.excludedActivityTypes = excludedActivities;

// Present the controller


[self presentViewController:controller animated:YES completion:nil];

384
}

If you’re not forgetful, the above code should be very familiar to you as we’ve discussed it at
the very beginning. The above code simply creates a UIActivityViewController, excludes all
activities except AirDrop and presents the controller as a modal view. The tricky part is how
you define the objects to share. Here we turn the file to share into a NSURL object and pass
the file URL as an array to AirDrop.

The first two lines of code are responsible for the file URL conversion. The documentName
property stores the current file (e.g. ios-game-kit-sample.pdf) displaying in document view.
We simply call up the fileToURL: method with the document name and it returns the
corresponding file URL. The fileToURL: method is bundled in the project template and
here is the code:
- (NSURL *) fileToURL:(NSString*)filename
{
NSArray *fileComponents = [filename componentsSeparatedByString:@"."];
NSString *filePath = [[NSBundle mainBundle] pathForResource:[fileComponents objectAtIndex:0]
ofType:[fileComponents objectAtIndex:1]];

return [NSURL fileURLWithPath:filePath];


}

The code is very straightforward. For example, the ios-game-kit-sample.pdf will be


transformed to file:///Users/simon/Library/Application%20Support/iPhone
%20Simulator/7.0.3/Applications/A5321493-318A-4A3B-8B37-E56B8B4405FC/
AirDropDemo.app/ios-game-kit-sample.pdf. The file URL varies depending on the device
you’re running. But the URL should begin with the “file://” protocol. With the file URL
object, we create the corresponding array and pass it to UIActivityViewController for
AirDrop sharing.

Build and Run the AirDrop Demo


You’re done. That’s what you need to implement AirDrop sharing. Compile and run the app
on a real iPhone. Yes, you need a real device to test AirDrop sharing. The sharing feature
won’t work on the Simulator.

385
Figure 30-7. Sharing PDF using the demo app

Uniform Type Identifiers (UTIs)


When you share an image to another iOS device, the receiving side automatically opens
Photos app and loads the image. If you transfer a PDF file, the receiving device may prompt
you to pick an app for opening the file or open it directly in iBooks. How can iOS know
which app to use for the type of data?

UTIs (short for Uniform Type Identifiers) is Apple’s answer to identify data handled within
the system. In brief, a uniform type identifier is a unique identifier for a particular type of
data or file. For instance, com.adobe.pdf represents a PDF document and public.png
represents a PNG image. You can find the full list of registered UTIs here. Application that
is capable of opening a specific type of file has registered to handle that UTI with the iOS.
So whenever that type of file is opened, iOS hands off that file to the specific app.

386
The system allows multiple apps to register the same UTI. In this case, iOS will prompt user
with the list of capable apps for opening the file. For example, when you share a PDF
document, you may experience the following screen in the receiving device:

Figure 30-8. Opening the PDF file using your preferred app

Summary
AirDrop is a very cool feature introduced in iOS 7. It offers a great way to share data
between devices. Best of all, the built-in UIActivityViewController has made it easy for
developers to add AirDrop support in their apps. As you can see from the demo app, it just
needs a few lines of code to implement the feature. I highly recommend you to put this
sharing feature in your app.

For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/2wdyiadx79zoc0w/AirDropDemo.zip.

387
BONUS CHAPTER

OBJECTIVE-C
BASICS
“You can't invent Google, Facebook or the
iPod unless you've mastered the basics,
are willing to put in long hours and can
pick yourself up from the floor when life
knocks you down the first 10 times.”
~ Amy Chua

Great! You’re still with us. If you read from the very beginning of the book, first
congratulation you made this far! By now, I believe you should be able to build your own
app or at least you’ve built yourself a solid foundation to chart your own course.

By working on the projects in this book, you’ve got the basic knowledge of Objective-C.
However, if you haven’t programmed in any language before, you’re probably confused by
some of the syntaxes. This bonus chapter intends to cover the basics of Objective-C
language. Yet, it cannot replace a complete Objective-C book. If you’re serious to master the
language, grab a book such as Objective-C Programming: The Big Nerd Ranch Guide or
explore other free resources on the web.

Anyway, let’s start to go over the basic principles and fundamental of Objective-C. But
before you start, it is recommended you should first read chapter 2 (Hello World Explained)
and chapter 12 (Introduction to Object Oriented Programming).

388
Programming Language and Objective-C
First thing first, what’s a programming language? Programming language is just like the
language you speak every day. It’s used for communication. However, instead of
communicating with people, you use the language to create program and communicate
instructions to a machine. There are a wide range of programming languages such as C++,
Java, PHP, Ruby, etc. Each computing platforms supports certain types of programming
languages. It’s just like English is an official language in United States. For Apple’s Mac OS
and iOS platforms, Objective-C is the main programming language used for creating apps.

Objective-C is not a new programming language. It was first invented in the early 1980s and
evolved over time. But it only gains in popularity with the prevalent of iOS. As you may
guess from its name, Objective-C is derived from C language and added with lots of object
oriented features. It inherits the syntax, primitive types and flow controls of C language
with the addition of classes and methods.

Okay, enough background and history. Let’s talk about the code.

Basic Data Types and Variables


At the very basic level, you work with data and variable in the code. First, let’s talk about
data types. As mentioned, Objective-C is a superset of C. It inherits the primitive types of
the languages and adds a few of its own. Here are some of the primitive data types you deal
with in Objective-C:

• int - an integer value. It’s used to describe a positive or negative whole number (i.e. a
number without decimals). Say, 100 can be stored as an integer data type.
• float - a floating point value. In other words, values containing decimal places. For
instance, 234.557 is a floating point number.
• double - it doubles the precision of floating point number.
• BOOL - a boolean value. It is either true or false. Traditionally, we use “true” and “false”
for boolean value. In Objective-C, however, it prefers the use of YES and NO for boolean
value.
• char - short for character. You use this data type for a single character value. Say, the
letter A or the symbol * or the numerical character 9.

389
Other than the primitive data types inherited from C, Objective-C adds a number of data
types for representing more complex data. Here are a few common ones you may have
come across:

• NSString - for string or text value. String is a sequence of characters. For example, you
can use NSString to store the value of “I love programming”. NSString is immutable. In
other words, you can’t change its value once it is instantiated. However, it has a mutable
counterpart called NSMutableString.
• NSNumber - a wrapper class around the numeric primitive data types (as discussed
above). You can think of it as the object-oriented version of the primitives. By converting
primitive type to the object-oriented version, you can enjoy some additional features such
as string conversion.
• NSArray - represents a collection of objects. You should be very familiar with this data
type as we’ve used it in most of the demo apps. Like NSString, NSArray is immutable once
it’s instantiated. It does, however, has a mutable counterpart named NSMutableArray.
• NSDictionary - like NSArray, it is used to store a collection objects. However, each entry
within a dictionary is a key-value pair. The key is considered as an identifier for the
associated value. Say, you want to create a price dictionary to store pricing information of
recipes. The key is the recipe name and the value is the price of that recipe.

Strictly speaking, the above data types are actually classes of the Cocoa Foundation
framework. Other than representing a certain type of data, the class provides useful
features for manipulating the data. Say, for NSString, it provides methods to change all
letters within the string to upper case or lower case.

You may wonder why the class names start with the NS. This is a naming convention
adopted by Apple. All classes under the Foundation framework use NS as prefix. The prefix
prevents name collision as all classes must be named uniquely. Okay, but why NS? The
prefix NS comes from NeXT Computer and its NeXTSTEP toolkit. If you haven’t heard of
NeXT Computer, it’s the company Steve Jobs founded after being forced out of Apple in
1985. The company was acquired by Apple in 1996 and its product became the foundation
of Mac OS and eventually, evolved into iOS.

390
With a basic understanding of data types, let’s move onto variables. In programming, you
use variable to hold temporary data. When you declare a variable in code, you usually give
the variable a type. For any variable declaration in Objective-C, it follows the below syntax:

[data type] [variable name] = [value];

Here are some of the examples:

int productNumber = 2334;


float productPrice = 19.99;

The first line of code declares an int (i.e. integer) variable named productNumber and
assign it with a value of 2334. The second line declares another variable named
productPrice with float data type. Its initial value is set to 19.99. In Objective-C (or other C
languages), each statement should end with a semicolon. Otherwise, you’ll see errors in
Xcode.

It’s pretty straightforward to declare variables with primitive type.

Declaring variables with object data types (e.g. NSString) is a bit different than that of
primitives. First, take a look at the following example:

NSString *recipeName = @"Egg Benedict";

The above code should be familiar to you if you've worked on the demo project. The above
line of code declares a string variable with the name recipeName and assigns it with a value
of "Egg Benedict". But what are the asterisk (*) and at-sign (@) for?

In Objective-C, all object variables are declared as pointers. The * symbol is used to indicate
the variable as a pointer. So what's a pointer? Pointer, as its name implies, points to a
specific memory location. When an object is instantiated, the actual data is stored in certain
memory location. You use the pointer to dereference the data.

Feeling confused, right? That's normal especially you're a new comer to C or Objective-C.
Pointers are often thought to be the most difficult aspect of C.

Let's consider this analogy and see if this helps. Imagine you're visiting a client office
located in a highly-secure building. Anyone who enters the building has to leave the
belongings at the front counter. So you stop by the counter and leave your suitcase. The
guard secures your suitcase in one of the lockers and hands you a label with a number (e.g.
389239454) on it. Later, when you want to get back the suitcase, you have to pass that label
to the guard. That number indicates the location of your suitcase. In this example, the

391
suitcase is the actual data and the locker is the chunk of memory for storing the suitcase.
And, the label with the number is the pointer that points to the location of the suitcase.

The analogy is not perfect but I hope you get a better idea about pointers. Still confused?
Not a problem. Meanwhile, you just remember to add the asterisk (*) when declaring an
object variable. As you continue to write code and explore Objective-C programming, you'll
eventually understand the concept thoroughly.

Next, let's talk about the at-sign (@). The @ symbol (together with the string value and
double quotation) is known as a NSString literal. This is the simplest way to create a string
in Objective-C.

In programming, a literal is simply a representation of a fixed value in source code.


Objective-C supports a wide range of object literals. NSString is just one of them. Developer
can make use of NSArray literal, NSNumber literal, NSDictionary literal, etc to create object
variables. The following is an example of NSArray literal:

NSArray *recipeNames = @[@"Egg Benedict", @"Full Breakfast", @"Coffee"];

without using literal, the same line of code will be rewritten like this:

NSArray *recipeNames = [NSArray arrayWithObjects: @"Egg Benedict", @"Full


Breakfast", @"Coffee", nil];

Before moving on, let's take a look at an example of NSNumber and NSArray literals. Here
we create an array of integers. As NSArray can only contain object types, we can use
NSNumber to convert integer to NSNumber objects. Traditionally, you declare the NSArray
like this:

NSNumber *quantities = [NSArray arrayWithObjects: [NSNumber numberWithInt:10],


[NSNumber numberWithInt:35],
[NSNumber numberWithInt:60],
[NSNumber numberWithInt:98],
nil];

By using literals, the same line of code will be rewritten like this:
NSArray *quantities = @[@10, @35, @60, @98];

It's much simpler. If you're still confused of literal syntax, simply think of it as a shorthand
of object creation. From the above examples, as you can see, literal syntax makes your code
more concise and easier to read.

392
Classes
We've covered the concept of Object Oriented Programming (OOP) in chapter 12. If you've
read the chapter, you should have a basic understanding of OOP. Like any object-oriented
programming language, you spend most of your time working with objects which are
technically instances of classes. In Objective-C, you define a class by declaring an interface.
A class generally contains properties and methods. Here is an example of class declaration:

@interface Recipe : NSObject

@property NSString *name;


@property NSString *image;

@end

In the above example, we declare a class named Recipe, which inherits from NSObject. In
other words, it is a subclass of NSObject. On top of that, the class has two public properties
called name and image. The interface is commonly stored in a .h file, as you should know.

The implementation of a class, on the other hand, is usually stored in a .m file. The
@implementation directive is used to declare the implementation of a class. Here is a
sample implementation of the Recipe class:

#import "Recipe.h"

@implementation Recipe

- (id) init
{
if ( self = [super init] ) {
self.name = @"Default Name";
self.image = @"Default Image";
}
return self;
}

@end

We just create an init method to set the default values of the recipe. There are two words -
super and self that may be interested to you. The Recipe class is defined as a subclass of
NSObject, which is known as a super class. So [super init] is actually calling the init method
of NSObject for initialization. The self is another keyword in Objective-C. It’s a hidden
pointer that can be used to call a method of the current object. You may be confused by the
term “method” right now. Don’t worry. We’ll talk about that in a while.

393
So how do you create an object instance of the class? First, this creation process is typically
called instantiation. When you instantiate an object, memory is allocated and that object’s
internal values are initialized to some default values. In Objective-C, you can instantiate an
object like this:

Recipe *recipe = [[Recipe alloc] init];

Methods
In object-oriented world, objects hardly work alone. Each object provides action methods to
handle certain tasks. They interact with each other by sending messages and work
collaboratively to complete a task or feature. It's quite similar to that in real world. For
instance, you order your food in a restaurant. It rarely happens that a single person takes up
the roles of a chef and a waiter. Commonly, the waiter (object A) takes your order and
passes it (sends a message) to the chef in the kitchen. And, the chef (object B) prepares the
dish.

From time to time, you come across the term "method" and "message" in object-oriented
programming. In terms of the above example, we'll say "object A sends message to object B
by calling the method on object B". In Objective-C world, it tends to use the phrase “sending
a message”. However, “calling a method” basically refers to the same thing. Let's keep to use
the terminology "call a method" for the rest of the chapter.

So what's a method and how do we call it? Let's consider the below example.

@interface RecipeStore : NSObject

- (void) sayThankYou;
- (void) greeting: (NSString*)message
+ (RecipeStore*) storeWithName: (NSString*)aName location: (NSString*)aLocation;

@end

When you look at the above code, method definitions begin with either a minus (-) or a plus
(+). In Objective-C, there are two types of methods: instance method and class method. The
minus (-) sign indicates that it's an instance method, while the plus (+) sign denotes a class
method. What an instance method means is that it is only accessible by an instance of the
class. On the other hand, a class method works on class level and you can call the method
directly without obtaining the instance.

Right next to the plus and minus sign, it’s the return type. You specify the return type by
using a pair of parenthesis. In the above example, the storeWithName: method returns an

394
instance of RecipeStore. It’s not mandatory for methods to return data. If there is nothing
to return, use the word void.

Followed by the return type, it’s the method name. If the method doesn’t take argument,
the method declaration ends with a semicolon.

Optionally, a method can take one or more arguments. The syntax may be a bit weird to
some of you especially you have C or Java background. The colon right after method name
indicates there is an argument coming. The parentheses that follow specify the type of
argument. Lastly, it’s the parameter name.

Okay, you’ve got a basic idea of a method declaration. So how do you use the method? The
basic syntax of calling a method is like this:
[object method];

Assuming you hold an instance of RecipeStore named store, you can invoke the
sayThankYou: method like this:
[store sayThankYou];

As said, you can declare methods with arguments. In terms of our example, the greeting:
method takes a NSString object as parameter. You can call the method like this:
[store greeting:@"Welcome to the store!"];

The syntax of calling of a class method is exactly the same as that of instance method.
However, you can call the method directly by using the class object (instead of a specific
object instance). Here is an example:
RecipeStore *store = [RecipeStore storeWithName:@"AppCoda" location:@"Hong Kong"];

Specifically, class methods used to create new objects are known as factory methods. Class
methods are commonly found in the iOS SDK and mostly used for creating new objects.
You can find lots of examples in classes such as NSString, NSArray, etc.

Assessors
Let’s consider the Recipe class again and talk about assessor methods.

@interface Recipe : NSObject

@property NSString *name;


@property NSString *image;

@end

395
You've used @properties directive in the demo project quite frequently. But probably you
do not know what's going on under the hood when it is used. Properties are a feature in
Objective-C that makes it easier to create and configure properties by automatically
generate accessor (getter/setter) methods. Without the @property directive, you need to
declare the class like this:

@interface Recipe : NSObject {


NSString *name;
NSString *image;
}

- (NSString*) name;
- (NSString*) image;

- (void) setName: (NSString*)newValue;


- (void) setImage: (NSString*)newValue;

@end

In the above code, we declare both name and image as member variables. As all instance
variables are private in Objective-C, we have to create our own getter and setter methods
for accessing the variables. Of course, you also need to provide the implementation of the
methods, that we'll not go into the details. Apparently, the @property directive simplifies
the use of properties and save developers from writing tons of code.

You have two ways to call the accessor methods. First, you can use the regular syntax and
call the methods explicitly just like below:
[recipe setName:@"English Breakfast"];
NSString *recipeName = [recipe name];

In Objective-C, you can use the dot syntax for getters and setters. Here is an example:

recipe.name = @"English Breakfast";


NSString *recipeName = recipe.name;

Look familiar to you, right? We’ve used the dot syntax throughout the book. When you the
dot syntax, the property is still accessed or updated via the accessor methods. It just
provides a convenient way for developers to call the methods.

Logging
In programming, sometimes you, as a developer, would like to log information about a
program execution. The information may include the application’s state, message or
actions. Basically, developers leave these traceable information for debugging purpose.

396
In Objective-C, you can use the NSLog() function to output information to the Apple’s
system facility. It’s pretty straightforward to use NSLog(). You simply pass a string or a
format string with additional parameters for values into NSLog() function and it’ll log the
message to the system-wise logging facility. Here is a sample line of code:
NSLog(@"Showing a message alert");

You can also pass a format string to display a message dynamically:


NSLog(@"Display recipe named %@", cell.nameLabel.text);

Depending on the device your app is running, the logging information is directed to system
console or displayed on-screen. For instance, if you run the app in the built-in iPhone
simulator, you’ll see the log right within Xcode in the debug area. Sometimes, Xcode doesn’t
display the debug area. You can bring it up by selecting View -> Debug Area -> Show Debug
Area.

Will the app user see this type of debug info? Normally not. The log will not be displayed on
screen. It’s output to the system console.

SELECTOR
If you’ve read the chapters about Parse cloud, you’d come across the following code:

[[NSNotificationCenter defaultCenter] addObserver:self


selector:@selector(refreshTable:)
name:@"refreshTable"
object:nil];

We’ve explained how the NSNotificationCenter work but we haven’t talked about the
@selector directive. So what’s a selector in Objective-C? A selector is the name used to
select a method to execute for an object. You can think of it as an internal representation of
a method name. Basically, a selector does nothing but just identifies a method. In the above
sample code, we use the @selector directive to get a selector for the refreshTable: method.
Actually it returns a special data type named SEL. The code can be rewritten as follows:

SEL refreshTableSel = @selector(refreshTable:);


[[NSNotificationCenter defaultCenter] addObserver:self
selector:refreshTableSel
name:@"refreshTable"
object:nil];

A selector doesn’t do anything until you execute it. You can invoke a method using a
selector with performSelector: method. Here is an example:

397
SEL replaySelector = @selector(replay:);
[videoPlayer performSelector:replaySelector];

In certain circumstances, you may not know which method to invoke at compilation time.
The beauty of selectors is that it lets developer execute a method dynamically during
runtime.

Comments
In the source code, some of the lines starts with // or /*. Those lines are known as
Comments and it’s a great feature in Objective-C and other programming languages.
Comments, as its name implies, allows developers to document their code. By documenting
the code, you provide extra information or explanation for yourself and other developers.
This makes the code more readable and lets other developers understand how the code
works.

You can use the // or /* */ syntax to write comment in code. If you just leave a single line of
comment, you usually use the // syntax. Anything you write after the forward slashes is
considered as comment by the compiler. Here is an example:
// Reload table data
[self.tableView reloadData];

If you need to provide detailed explanation of your code, a single-line comment may not be
enough. In such case, you can use the /* */ syntax to write comments that span multiple
lines. Everything enclosed within /* */ will be ignored by the compiler and considered as
comments. Here is another example:

/*
* This method is called when the application is about to terminate.
* You can save data if applicable.
*/
- (void)applicationWillTerminate:(UIApplication *)application
{
}

We ofter add an asterisk In Xcode, comments are by default highlighted in green.

Pragma
When you create a class that is a subclass of UITableViewController, you should see a few
lines of codes that start with #pragma in the implementation file. #pragma is a directive
used by Xcode for code organization. Generally you use #pragma in @implementation to
organize your code into logical sections. The directive tells Xcode’s code editor to add some

398
information in the pop-up menu of methods at the top of the editor pane. Let’s take a look
at the below example and you’ll see what I mean in a minute.

#pragma mark -
#pragma mark Table view data source
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:


(NSIndexPath *)indexPath
{
}

#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath
{
}

In the above code, we simply add #pragma mark to divide the methods in reference to their
own protocols. If you select the method menu at the top of the editor pane, you’ll see the
below screenshot:

As you can see, the #pragma mark starting with a dash (-) instructs the editor to add a
horizontal divider in the menu. The text after #pragma mark is highlighted in bold. By
using #pragma, you can better organize your code to improve readability. So take some
time to leave #pragma mark in your implementation.

Blocks
When you work on the Parse cloud chapter, there is a piece of code that may look weird to
you. Here is the code snippet extracted from the demo project:
[recipe saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[hud hide:YES];

399
if (!error) {
// Show success message
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload
Complete" message:@"Successfully saved the recipe" delegate:nil
cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];

// Dismiss the controller


[self dismissViewControllerAnimated:YES completion:nil];

// Notify table view to reload the recipes from Parse cloud


[[NSNotificationCenter defaultCenter]
postNotificationName:@"refreshTable" object:self];

} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Upload
Failure" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK"
otherButtonTitles:nil, nil];
[alert show];

}];

What’s that caret symbol (^) for? Why can we pass a chunk of code to the
saveInBackgroundWithBlock: method as parameter? It just looks weird if you haven’t been
introduced to the Blocks feature in Objective-C. Blocks are a feature that was first
introduced in iOS 4.0 SDK and provide much the same functionality as C functions that
greatly simplifies your code.

The basic syntax to define a block looks like this:


^ {

// Block implementation
NSLog(@"This is a block");

The caret (^) is the symbol that indicates a block. A block can be anonymous. And, just like
methods, a block doesn’t need to take any parameters. The above shows the simplest form
of a block.

Optionally, blocks take parameters like the code snippet we displayed in the very beginning
of the section:
^(BOOL succeeded, NSError *error) {

400
Like methods, blocks can have return value. Here is a block that returns a result of adding
three numbers.
^ (int numberOne, int numberTwo, int numberThree) {

return numberOne + numberTwo + numberThree;

Unlike method, it’s not mandatory to specify the return type in blocks. The compiler can
infer it from the return statement of the block.

To use the block, you can declare a block as variable and invoke it like this:
int (^AddingThreeNumbers)(int, int, int) = ^ (int numberOne, int numberTwo, int
numberThree) {
return numberOne + numberTwo + numberThree;
};

int result = AddingThreeNumbers(3, 9, 10);

In real world, we seldom use blocks like that. We normally use block inline without
declaring a separate variable.

The power of blocks is that you can pass it to methods as parameter and make your code a
lot simpler. Therefore, blocks are commonly used for callbacks. If you’re new to
programming, callbacks refer to the code that is executed after a task completes. Let’s take
another look at the code snippet we mentioned earlier (I’ve removed some of the code to
make it simpler):
[recipe saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
[hud hide:YES];

};

The saveInBackgroundWithBlock: method uploads the recipe to the cloud. When the
upload is done, it executes the block. This block is known as callback and specifies the code
to run after the upload is complete.

Of course, if you prefer, you can still declare a variable for the callback and use the block
like below:
void (^saveRecipeCallback)(BOOL, NSError*) = ^(BOOL succeeded, NSError *error) {
[hud hide:YES];

};

[recipe saveInBackgroundWithBlock:saveRecipeCallback];

But as you can see, the inline block is much easy to read as we put the code in one place.

401
Where to Go from Here
By now you should have a basic understanding of Objective-C. We’ve covered class, method,
data type and some common features of Objective-C such as blocks and assessors. You’ve
also learnt how to write comment in code and output logs for debugging purpose. On top of
that, you know how to make your code easier to read by adding pragma mark.

However, as mentioned at the very beginning of the chapter, this tutorial can’t replace a real
book of Objective-C. There are a lot to learn if you want to become a competent
programmer. So grab a book to learn more or you can keep exploring the following free
resources:

Apple’s Official Document about Objective-C

https://developer.apple.com/library/ios/documentation/cocoa/conceptual/
ProgrammingWithObjectiveC/Introduction/Introduction.html

Objective-C Introduction by RyPress

http://rypress.com/tutorials/objective-c/introduction.html

Objective-C Tutorials from Mobiletuts+

http://mobile.tutsplus.com/tutorials/iphone/learn-objective-c-day-1/

Objective-C Guide for Developers by iOS-Blog

http://ios-blog.co.uk/tutorials/objective-c-guide-for-developers-part-1/

Blocks Tutorial by Raywenderlich.com

http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2

Blocks Tutorial by AppCoda

http://www.appcoda.com/objective-c-blocks-tutorial/

Lastly, thanks for reading this book. This is a long journey for both of us. Here, I wish you
the best of luck and hope you’ll release your app on App Store very soon.

402

You might also like