Learn Ios7 Programming From Scratch
Learn Ios7 Programming From Scratch
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.
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.
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
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.
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.
Once launched, Xcode displays a welcome dialog. From here, choose “Create a new Xcode
project” to start a new project:
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.
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.
14
Figure 1-7. Main Xcode Window for Hello World Project
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.
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.
Xcode automatically builds the app and runs it in the Simulator. This is how the Simulator
looks like:
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.
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.
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”.
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
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:
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”.
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:
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;
-(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];
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.
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.
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.
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
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:
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.
.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.
.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
// 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];
@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.
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
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.
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
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:
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
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.
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.
#import <UIKit/UIKit.h>
@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.
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:
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).
43
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [recipes count];
}
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
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?
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.
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
! ! ! ! !
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.
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
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:simpleTableIdentifier];
}
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
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.
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.
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.
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.
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];
55
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
static NSString *CellIdentifier = @"CustomTableCell";
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
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.
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.
57
Before we move on to change the code, let’s revisit the code for displaying thumbnail in
table row.
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];
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.
After saving all the changes, try to run your app again. It should now display different
thumbnails in the table:
59
Figure 4-8. CustomTable App Showing Different Recipe Images
60
Figure 4-9. CustomTable App Showing Different Recipe Images
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.
Then select the prototype cell and click the “Size” inspector. Check the custom checkbox
and change the row height from 44 to 71.
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:
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.
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.
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.
Fill in “CustomTableCell” for the class name and select “UITableViewCell” for the “Subclass
of” option.
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;
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.
Select the prototype cell in storyboard. Under Identity Inspector, set the custom class to
CustomTableCell.
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.
66
Figure 4-20. Connecting the outlets
Let’s revisit the code in “CustomTableViewController.m” that currently used to create table
row:
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
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];
return cell;
}
However, as soon as you update the code, Xcode detects there are some errors as indicated
in the source code editor.
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.
#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:
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
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
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>
@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.
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:
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:
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];
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:
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.
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];
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).
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
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:
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:
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
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.
- (void)viewDidLoad
{
[super viewDidLoad];
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:
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.
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.
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.
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
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.
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];
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.
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.
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.
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:
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”.
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>
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:
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"];
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.
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:
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.
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:
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.
102
Figure 8-3. Launch Images for demo
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.
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
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”.
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.
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.
107
Figure 9-1. An example of Navigation Controller - Photos App
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.
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”.
109
Xcode automatically embeds the RecipeBook View Controller with Navigation Controller.
Your storyboard should look like this:
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
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).
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.
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
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:
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.
118
Figure 10-2. Create a RecipeDetailViewController class (Subclass of UIViewController)
119
Figure 10-3. Assign a custom class for the detail view controller
• 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
@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.
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];
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.
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”.
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.
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
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.
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.
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.
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.
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.
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”.
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.
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 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
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”.
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.
@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
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.
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.
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.
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
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.
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];
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.
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.
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"
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];
150
recipe8.name = @"Starbucks Coffee";
recipe8.prepTime = @"5 min";
recipe8.image = @"starbucks_coffee.jpg";
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.
return cell;
}
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.
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
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.
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.
- (void)viewDidLoad
{
155
[super viewDidLoad];
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];
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.
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.
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
161
RecipeDetailViewController.h and establish
the connection with the visual element in
storyboard.
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.
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”.
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"
@end
self.title = self.recipe.name;
self.prepTimeLabel.text = self.recipe.prepTime;
self.recipeImageView.image = [UIImage imageNamed:self.recipe.image];
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”.
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.
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.
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
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
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.
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)
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.
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];
}
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;
}
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
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.
To learn more about Predicate, check out Apple’s official documentation (https://
developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Predicates/
predicates.html).
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.
} else {
return [recipes count];
}
}
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.
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];
}
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
For your reference, you can download the complete Xcode project from https://www.dropbox.com/s/4h553i9jevjc8xu/RecipeAppSearchBar.zip.
179
15
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.
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.
182
Figure 15-3. Add a Collection View Controller
• 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.
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.
185
Under the Attribute Inspector of navigation item, set the title as “Recipe Photo”.
186
Go back to the storyboard and assign it as the custom class of the Collection View
Controller.
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.
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];
}
return cell;
}
Now compile and run the app. You should have a grid-
based photo app.
188
different views including background, selected background
and content view (see figure 15-12):
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
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
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:
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.
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.
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];
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];
}
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
194
You can use UIEdgeInsetsMake to create an inset:
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.
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
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.)
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.
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.
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”.
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”.
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"
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
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.
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”.
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
@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];
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];
}
}
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.
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];
}
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.
211
Figure 17-8. Adding the Share Button
212
Figure 17-9. Establish Connection with the Share Button
@end
• 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];
}
}
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) {
} else {
}
}
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”.
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.
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.
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.
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.
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
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.
223
Lastly, select the RecipeStoreAppDelegate.m and update the
didFinishLaunchingWithOptions: to the following:
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.
The below illustration can probably give you a better idea about the Core Data Stack:
224
Figure 18-6. Core Data Stack
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
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”.
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.
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
@end
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];
- (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.
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.
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.
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:
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?
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;
}
if (editingStyle == UITableViewCellEditingStyleDelete) {
// Delete object from database
[context deleteObject:[recipes objectAtIndex:indexPath.row]];
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
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.
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];
} 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"];
}
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.
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”.
Select the “Arguments” tab. Under “Argument Passed on Launch” section, click the “+”
button and add the “-com.apple.CoreData.SQLDebug 1″ 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
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.
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.
246
Figure 20-2. Demo app for localization
To make the app available in another language, we’ll demonstrate how to localize:
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.
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.
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.
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 = "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?";
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:
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 = "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 ";
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.
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.
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 .
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.
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.
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.
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
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.
262
Designing Static Table View
In Storyboard, first delete the default view controller and drag a Table View Controller from
the Object library.
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.
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.
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.
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.
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.
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.
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”.
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
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
271
Figure 22-2. Creating a new Xcode Project
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.
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.
<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:
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
276
@property (strong, nonatomic) NSString *pubDate;
@end
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];
[rssParser setDelegate:self];
[rssParser parse];
}
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"]) {
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];
}
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:
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.
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.
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"]) {
}
}
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.
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
283
Figure 23-2. Creating a new Xcode project
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:
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
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])
{
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]) {
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.
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 {
288
Figure 23-5. Camera App
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
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.
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.
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
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>
- (IBAction)captureVideo:(id)sender;
@end
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:
self.videoURL = info[UIImagePickerControllerMediaURL];
[picker dismissViewControllerAnimated:YES completion:NULL];
[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 {
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.
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];
// 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.
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.
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:
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.
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];
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];
}
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.
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?
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.
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.
306
// Request to reload table view data
[[NSNotificationCenter defaultCenter] postNotificationName:@"reloadData" object:self];
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.
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
310
Figure 26-2. Walkthrough Screens of our Demo App
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
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.
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.
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.
Go back to Storyboard. Select the page content view controller and set the custom class to
PageContentViewController under Identify Inspector.
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.
@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.
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];
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"
- (IBAction)startWalkthrough:(id)sender;
@property (strong, nonatomic) UIPageViewController *pageViewController;
@property (strong, nonatomic) NSArray *pageTitles;
@property (strong, nonatomic) NSArray *pageImages;
@end
We have created the data model for the page content. Next, we have to implement at least
two methods of the UIPageViewControllerDataSource protocol:
322
Add the following lines of code before the end of the ViewController.m file:
#pragma mark - Page View Controller Data Source
index--;
return [self viewControllerAtIndex:index];
}
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.
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.
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.
325
Figure 26-15. PageView Demo App
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.
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:
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”.
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.
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”.
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"
// Set the side bar button action. When it's tapped, it'll show up the sidebar.
_sidebarButton.target = self.revealViewController;
_sidebarButton.action = @selector(revealToggle:);
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.
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.
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:
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
• 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;
}
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.
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.
343
if ( [segue isKindOfClass: [SWRevealViewControllerSegue class]] ) {
SWRevealViewControllerSegue *swSegue = (SWRevealViewControllerSegue*) segue;
UINavigationController* navController =
(UINavigationController*)self.revealViewController.frontViewController;
[navController setViewControllers: @[dvc] animated: NO ];
[self.revealViewController setFrontViewPosition: FrontViewPositionLeft animated:
YES];
};
• 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"
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.
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.
347
Figure 28-2. Simple Recipe App
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.
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.
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.
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.
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.
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.
Repeat the above procedures to add the rest of recipes. You’ll end up with a screen similar
to Figure 28-8.
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>
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.
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.
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:
Next, we’ll tweak the RecipeApp and replace the UITableView with the
PFQueryTableViewController.
@end
356
cell.nameLabel.text = recipe.name;
cell.thumbnailImageView.image = [UIImage imageNamed:recipe.image];
cell.prepTimeLabel.text = recipe.prepTime;
return cell;
}
// The key of the PFObject to display in the label of the default cell style
self.textKey = @"name";
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
return query;
}
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.
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.
359
Now compile and run the app. If everything is correct, the Recipe app should be able to
retrieve data from Parse backend.
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.
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.
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;
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.
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.
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:
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.
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;
}
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>
368
// Displays saved pictures from the Camera Roll album.
picker.mediaTypes = @[(NSString*)kUTTypeImage];
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.
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.
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;
}
// 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];
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];
} 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];
}
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];
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.
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.
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.
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.
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.
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.
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.
UIActivityTypeAddToReadingList, UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo, UIActivityTypePostToTencentWeibo];
381
controller.excludedActivityTypes = excludedActivities;
The activity view controller now only shows the AirDrop option:
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.
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
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]];
385
Figure 30-7. Sharing PDF using the demo app
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.
• 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:
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.
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:
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.
without using literal, the same line of code will be rewritten like this:
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:
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:
@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:
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.
- (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.
@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:
- (NSString*) name;
- (NSString*) image;
@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:
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");
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:
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:
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
{
}
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
{
}
#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];
} 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.
// 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) {
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;
};
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:
https://developer.apple.com/library/ios/documentation/cocoa/conceptual/
ProgrammingWithObjectiveC/Introduction/Introduction.html
http://rypress.com/tutorials/objective-c/introduction.html
http://mobile.tutsplus.com/tutorials/iphone/learn-objective-c-day-1/
http://ios-blog.co.uk/tutorials/objective-c-guide-for-developers-part-1/
http://www.raywenderlich.com/9438/how-to-use-blocks-in-ios-5-tutorial-part-2
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