Академический Документы
Профессиональный Документы
Культура Документы
by Mike Rundle
Before taking a crack at any Design Then Code project tutorials you'll need some
knowledge of Xcode, ObjectiveC, Cocoa and UIKit. My goal is for this guide to help bridge
the gap between having no knowledge of iOS development and having enough to start
tackling more interesting projects.
Tools
Apple provides a number of tools to enable developers to build Mac and iOS apps. To
download them, head to the Mac App Store and search for "Xcode". This $4.99 download
will give you access to Xcode (the IDE that Mac/iPhone developers use), Interface Builder,
the Cocoa Frameworks, tools for testing your apps, and a lot more. To register as an
official iOS developer and publish apps to the App Store (not to mention testing your
apps on a real device!) it costs $99 per year.
Here's a quick overview of the tools Apple provides.
Xcode
Xcode is an IDE (Integrated Development Environment) used by Mac and iOS developers to
build applications. It's not just a code editor: it has a variety of additional goodies baked in
like great autocomplete support, static code analysis (it finds bugs in your code before you
compile, including memory leaks) and a variety of debugging and performance tools. You
could use TextMate or BBEdit and then use command
line tools to do the final compilation, but most
developers choose to do it all within Xcode. I use
Xcode for all app development.
Interface Builder
Interface Builder is an application that lets you build
your interfaces visually. Builtin objects like buttons,
tab bars, sliders and labels can easily be dragged
onto your app's interface and then configured by
tweaking the palettes and panels. You can also use Interface Builder to connect targets and
actions (what happens when an interface element is acted on by a user, and what object
handles the action) as well as manipulate controllers and object bindings.
In my particular development workflow, I prefer not to use Interface Builder, mostly
because I work on custom interface components and those still take a lot of code to get
exactly right. I recommend that new Mac and iPhone developers get acquainted with
Interface Builder, but at the same time still learn the UIKitcode that it is generating for
you. I've seen many developers start using Interface Builder and never leave it, so they
never actually learn how to code interfaces from scratch. All Design Then Code tutorials
forego Interface Builder and explain how to write all UI code by hand.
Frameworks
And the most important piece of the puzzle: frameworks. Without frameworks and APIs
developers wouldn't easily be able to create applications that run on Mac OS X or iOS.
Apple provides dozens of frameworks that enable developers to do things like create user
interfaces, write networking code, encrypt important information, draw graphics to the
screen, play audio and video, save data and passwords, take pictures, display webpages
and much more.
The frameworks that Apple provides let you start off with a rich set of commands and
tools upon which to build your applications. Without the various frameworks that Apple
provides, every developer would be reinventing the wheel over and over again. There are
lots of goodies given to you in the various Cocoa frameworks so, many times, an
incredibly complex thing can be accomplished in only a few lines of code. An example of
this is automatically fetching a file on the Internet by its URL, parsing this file, then
stuffing it into a data structure that you can manipulate instantly is just a oneliner!.
These are just the main tools that Apple provides but there are many, many more to
explore and use. Go pick up Xcode from the Mac App Store and start poking around within
your new /Developer directory.
Introduction To Programming?
Are you totally new to computer programming? Have never even written any JavaScript?
This tutorial might be tough to swallow. Take a look at the following list of terms and
examples:
Variable var x = 15;
Function var name = John.getName();
Loop for (var x = 0; x < 10; x++)
Conditional if (x == 20)
Array var things = array("dog", "cat");
If any of these are confusing to you, I'd suggest heading to the JavaScript tutorial at
w3schools.com and working through the first few sets of chapters to get a quick feel for
general computer programming constructs. If you're coming from a language that does
not look like C (Lisp, Ruby, etc.) or if you have no C programming knowledge (what's a
header file? what's a struct?), I'd recommend taking a quick read of Scott Stevenson's
excellent C language tutorial and then coming back after your brain is thoroughly soaked
with knowledge.
Now let's talk about ObjectiveC.
Introduction To Objective-C
ObjectiveC is the language that most Mac and iOS developers use to write native
applications. There are alternative ways to write Mac and iOS apps (pure C or C++,
MacRuby, PyObjC, MonoTouch, etc.) but we won't be exploring those avenues here. Apart
from these programming languages, it's also possible to build "apps" that aren't natively
compiled for the platform but are instead websites made to look like a native app but
loaded in a web browser. Those apps can be written using a variety of frameworks but
they're primarily built using web technologies including JavaScript, HTML and CSS.
If you're looking to build mobile websites or web apps then you don't need to learn
ObjectiveC or Cocoa and probably don't need to go any farther in this tutorial. If you're
looking to learn a new programming language and build native apps for the Mac and iOS,
then this is the place for you!
ObjectiveC is an objectoriented programming language that is essentially a thin layer on
top of C. It adds Smalltalkstyle messaging, runtime reflection, inheritance and many other
things to the C programming language. Its syntax is very unique and may be confusing at
first to seasoned developers used to languages like Ruby or Java. Because ObjectiveC is a
superset of C, developers can "drop down into" C at any point in their code if they wish.
Many parts of a Mac or iOS application will utilize native C function calls and primitive C
data types right next ObjectiveC methods and data types.
ObjectiveC, you can call a method directly on the class itself or on an instance of it. These
two types of methods are called class methods and instance methods.
When you want to create a new instance of a class, you have to first allocate and initialize
a block of memory for it on the heap. The heap is memory set aside for dynamic allocation
and in ObjectiveC all objects live on the heap. All ObjC objects are pointers to this block
of memory and an asterisk (*) denotes that a variable is a pointer type. Here's an example
of creating an instance of our Animalclass.
Calling methods on objects in ObjectiveC has a fairly unique syntax compared to other
languages you may be familiar with, so let's recreate the same calls in Java.
Here we're initializing a new Airplaneobject and then calling 3 different methods on it.
The first, -flytakes in no arguments. The second, -flyTo:takes in one argument. The
third, -flyTo:landAtTerminal:takes in two arguments. The full names of ObjectiveC
methods include the names of the arguments. Colons in a method name indicate that it
takes an argument, one colon for each argument that it takes. These are instance methods
so a minus sign is included at the beginning of the method name.
Objects don't just have behaviors, they can store data as well. Let's imagine a Carobject
and the types of data attributes it could possess:
Model year
Name of manufacturer
Color
Will it turn on?
Let's look at these attributes. First, we have model year. This could be represented by a
numerical year, like 2008. What kind of data is 2008? It's a number, and in ObjectiveC
that number could be represented in a few different ways:
As a primitive int
As a Foundation framework data type NSInteger
As an instance of the NSNumberclass
Choices, choices! Let's talk about what each of these mean. The variable type intcomes
from C and is a primitive data type that holds a number with a certain maximum value. An
NSIntegeris a special primitive data type from Apple's Foundation framework that is
automatically sized correctly for the current architecture. The third way is an instance of
the NSNumberclass that is also defined in Foundation framework. We'll learn more about
Foundation and other Apple frameworks in a bit.
Neither an intnor an NSIntegerare objects which means you don't have to worry about
dynamically allocating memory for them since they have a predefined size. They get
created on the stack, not the heap, so no pointer is needed to access their contents.
Things like an int, NSInteger, CGFloat, CGPoint, CGRect(and a whole slew of other
things defined in the Foundation Data Types Reference) aren't objects, so no pointer
(asterisk) is needed.
Now let's look at the third example of how we could store the number 2008: as an
NSNumberobject.
We can initialize a new NSNumberobject like this:
If this is an instance of the NSNumberclass, where's +alloc? Where's -init? Well, it turns
out, some objects have convenient class methods that return an object with memory that
you don't have to manage yourself. Since you didn't manually create the memory using
+allocand -init, you don't have to worry about sending it a -releasemessage when
you're done. Many frequentlyused objects defined in Apple's Foundation framework have
these nice constructors. Of course if you want (or need) to manually manage their
memory, we could have also done this:
So now we have an instance of the NSNumberclass that is wrapped around a regular intof
2008. Why go through the trouble of using an NSNumberobject when we could have more
easily used an intor NSInteger? Because some Cocoa classes only make use of other
objects, they simply don't work with primitive types, for example, NSArray. An NSArrayis
an object that manages the ordered collection of other objects. If we wanted to use an
NSArrayto hold a series of numbers, we'd have to use NSNumberobjects because it
doesn't work with primitive data types like intor NSInteger. We could use ints and
NSIntegers in a regular C array, but then we wouldn't get all the great, builtin behaviors
that are defined in the NSArrayclass.
To initialize our Carobject and access its instance variables, we do this:
In this example, modelYearis a @propertyof the Carclass which enables us to use the
dot syntax to access it. We'll get into the specifics of properties and instance variables a
bit later.
Now that we know how to initialize new objects, call methods, and access its instance
variables, let's dive into defining new classes.
NSNumber *modelYear;
NSString *manufacturerName;
UIColor *color;
BOOL willTurnOn;
}
@property (nonatomic, retain) NSNumber *modelYear;
@property (nonatomic, copy) NSString *manufacturerName;
@property (nonatomic, retain) UIColor *color;
@property (nonatomic, assign) BOOL willTurnOn;
- (void)drive;
- (void)turnRadioToStation:(NSString *)station;
- (void)setRadioVolume:(NSNumber *)volume;
- (BOOL)hasNavigation;
@end
At the top of our interface file we declare Carto be a subclass of NSObjectwhich is the
root class for most of the Cocoa classes you'll encounter. I like to think about subclassing
as creating a more specialized version of an object. A generic class in Cocoa should be an
NSObjectsubclass. If you're making a specialized version of a button you might make a
UIButtonsubclass. Specialized version of a text label? It'd be a UILabelsubclass. What if
we wanted to make a specialized version of a Car, say, a convertible? Well, we'd probably
declare a Convertibleclass which would be a subclass of our Carclass, which, as I just
explained, is itself an NSObjectsubclass.
A subclass gives you the functionality of its parent class and all the other parent classes
up the chain. A Ferrariobject is a also a Convertibleobject which is also a Carobject.
If you declare all Carobjects as having GPS then the Ferrariwill as well. Right? Sure, but
when you create a subclass in ObjectiveC you can choose to add or reimplement
functionality of the parent classes as you see fit. For example, all UIButtonslook a certain
way, but if you want to make a totally custom button for your iOS app, you can subclass
UIButtonand draw the button's graphics yourself. Your custom code will override the
default behavior in the parent UIButtonclass.
Next up, the class's instance variables.
Inside the curly braces, right after the class declaration, you'll find the object's instance
variables. Instance variables are attributes for a specific instance of an object. If you create
3 different Carobjects they would each have their own values for these instance variables.
Not all cars have the same color, and not all instances of our Carclass will have the same
colorinstance variable value. Instance variables are declared with the variable type and
the variable's name. Three of these instance variables are objects (see the asterisks?) and
the fourth is a primitive boolean type that can only be either YESor NO.
After the instance variables you'll find a series of @propertydeclarations.
- (void)drive;
- (void)turnRadioToStation:(NSString *)station;
- (void)setRadioVolume:(NSNumber *)volume;
- (BOOL)hasNavigation;
These method declarations describe the type of data returned by a method, the method
name and its arguments. These are all instance methods because of the preceding each
one. These must match their corresponding implementation (the code that actually runs
when the method is called) in the .m file exactly.
Now for the implementation of our Carclass which will exist in a file called Car.m.
@implementation Car
@synthesize modelYear, manufacturerName, color, willTurnOn;
- (void)drive {
// Code to make the car drive would go here!
}
- (void)turnRadioToStation:(NSString *)station {
// Turn on the radio adjust the station!
}
- (void)setRadioVolume:(NSNumber *)volume {
// This one goes to 11!
}
- (BOOL)hasNavigation {
// Does this car have it?
}
@end
@implementation Car
- (void)drive {
// Code to make the car drive would go here!
}
- (void)turnRadioToStation:(NSString *)station {
// Turn on the radio adjust the station!
}
- (void)setRadioVolume:(NSNumber *)volume {
// This one goes to 11!
}
- (BOOL)hasNavigation {
// Does this car have it?
}
After we've synthesized our properties, we will write the code for each of the methods
declared in the interface. The method signature is the same as in the interface file, but the
functionality for each method follows directly after within a pair of curly braces.
And that's it! Our Carclass is now fully defined. Let's initialize a new Carobject and work
with it a bit.
After creating a fresh instance of a Carobject we can start using it. First, we can set some
of its properties like its coloror modelYear. The dot notation "object.property" works
because we declared certain instance variables to be able to be retrieved or set in this
manner. It doesn't happen automatically, but if you use @propertyand @synthesizethen
you're golden. It may seem pretty simple, but ObjectiveC is doing some legwork in the
background to make sure that memory is allocated and deallocated properly when you
directly access an object's properties.
We can also call a Carobject's instance methods like -turnRadioToStation:and drive. Notice in the first method call, we're passing in a string as an argument since that
is expected from the method definition. The second method, -drive, takes in no
arguments.
When we're done using our instance of a Car
class, we send it a -releasemessage to decrease
its reference count by one. As the only place using
this object, the reference count would drop to
applications. Apple used to provide support for using Java to access the Cocoa
frameworks, but it has fallen out of use in recent years. There are other ways to access
Cocoa APIs, including bindings for Python, Perl, Ruby, and C#, but for the most part those
aren't sanctioned by Apple.
The frameworks that make up what's known as
"Cocoa" include Foundation and Application Kit
operating system.
NSArray
An array is an ordered collection of things and if
making an array:
NSUInteger, a Foundation framework primitive type that's an unsigned integer, that is, an
integer that's greater than zero.
NSDictionary
Almost all languages have the concept of a data structure that holds keyvalue pairs. Perl
and Ruby have hashes, Python has dictionaries, PHP has associative arrays and ObjectiveC
has the NSDictionaryclass. The Foundation framework provides us with NSDictionary
(and its mutable brother NSMutableDictionary) to hold keyvalue pairs where all keys
and values have to be objects. Let's define some dictionaries:
NSNumber
If you want to store a primitive number in an NSArrayor NSDictionaryyou'll have to
package it up into an NSNumberobject first. NSNumberhas a number (ha!) of convenient
class methods so you'll rarely need to manage the memory yourself.
You can get the primitive value of an NSNumberobject just as easily as you can store it.
NSLog()
NSLog()is a C function to send output to the console, useful when debugging your
application.
Writing static text out to your log isn't that useful, so NSLog()lets you output variables as
well. To include a variable in your output, you need to know what type of variable it is
because NSLog()has different format specifiers based on the variable type. For example,
an array is an object so you'd use %@. The specifier for a plain intis %d. These format
specifiers are "standins" for the actual value of the variable listed at the end of the
function call. Apple provides the full list of string format specifiers to be used in NSLog()
so make sure to always use the right one.
frameworks practically force developers to use solid MVC principles, especially when
building iOS apps.
Each object in your application is assigned one of three roles: model, view or controller.
These roles describe that object's behavior and responsibilities. Let's talk about what each
part of MVC means as it pertains to building iOS apps.
Model
The model layer manages the data of your application. Model objects encapsulate data and
also hold the behaviors that will update or process this data. If you're building a Twitter
app you might create model objects to represent an individual account, tweet, direct
message or follower profile. If you were writing an invoicesending app you'd probably
have model objects for a company, person, invoice and more. A retro photography app?
Perhaps only one model object representing a photo. You can think of model objects as
being the nouns in a sentence that describes what your app does.
If you were building an app that persistently stores its data across launches, you'd build
behavior into your models so that they could save their own data to a file on the user's
iPhone, or perhaps send it to a server somewhere.
Here's some code showing how you'd initialize and use a model object.
In your apps you may use many model objects or you may use none, it all depends on
what kind of app you're building. Model objects are typically just subclasses of the Cocoa
root object, NSObject.
A benefit of using model objects is that they can be reused. Say you've built an iOS app
and then you choose to port it to the Mac. If you've built your model objects with
reusability in mind (not using iOSspecific APIs) it's fairly easy to port them to another
Cocoa platform like OS X. Your model objects should not be concerned or tied to the user
interface at all, thus making reusability possible.
View
Views are the objects used to build your user interface. For most of this guide the objects
we've discussed represented abstract concepts, things or types of data, but view objects
are different because they're actually seen by the user on the screen. Text labels, buttons,
input fields, table views, scrollable panes and tabs are all view objects with defined
behaviors and attributes. Views in iOS apps are all descendants of UIView, the root object
used to build user interface components defined in the UIKitframework. Here's a list of
all the classes available to you in UIKit.
Views have the ability to draw themselves (with colors, patterns, images, borders,
shadows, corner radii, transparency, etc.) and also respond to user input. Apple provides a
number of builtin view classes for use in your applications. You can use these asis,
customize some of their attributes, or go completely custom and write your own totally
unique view objects to build custom user interfaces and new methods for user interaction.
Here's an example of initializing and customizing a UILabel, a builtin view object that
draws a simple run of text on the screen.
That's a lot of properties! You'll find that most Applebuilt view objects come loaded with
ways for them to be configured, mostly by setting their properties. Many properties on
UILabellook similar to CSS styles like color, background color, text shadow, font and
more.
An important thing to remember is that initializing a view object does not put it on the
screen, it simply creates the object in memory. To put it on the screen, you must add the
view as a subview on an existing user interface object. The main UIWindowfor your
application is the toplevel view object for your application. All views that you create and
use in your app are subviews of the window, or of each other, creating an overall view
hierarchy.
Let's take a look at another view object example where we add it to the screen.
When you initialize a view object you typically don't just call -initon it, you call a
different initialization method -initWithFramewhich not only initializes the view object
but also sets its frameproperty. What's a frame? A frameis the rectangular region on the
screen that the view is drawn into. It's a CGRectstructwith four members: X position, Y
position, width and height. The X and Y coordinates make up a CGPoint structnamed
origin, and the width and height are part of an CGSize structnamed size.
In web design, when you absolutely position a <div>or any other blocklevel element,
you define things like its position and dimensions. A view object is no different. Once a
view object has been added to the overall view hierarchy (either as a subview of the
window, or another view), it then has another property called its bounds. The boundsand
frameproperties are very similar in that they both describe the view's position and
dimensions, but the boundsposition is {0,0}whereas the frameposition is relative to the
parent view. If a 100x100px view object is placed 10px down and 10px from the top left
of the parent view, its framewill be {10,10,100,100}and its boundswill be
{0,0,100,100}. This distinction is important in a few places, mainly when you want to
adjust the position of a view object once it's been placed. You'd do this by modifying its
frame, not its bounds.
If you want to completely modify how a builtin interface widget is drawn, you'll want to
create a subclass of it then reimplement its -drawRect:method and fill it with your own
drawing code. Here's an example of how you might implement some custom drawing.
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setFill];
UIRectFill(self.bounds);
[[UIColor whiteColor] setFill];
CGRect thinLine = CGRectMake(0,0,self.bounds.size.width, 1);
UIRectFill(thinLine);
[[UIColor blueColor] setFill];
NSString *springIsHere = @"Spring Is Here!";
UIFont *springFont = [UIFont systemFontOfSize:24];
[springIsHere drawAtPoint:CGPointMake(5,5) withFont:springFont];
}
Cocoa drawing follows what's called the "painter's model" for imaging. This means that
each successive drawing operation is overlaid on top of the next so the order of your
drawing code is very important. In this example we set the fill color to red and then fill the
entire view's rectangle with that color. After this operation we set the fill color to white and
fill a different rectangle with this new color. The CGRectthat we define for the second
drawing operation is a thin line that is 1px tall and spans the width of the entire view
rectangle. It's drawn at {0,0}which is the top left of the view. Finally, we set the fill color
to blue and draw a string at the point {5,5}with a 24px font.
Notice the call to self.bounds.size.width? If we take it step by step, we first make a
call to self.boundsto access the boundsproperty on this UIViewobject. This returns a
CGRect structthat, if you'll remember, has two members: originand size. We then
use dot notation to access the sizemember which is actually a structagain: an CGSize.
Finally, we access the widthmember of this final CGSize structto get the widthof this
view object's rectangle on the screen. Trust me, it's a lot more complex to describe it than
to actually use it. You'll be accessing a view's size, position, X coordinate, width, size, etc.,
in this fast way all over your code.
This isn't a very exciting example of custom drawing, but in -drawRect:you could overlay
graphics, vector shapes, colors and text any way you choose to implement a particular
design. For interface widgets, a common pattern is to check what interaction state it's in
before drawing to implement different designs for when a user's finger is tapping the
control or other states.
Each view object takes up memory and if you have a very complex interface you may end
up with many, many view objects on the screen and in memory all at once. This may be
fine if you're presenting a fairly staticlooking interface, but if you're building a complex
scrollable container or table view, it just won't do at all. One way to get around a multitude
of view objects on screen at once is to use only a few view objects (or just one!) and do all
your text and shape drawing in -drawRect:as it's extremely fast. Instead of placing
UIImageViewobjects into the view hierarchy, you'd instead draw a UIImagestraight to the
screen, and instead of using UILabel, you'd just draw strings directly at a CGPointor
within an CGRect.
Custom drawing in Cocoa is a very complex subject but it's an essential skill to have when
building custom apps. Take a look at Apple's Introduction To Cocoa Drawing once you're
ready to get dirty.
Controllers
Controllers are the meat of your application. They have many responsibilities and the bulk
of your custom application code will be within controller classes. Here's a sampling of the
responsibilities they have:
Initializing and positioning the view objects for your user interface
Automatically handling rotation changes and necessary layout updates
Handling user events (tapping, swiping, etc.)
Letting model objects know of data changes made at the view layer
Retrieving updated data from model objects and updating the view accordingly
When developing iOS apps, controller objects are subclasses of the root controller class,
UIViewController. Typically, a view controller will manage one screenful of data. If your
iPhone app contains 3 screens of functionality, you'll have a UIViewControllersubclass
managing each one of them. Take a look at the UIViewControllerclass reference to see
all the methods and properties that it provides.
As mentioned previously, when you write a subclass you're creating a specialized version
of the parent class. By itself, UIViewControllerisn't very exciting. If you initialize a new
UIViewControllerobject (not your custom subclass) it won't work for you as it expects
you to implement certain behaviors if you want things to happen. It's mainly a blueprint
for you to use to build your own custom controller functionality.
UIViewControllerobjects have a viewproperty which is initialized as the toplevel view
object for a single screenful of content. Here's an example of creating this initial view
object within the -loadViewmethod and adding a UIImageViewto it.
- (void)loadView {
CGRect rect = [UIScreen mainScreen].applicationFrame;
self.view = [[UIView alloc] initWithFrame:rect];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(5,5,
imageView.image = [UIImage imageNamed:@"logo"];
[self.view addSubview:imageView];
[imageView release]; // Skip this if you're using ARC
}
Let's now add a UIButtonobject to our view and go over how we handle tap events.
app.