Design Then Code - Building IOS Apps From Scratch

June 9, 2018 | Author: iluvmusic2 | Category: Class (Computer Programming), Ios, Xcode, Method (Computer Programming), Pointer (Computer Programming)
Report this link


Description

Building iOS Apps From Scratchby Mike Rundle Before taking a crack at any Design Then Code project tutorials you'll need some knowledge of Xcode, Objective‑C, 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. Built‑in 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 one‑liner!. 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 Objective‑C. Introduction To Objective-C Objective‑C 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 Objective‑C 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! Objective‑C is an object‑oriented programming language that is essentially a thin layer on top of C. It adds Smalltalk‑style 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 Objective‑C 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 Objective‑C methods and data types. Classes, Objects & Methods In case your object‑oriented programming fu is rusty (or totally nonexistent) we should first define what classes, objects and methods are and how they relate to each other in Objective‑C. A class is a blueprint that describes the state and behavior of a particular set of objects. It typically models a concept or a thing from the real world, for example, an Animal. An Animalclass would probably define some variables like the number of legs it has, its height and weight, and also behaviors, like running, eating and sleeping. An object of a given class is called an instance of the class. Each new instance you create can have its own values for the data. You could create 100 instances of the Animalclass and set each one's variables to something different. Methods are behaviors a class possesses, and in Objective‑C, 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 Objective‑C all objects live on the heap. All Obj‑C 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. Animal *myAnimal = [[Animal alloc] init]; Let's break this down piece by piece. The first thing you'll probably notice are the square brackets. They're everywhere in Objective‑C and they encapsulate method calls. On the left side of the line we're creating a new Animal*variable named myAnimal, then, on the right side, we're doing 2 different method calls (one nested within the other) and assigning what they return to myAnimal. To understand what's going on we need to look at the method calls from the inside‑out. First, we're calling [Animal alloc]which means we're When working with variables that have an explicit, set size at compile time, they're stored on the stack. The stack stores local information and you can access its data without pointers. Primitive data types (int, char, float, etc.) have a defined, maximum size so they're stored on the stack. In Obj‑C, objects don't have a maximum size (they can grow as much as you need them to) so their memory must be dynamically allocated on the heap and referenced with pointers. A pointer is an address in memory, and to access what's actually stored in that memory position, you dereference the pointer to get at its contents. calling the class method +allocdirectly on the Animalclass. Class methods are preceded with a plus sign. This method returns a generic object type of id. Then, we call the instance method -initwhich initializes the memory, allowing us to actually use this new object. Instance methods start with a minus sign. Objects in Objective‑C aren't fully ready to be used unless these two steps are taken. An important concept to understand when working with objects is that if you allocated memory for it, you're also responsible for letting the runtime know when you're done using the object so that its memory can be released. An object can be in use in different parts of an app at once, so Objective‑C uses a reference counting system to keep track of all this usage. When an object is first created, the number of references on the object is one. When an object is no longer being used, a -releasemessage must be sent to it, and this decreases the number of references on the object by one. When the overall number of references on an object drops to zero, Starting in Xcode 4.2, there's a new feature that Cocoa developers can use called Automatic Reference Counting (ARC) which takes all the effort out of manual memory management. It's a compiler feature which will automatically add in calls to -releasefor you so you don't ever the object's memory is freed. If objects that are no longer being used stay around and occupy memory some bad things could happen, one of which is your iOS app is forced to quit because it's using too many resources. For Mac apps, if an app takes up more and more memory, it could make have to manually release or autorelease any objects. It's still important to understand what's happening to your memory behind the scenes, but now ARC can make your development times faster. Read an overview of ARC at Apple's website or a full explanation at the Clang LLVM site. the whole system sluggish. Have you ever left Photoshop or Safari open for a few days and everything gets bogged down? It's because the apps are continually using more memory — potentially due to a memory leak — and there are fewer resources for other running apps. Memory is a precious resource when developing software so it's important to use it judiciously. For an in‑depth look at Cocoa memory management rules, read Apple's Memory Management Programming Guide. Now that we know how to create a fully‑formed, fully‑functional instance of a class, let's create a new Airplaneobject and call some methods on it. Airplane *myAirplane = [[Airplane alloc] init]; [myAirplane fly]; [myAirplane flyTo:@"Austin, TX"]; [myAirplane flyTo:@"Dulles Airport" landAtTerminal:3]; Calling methods on objects in Objective‑C has a fairly unique syntax compared to other languages you may be familiar with, so let's recreate the same calls in Java. Airplane myAirplane = new Airplane(); myAirplane.fly(); myAirplane.flyTo("Austin, TX"); myAirplane.flyToAndLandAtTerminal("Dulles Airport", 3); 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 Objective‑C 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 Objective‑C 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 pre‑defined 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: NSNumber *year = [NSNumber numberWithInt:2008]; 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 frequently‑used 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: NSNumber *year = [[NSNumber alloc] initWithInt:2008]; 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, built‑in behaviors that are defined in the NSArrayclass. To initialize our Carobject and access its instance variables, we do this: Car *myCar = [[Car alloc] init]; myCar.modelYear = [NSNumber numberWithInt:2008]; 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. Defining An Objective-C Class To define a new class in Objective‑C you must define two things: the class's interface and its implementation. A class interface tells the compiler about the class's instance variables and methods so it know what to expect. Like a preview of upcoming attractions. A class implementation is the actual code and functionality for each method. By convention, a class interface goes into a .h file and the implementation is in a .m file. For a Carclass, the two files would be Car.h and Car.m. Here's our Carinterface file: @interface Car : NSObject { 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 Let's go over the Carclass interface line by line. @interface Car : NSObject 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 Objective‑C you can choose to add or re‑implement 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. @interface Car : NSObject { NSNumber *modelYear; NSString *manufacturerName; UIColor *color; BOOL willTurnOn; } 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. @property (nonatomic, retain) NSNumber *modelYear; @property (nonatomic, copy) NSString *manufacturerName; @property (nonatomic, retain) UIColor *color; @property (nonatomic, assign) BOOL willTurnOn; Properties are an Objective‑C 2.0 feature that allow you quicker, more effortless access to your instance variables. What these actually do is configure the getter and setter methods to access and update these instance variables. For each instance variable you want to access and update using dot syntax (car.color, car.willTurnOn, etc.), you have to declare it as a @propertyas well. Before Obj‑C properties came along, you'd have to write two methods for each instance variable to "get" and "set" its value, but Another great thing about properties is that if you want to keep things really simple, you don't have to declare them as instance variables at all, you can let the runtime environment automatically create the instance variables as they're needed. So with that in mind, you could skip all instance variable declarations in the Carclass and have the first line be simply @interface Car : NSObjectand skip ahead to your @propertydeclarations. This really saves a lot of time, and lets you not repeat yourself in the code. now you can use properties to configure them automatically, allowing us to use the nifty dot syntax described previously. There are various ways to configure @propertiesand rather than go into depth here, you'll find Apple has a guide just for them. And finally you'll see the method signatures for the behaviors implemented in the .m file. - (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 Again, let's go over each line. @implementation Car At the top is a compiler directive of @implementationwhich signifies the start of our implementation file for this class. @synthesize modelYear, manufacturerName, color, willTurnOn; Next is the @synthesizestatement which is the counterpart to @propertyin the class interface. In our interface we declared our object's properties, and now, in the implementation file, we use @synthesizeto automatically generate the getters and setters based on our declared configurations for them. Don't forget to have both @propertyand @synthesizedeclarations for each property you want to use. - (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. Car *myCar = [[Car alloc] init]; // Set some of its properties myCar.color = [UIColor redColor]; myCar.modelYear = [NSNumber numberWithInt:1995]; // Call some of its methods [myCar turnRadioToStation:@"Hot 97"]; [myCar drive]; // Tell the Objective-C runtime that we're done with this Car object [myCar release]; 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 Objective‑C 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 If you're developing in Xcode 4.2 or later and have Automatic Reference Counting turned on, you would skip the -releasemessage as it's not needed. zero and its memory would be automatically deallocated. To be more specific, the runtime automatically calls the -deallocmethod on our Carobject. Typically we would implement -deallocin this class and do some cleanup/maintenance work before the memory is deallocated and the object vanishes. This is just scratching the surface of Objective‑C, but it's enough to propel you in the right direction if you want to start understanding and building Mac and iOS apps. Now onto the interesting stuff. Introduction To Cocoa (and Cocoa Touch) The phrases "Objective‑C development" and "Cocoa development" are thrown around interchangeably, and usually if you say either one, people know that you're talking about building Mac and iOS apps. Cocoa is a set of frameworks that Apple built for developers to use when building Mac and iOS apps. The Cocoa frameworks are written in Objective‑C and Objective‑C is the preferred language used to access the Cocoa frameworks when developing Mac and iOS 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 The NS‑prefix comes from NeXTSTEP, the (AppKit) for Mac OS X, and when developing iOS object‑oriented operating system developed by NeXT Computer. Apple purchased NeXT in apps, Foundation and UIKit. Foundation 1997 and Mac OS X is a descendant of its framework provides a base layer of classes for operating system. string and number manipulation, date objects, It's important to remember that not everything collections, networking and more. Some objects with an NS‑prefix is an object, for example, NSRectMake()is a C function that returns an include NSString, NSArray, NSURLConnection, NSDictionaryand NSNumber. AppKit classes are used to build the interface for Mac applications including windows, controls and menus. Example AppKit objects include NSWindow, NSView, NSButton, NSImageViewand NSScrollView. When developing iOS apps you'll still use the NSRect(a type of struct), and NSIntegeris a primitive data type. Depending on which framework you're working with, objects and functions may have a CG‑ prefix (for Core Graphics), a CF‑prefix (for Core Foundation), an MK‑prefix (for the iOS mapping framework MapKit), a UI‑prefix (for the interface framework UIKit in iOS), or various others. same, more generic classes available in the Foundation framework but instead of using AppKit to build user interfaces, UIKit is used instead. Some objects from UIKit that you'll be using all the time are UIWindow, UIView, UIButton, UIViewController, UILabel, UITableView, UITextViewand UIWebView. You might notice that for many objects, the UIKit version has the same name as the AppKit original, but with a UI‑ prefix instead of NS‑. A full list of classes available within UIKit is shown here at Apple's Developer site. To be honest, you'll probably never use Caror Airplaneobjects unless you're building an app for a car dealership or airport. The objects that you'll typically be using and subclassing are ones that Apple provides as part of the Cocoa frameworks, like NSArrayto manage collections of objects, UIViewControllerto manage a screenful of content, UITableViewCellfor a custom row in a table, UILabelfor displaying small bits of text, UIImageViewto display an image on the screen and many, many more. Apple provides many interesting and incredibly useful classes for you to use as you construct Mac and iOS apps, and typically they are descendants of a number of other classes, adding more functionality and specialization the further down the inheritance chain you look. For example, let's take a look at UIButton's full inheritance chain: UIButtoninherits from UIControl UIControlinherits from UIView UIViewinherits from UIResponder UIResponderinherits from the root class NSObject NSObjectis the root class for most Cocoa objects and handles memory allocation, initialization, message passing and other lower‑level tasks. UIResponderhandles interaction events like tapping the screen with your finger. UIViewdefines a rectangular area on the screen and provides the functionality to place or draw content within that rectangle. UIControlis the base class for user interface widgets and manages the state (selected? highlighted? disabled?) as well as what actions occur when the user interacts with it. And finally we get down to UIButton, a specialized control that takes care of a button's text label, all the styling of the button, and what it looks like when it's selected or highlighted. It's important to remember that when using a UIButtonyou don't just have access to the functionality of that class, but of all the classes that UIButtoninherits from all the way up to NSObject. I can't tell you how many times I missed something that was available in an object's parent class because I was only looking in the documentation for the immediate class I was using. Common Objects & Functions Regardless of what your app looks like there are some objects, functions and data structures that you'll use over and over so it's important to become familiar with them. NSString A string is a sequence of characters, and in Objective‑C, a string is an NSStringobject with a lot of built‑in functionality. The shorthand way of referring to a string @"looks like this"with an @ sign in front of a double‑quoted run of characters. NSString objects can be compared to one another, converted to other encoding formats, used to hold the contents of a file or URL, changed to C‑style strings, used in regular expression matches or turned into numbers. Once you create a string it cannot be modified. This may sound odd if you're coming from PHP or JavaScript, but if you want to modify a string after it has been created, you should use NSMutableStringinstead, a mutable (modifiable) subclass of NSString. Here's an example of how you might construct an NSURLobject by using a string: NSURL *url = [NSURL URLWithString:@"http://google.com/"]; And here's an example of using a mutable string: NSMutableString *myString = [NSMutableString stringWithString:@"Reading"]; [myString replaceOccurrencesOfString:@"Read" withString:@"Writ" options:NSLiteralSearch range:NSMakeRange(0, [myString length])]; That's a gigantic method that we're calling! The NSMutableStringinstance method replaceOccurrencesOfString: withString:options:range:finds a string within a string, then replaces it with something else. Notice the call to the NSMakeRange() function, part of the Foundation framework. It returns an NSRangestructdata type which has two members: location and length. A structis a special C data type that encapsulates other pieces of data into a single cohesive unit. Like an object, but built into C. Many Cocoa objects use structs. Each component of a structis called a member. Some common structs you'll need to be familiar with include NSRange(has 2 NSUIntegermembers: location and length), CGPoint(has 2 CGFloatmembers: an X coordinate and a Y coordinate), CGSize(has 2 CGFloatmembers: width and height) and NSArray An array is an ordered collection of things and if CGRect(has one CGPointmember and one CGSizemember). you're using NSArray, those "things" all have to To access the value of a member within a be objects. Just like with an NSString, once it's struct, dot notation is used. For example, if created it cannot be modified unless you use NSMutableArrayinstead. Here's an example of myPointis an CGPoint, you can access the X coordinate member of it with myPoint.x. making an array: NSArray *myArray = [NSArray arrayWithObjects:@"Fish", @"Dogs", nil]; When creating an NSArrayusing the +arrayWithObjects:class method, the list of objects needs to be nil‑terminated or it won't compile. It's not very intuitive at first, but it's important to remember to add it at the end of your list of objects or your code will crash and burn. Organizing objects into an array is useless if you never do anything with your new collection. Here are some examples of what you can do with a filled array. id myObject = [myArray objectAtIndex:3]; [myArray makeObjectsPerformSelector:@selector(runTowardsTheMoon)]; NSUInteger howMany = [myArray count]; The -objectAtIndex:instance method returns an id, a generic object in Cocoa. Since you can hold any type of object you want in an NSArray, when you access an object it gives you back a generic object type. It doesn't know what types of objects are being stored. Also, in the second example, what's a @selector? A @selectorin Objective‑C is basically a method signature. In this example, we're telling each object in the array to call their -runTowardsTheMooninstance method. In the third example the method returns an 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 key‑value pairs. Perl and Ruby have hashes, Python has dictionaries, PHP has associative arrays and Objective‑C has the NSDictionaryclass. The Foundation framework provides us with NSDictionary (and its mutable brother NSMutableDictionary) to hold key‑value pairs where all keys and values have to be objects. Let's define some dictionaries: NSDictionary *myDict = [NSDictionary dictionaryWithObjectsAndKeys:@"aObj", @"aKey", @"bObj", @"bKey", nil]; NSArray *objs = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil]; NSArray *keys = [NSArray arrayWithObjects:@"Blue", @"Green", @"Yellow", nil]; NSDictionary *anotherDict = [NSDictionary dictionaryWithObjects:objs forKeys:keys]; In the first example we're calling the class method +dictionaryWithObjectsAndKeys: which has to be nil‑terminated just like with some NSArraymethods. In our object listing at the end we alternate object, key, object, key until we're done with all the pairs. In the second example we're creating two different NSArrayobjects — one to hold the keys and another to hold the objects — and then we pass these two arrays into the NSDictionaryclass method +dictionaryWithObjects:forKeys:to build our dictionary. Again, notice that we're not using +allocor -initto manually allocate memory for these dictionaries. This means we don't have to worry about their memory or calling -releasewhen we're done using them. A common use of NSDictionaryis within an array: each element in the array is an NSDictionaryobject. For example, if you're building a Twitter app, the information for each tweet could sit in an NSDictionary, then each of these dictionaries is kept in order within an NSArray. // First, find the dictionary in the 4th position in the array. // Then, access the object paired with the key "tweet_text" NSString *status = [[myArray objectAtIndex:4] objectForKey:@"tweet_text"]; Organizing NSDictionarys within an NSArrayis a simple way to store some structured data. 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. NSNumber *myNumber = [NSNumber numberWithInt:326]; int myInt = [myNumber intValue]; 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. NSArray *myArray = [NSArray arrayWithObjects:@"Yo", @"Hey", nil]; NSLog( @"My array: %@", myArray ); int myInt = 2011; int anotherInt = 2012; NSLog( @"My int: %d and another int: %d", myInt, anotherInt ); 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 "stand‑ins" 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. Building Cocoa Applications We've talked about Objective‑C syntax and what objects are. We've also discussed some common Foundation frameworks objects and how Objective‑C classes are defined. Now it's time to start using some more complex Cocoa objects and putting all the pieces together to build a Cocoa app. Models, Views and Controllers, Oh My! A common design pattern in software development is called Model‑View‑Controller, or MVC for short. It's a methodology that separates the behavior and data of the application from the user interface. By keeping parts of the application separated into different classes your application can be developed more quickly and easily. By their design, the Cocoa 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 invoice‑sending 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. Client *myClient = [[Client alloc] init]; myClient.firstName = @"John"; myClient.lastName = @"Smith"; myClient.phoneNumber = @"212-555-1212"; [myClient saveDataToFile]; 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 iOS‑specific 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 built‑in view classes for use in your applications. You can use these as‑is, 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 built‑in view object that draws a simple run of text on the screen. CGRect textRect = CGRectMake(0,0,200,50); UILabel *myLabel = [[UILabel alloc] initWithFrame:textRect]; myLabel.text = @"Photos"; myLabel.backgroundColor = [UIColor clearColor]; myLabel.font = [UIFont boldSystemFontOfSize:24]; myLabel.textColor = [UIColor blackColor]; myLabel.shadowColor = [UIColor whiteColor]; That's a lot of properties! You'll find that most Apple‑built 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 top‑level 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. CGRect imageViewRect = CGRectMake(0,0,150,150); UIImageView *logoView = [[UIImageView alloc] initWithFrame:imageViewRect]; logoView.image = [UIImage imageNamed:@"logo.png"]; [self.window addSubview:logoView]; [logoView release]; // Skip this if you're using ARC 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 block‑level 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 built‑in interface widget is drawn, you'll want to create a subclass of it then re‑implement 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 static‑looking 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 top‑level 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 } The -loadViewmethod in your UIViewControllersubclass should be implemented when you are programmatically creating your view hierarchy and not using Interface Builder. It's called automatically when you first access the view controller's view property (typically when you add it to the main window object) so there's no need to call it manually. On the first line we're creating a CGRectto set as our main view's frame. The call to [UIScreen mainScreen]'s applicationFrameproperty returns a CGRectrepresenting the usable section of the screen which is the entire thing, minus the 20px status bar at the top. Next, we +allocand -initour UIViewobject and set it to self.viewwhich is this controller's view property. Now that our top‑level view object has been initialized, we add a UIImageViewto it as a subview, then release that view. Let's now add a UIButtonobject to our view and go over how we handle tap events. UIButton *myButton = [UIButton buttonWithType:UIButtonTypeInfoLight]; [myButton addTarget:self action:@selector(wasTapped:) forControlEvents:UIControlEve [myButton setFrame:CGRectMake(20,20,24,24)]; [self.view addSubview:myButton]; Here we're initializing a UIButtonusing the convenient class method +buttonWithType and setting its type to UIButtonTypeInfoLightwhich is a small info button built into UIKit. Next, we're calling -addTarget:action:forControlEvents:which is the standard way to setup a method getting called when a user interaction occurs. It's an instance method on the UIControlclass, a parent class of UIButton. It takes in three arguments: which class will handle the action (in this case, self), which @selectorwill be called, and finally, which interaction event we want to pay attention to. In this example we're listening for UIControlEventTouchUpInsidewhich is basically a normal tap on an iPhone or iPad screen. All these event types are listed in the UIControlclass reference. All that's left is to implement the method -wasTapped:in this class and you're in business. Lastly, we set the button's frame and then add it as a subview on the main view object. So where do we initialize our view controller and how do we begin using it? Well, if this is the view controller that will show the main screen of your app, it will be initialized in the application delegate class which Xcode creates for you when you create a new iOS project. - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( self.myViewController = [[MyViewController alloc] initWithNibName:nil bundle [self.window addSubview:self.myViewController.view]; [self.myViewController.view release]; // Skip this if you're using ARC [self.window makeKeyAndVisible]; } In our application delegate class, the instance method -application: didFinishLaunchingWithOptions:gets called when the app has successfully finished launching and is ready to be configured. Here we set self.myViewController(a property you'd create on this class) to the newly‑created MyViewControllerinstance, our subclass of UIViewController. Next, we add its view as a A delegate in Cocoa is a class that you specify will handle a task for another object. In order to qualify as a legitimate delegate object, the class may need to implement a certain set of methods that are automatically called. This set of methods is called a protocol. Some complex view objects like UITableView use a delegate to handle the creation of table cells, how many rows it has, and more. Typically, a view object's delegate (if it needs subview on the main window. It's at this exact one) is the view controller it was created within. moment that the runtime automatically calls - Cocoa uses the delegation design pattern extensively, and not just for view objects. The loadViewon the view controller and our custom I've only scratched the surface of how controller reason your application delegate class works is because it implements the UIApplicationDelegateprotocol to implement the main lifecycle behaviors of your objects are used. For a more in‑depth look, read app. view creation code is executed. Apple's View Controller Programming Guide. Your First App If you haven't already done so, open up Xcode and create a new iOS application. It will prepare a project for you with some key files already created. One of them is your application's delegate class which means it handles the main setup of your application. In this class you'll find -application:didFinishLaunchingWithOptions:which is the jump‑off point for your entire app. Here is where your first code will go, where your first UIViewControllerclasses will be initialized, where your first model objects may be created and used. If you want to get up and running fast with a detailed, step‑by‑step tutorial, head to the main Design Then Code site and take a look at the first tutorial for an app called Texture! It's over 70 pages of text, screenshots and code snippets, including the full Photoshop file and Xcode project.


Comments

Copyright © 2024 UPDOCS Inc.