- •Contents at a Glance
- •About the Authors
- •About the Technical Reviewer
- •Acknowledgments
- •Preface
- •What This Book Is
- •What You Need
- •Developer Options
- •What You Need to Know
- •What’s Different About Coding for iOS?
- •Only One Active Application
- •Only One Window
- •Limited Access
- •Limited Response Time
- •Limited Screen Size
- •Limited System Resources
- •No Garbage Collection, but…
- •Some New Stuff
- •A Different Approach
- •What’s in This Book
- •What’s New in This Update?
- •Are You Ready?
- •Setting Up Your Project in Xcode
- •The Xcode Workspace Window
- •The Toolbar
- •The Navigator View
- •The Jump Bar
- •The Utility Pane
- •Interface Builder
- •New Compiler and Debugger
- •A Closer Look at Our Project
- •Introducing Xcode’s Interface Builder
- •What’s in the Nib File?
- •The Library
- •Adding a Label to the View
- •Changing Attributes
- •Some iPhone Polish—Finishing Touches
- •Bring It on Home
- •The Model-View-Controller Paradigm
- •Creating Our Project
- •Looking at the View Controller
- •Understanding Outlets and Actions
- •Outlets
- •Actions
- •Cleaning Up the View Controller
- •Designing the User Interface
- •Adding the Buttons and Action Method
- •Adding the Label and Outlet
- •Writing the Action Method
- •Trying It Out
- •Looking at the Application Delegate
- •Bring It on Home
- •A Screen Full of Controls
- •Active, Static, and Passive Controls
- •Creating the Application
- •Implementing the Image View and Text Fields
- •Adding the Image View
- •Resizing the Image View
- •Setting View Attributes
- •The Mode Attribute
- •Interaction Checkboxes
- •The Alpha Value
- •Background
- •Drawing Checkboxes
- •Stretching
- •Adding the Text Fields
- •Text Field Inspector Settings
- •Setting the Attributes for the Second Text Field
- •Creating and Connecting Outlets
- •Closing the Keyboard
- •Closing the Keyboard When Done Is Tapped
- •Touching the Background to Close the Keyboard
- •Adding the Slider and Label
- •Creating and Connecting the Actions and Outlets
- •Implementing the Action Method
- •Adding Two Labeled Switches
- •Connecting and Creating Outlets and Actions
- •Implementing the Switch Actions
- •Adding the Button
- •Connecting and Creating the Button Outlets and Actions
- •Implementing the Segmented Control Action
- •Implementing the Action Sheet and Alert
- •Conforming to the Action Sheet Delegate Method
- •Showing the Action Sheet
- •Spiffing Up the Button
- •Using the viewDidLoad Method
- •Control States
- •Stretchable Images
- •Crossing the Finish Line
- •The Mechanics of Autorotation
- •Points, Pixels, and the Retina Display
- •Autorotation Approaches
- •Handling Rotation Using Autosize Attributes
- •Configuring Supported Orientations
- •Specifying Rotation Support
- •Designing an Interface with Autosize Attributes
- •Using the Size Inspector’s Autosize Attributes
- •Setting the Buttons’ Autosize Attributes
- •Restructuring a View When Rotated
- •Creating and Connecting Outlets
- •Moving the Buttons on Rotation
- •Swapping Views
- •Designing the Two Views
- •Implementing the Swap
- •Changing Outlet Collections
- •Rotating Out of Here
- •Common Types of Multiview Apps
- •The Architecture of a Multiview Application
- •The Root Controller
- •Anatomy of a Content View
- •Building View Switcher
- •Creating Our View Controller and Nib Files
- •Modifying the App Delegate
- •Modifying BIDSwitchViewController.h
- •Adding a View Controller
- •Building a View with a Toolbar
- •Writing the Root View Controller
- •Implementing the Content Views
- •Animating the Transition
- •Switching Off
- •The Pickers Application
- •Delegates and Data Sources
- •Setting Up the Tab Bar Framework
- •Creating the Files
- •Adding the Root View Controller
- •Creating TabBarController.xib
- •The Initial Test Run
- •Implementing the Date Picker
- •Implementing the Single-Component Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller As a Data Source and Delegate
- •Implementing a Multicomponent Picker
- •Declaring Outlets and Actions
- •Building the View
- •Implementing the Controller
- •Implementing Dependent Components
- •Creating a Simple Game with a Custom Picker
- •Writing the Controller Header File
- •Building the View
- •Adding Image Resources
- •Implementing the Controller
- •The spin Method
- •The viewDidLoad Method
- •Final Details
- •Linking in the Audio Toolbox Framework
- •Final Spin
- •Table View Basics
- •Table Views and Table View Cells
- •Grouped and Plain Tables
- •Implementing a Simple Table
- •Designing the View
- •Writing the Controller
- •Adding an Image
- •Using Table View Cell Styles
- •Setting the Indent Level
- •Handling Row Selection
- •Changing the Font Size and Row Height
- •Customizing Table View Cells
- •Adding Subviews to the Table View Cell
- •Creating a UITableViewCell Subclass
- •Adding New Cells
- •Implementing the Controller’s Code
- •Loading a UITableViewCell from a Nib
- •Designing the Table View Cell in Interface Builder
- •Using the New Table View Cell
- •Grouped and Indexed Sections
- •Building the View
- •Importing the Data
- •Implementing the Controller
- •Adding an Index
- •Implementing a Search Bar
- •Rethinking the Design
- •A Deep Mutable Copy
- •Updating the Controller Header File
- •Modifying the View
- •Modifying the Controller Implementation
- •Copying Data from allNames
- •Implementing the Search
- •Changes to viewDidLoad
- •Changes to Data Source Methods
- •Adding a Table View Delegate Method
- •Adding Search Bar Delegate Methods
- •Adding a Magnifying Glass to the Index
- •Adding the Special Value to the Keys Array
- •Suppressing the Section Header
- •Telling the Table View What to Do
- •Putting It All on the Table
- •Navigation Controller Basics
- •Stacky Goodness
- •A Stack of Controllers
- •Nav, a Hierarchical Application in Six Parts
- •Meet the Subcontrollers
- •The Disclosure Button View
- •The Checklist View
- •The Rows Control View
- •The Movable Rows View
- •The Deletable Rows View
- •The Editable Detail View
- •The Nav Application’s Skeleton
- •Creating the Top-Level View Controller
- •Setting Up the Navigation Controller
- •Adding the Images to the Project
- •First Subcontroller: The Disclosure Button View
- •Creating the Detail View
- •Modifying the Disclosure Button Controller
- •Adding a Disclosure Button Controller Instance
- •Second Subcontroller: The Checklist
- •Creating the Checklist View
- •Adding a Checklist Controller Instance
- •Third Subcontroller: Controls on Table Rows
- •Creating the Row Controls View
- •Adding a Rows Control Controller Instance
- •Fourth Subcontroller: Movable Rows
- •Creating the Movable Row View
- •Adding a Move Me Controller Instance
- •Fifth Subcontroller: Deletable Rows
- •Creating the Deletable Rows View
- •Adding a Delete Me Controller Instance
- •Sixth Subcontroller: An Editable Detail Pane
- •Creating the Data Model Object
- •Creating the Detail View List Controller
- •Creating the Detail View Controller
- •Adding an Editable Detail View Controller Instance
- •But There’s One More Thing. . .
- •Breaking the Tape
- •Creating a Simple Storyboard
- •Dynamic Prototype Cells
- •Dynamic Table Content, Storyboard-Style
- •Editing Prototype Cells
- •Good Old Table View Data Source
- •Will It Load?
- •Static Cells
- •Going Static
- •So Long, Good Old Table View Data Source
- •You Say Segue, I Say Segue
- •Creating Segue Navigator
- •Filling the Blank Slate
- •First Transition
- •A Slightly More Useful Task List
- •Viewing Task Details
- •Make More Segues, Please
- •Passing a Task from the List
- •Handling Task Details
- •Passing Back Details
- •Making the List Receive the Details
- •If Only We Could End with a Smooth Transition
- •Split Views and Popovers
- •Creating a SplitView Project
- •The Storyboard Defines the Structure
- •The Code Defines the Functionality
- •The App Delegate
- •The Master View Controller
- •The Detail View Controller
- •Here Come the Presidents
- •Creating Your Own Popover
- •iPad Wrap-Up
- •Getting to Know Your Settings Bundle
- •The AppSettings Application
- •Creating the Project
- •Working with the Settings Bundle
- •Adding a Settings Bundle to Our Project
- •Setting Up the Property List
- •Adding a Text Field Setting
- •Adding an Application Icon
- •Adding a Secure Text Field Setting
- •Adding a Multivalue Field
- •Adding a Toggle Switch Setting
- •Adding the Slider Setting
- •Adding Icons to the Settings Bundle
- •Adding a Child Settings View
- •Reading Settings in Our Application
- •Retrieving User Settings
- •Creating the Main View
- •Updating the Main View Controller
- •Registering Default Values
- •Changing Defaults from Our Application
- •Keeping It Real
- •Beam Me Up, Scotty
- •Your Application’s Sandbox
- •Getting the Documents Directory
- •Getting the tmp Directory
- •File-Saving Strategies
- •Single-File Persistence
- •Multiple-File Persistence
- •Using Property Lists
- •Property List Serialization
- •The First Version of the Persistence Application
- •Creating the Persistence Project
- •Designing the Persistence Application View
- •Editing the Persistence Classes
- •Archiving Model Objects
- •Conforming to NSCoding
- •Implementing NSCopying
- •Archiving and Unarchiving Data Objects
- •The Archiving Application
- •Implementing the BIDFourLines Class
- •Implementing the BIDViewController Class
- •Using iOS’s Embedded SQLite3
- •Creating or Opening the Database
- •Using Bind Variables
- •The SQLite3 Application
- •Linking to the SQLite3 Library
- •Modifying the Persistence View Controller
- •Using Core Data
- •Entities and Managed Objects
- •Key-Value Coding
- •Putting It All in Context
- •Creating New Managed Objects
- •Retrieving Managed Objects
- •The Core Data Application
- •Designing the Data Model
- •Creating the Persistence View and Controller
- •Persistence Rewarded
- •Managing Document Storage with UIDocument
- •Building TinyPix
- •Creating BIDTinyPixDocument
- •Code Master
- •Initial Storyboarding
- •Creating BIDTinyPixView
- •Storyboard Detailing
- •Adding iCloud Support
- •Creating a Provisioning Profile
- •Enabling iCloud Entitlements
- •How to Query
- •Save Where?
- •Storing Preferences on iCloud
- •What We Didn’t Cover
- •Grand Central Dispatch
- •Introducing SlowWorker
- •Threading Basics
- •Units of Work
- •GCD: Low-Level Queueing
- •Becoming a Blockhead
- •Improving SlowWorker
- •Don’t Forget That Main Thread
- •Giving Some Feedback
- •Concurrent Blocks
- •Background Processing
- •Application Life Cycle
- •State-Change Notifications
- •Creating State Lab
- •Exploring Execution States
- •Making Use of Execution State Changes
- •Handling the Inactive State
- •Handling the Background State
- •Removing Resources When Entering the Background
- •Saving State When Entering the Background
- •A Brief Journey to Yesteryear
- •Back to the Background
- •Requesting More Backgrounding Time
- •Grand Central Dispatch, Over and Out
- •Two Views of a Graphical World
- •The Quartz 2D Approach to Drawing
- •Quartz 2D’s Graphics Contexts
- •The Coordinate System
- •Specifying Colors
- •A Bit of Color Theory for Your iOS Device’s Display
- •Other Color Models
- •Color Convenience Methods
- •Drawing Images in Context
- •Drawing Shapes: Polygons, Lines, and Curves
- •The QuartzFun Application
- •Setting Up the QuartzFun Application
- •Creating a Random Color
- •Defining Application Constants
- •Implementing the QuartzFunView Skeleton
- •Creating and Connecting Outlets and Actions
- •Implementing the Action Methods
- •Adding Quartz 2D Drawing Code
- •Drawing the Line
- •Drawing the Rectangle and Ellipse
- •Drawing the Image
- •Optimizing the QuartzFun Application
- •The GLFun Application
- •Setting Up the GLFun Application
- •Creating BIDGLFunView
- •Updating BIDViewController
- •Updating the Nib
- •Finishing GLFun
- •Drawing to a Close
- •Multitouch Terminology
- •The Responder Chain
- •Responding to Events
- •Forwarding an Event: Keeping the Responder Chain Alive
- •The Multitouch Architecture
- •The Four Touch Notification Methods
- •The TouchExplorer Application
- •The Swipes Application
- •Automatic Gesture Recognition
- •Implementing Multiple Swipes
- •Detecting Multiple Taps
- •Detecting Pinches
- •Defining Custom Gestures
- •The CheckPlease Application
- •The CheckPlease Touch Methods
- •Garçon? Check, Please!
- •The Location Manager
- •Setting the Desired Accuracy
- •Setting the Distance Filter
- •Starting the Location Manager
- •Using the Location Manager Wisely
- •The Location Manager Delegate
- •Getting Location Updates
- •Getting Latitude and Longitude Using CLLocation
- •Error Notifications
- •Trying Out Core Location
- •Updating Location Manager
- •Determining Distance Traveled
- •Wherever You Go, There You Are
- •Accelerometer Physics
- •Don’t Forget Rotation
- •Core Motion and the Motion Manager
- •Event-Based Motion
- •Proactive Motion Access
- •Accelerometer Results
- •Detecting Shakes
- •Baked-In Shaking
- •Shake and Break
- •Accelerometer As Directional Controller
- •Rolling Marbles
- •Writing the Ball View
- •Calculating Ball Movement
- •Rolling On
- •Using the Image Picker and UIImagePickerController
- •Implementing the Image Picker Controller Delegate
- •Road Testing the Camera and Library
- •Designing the Interface
- •Implementing the Camera View Controller
- •It’s a Snap!
- •Localization Architecture
- •Strings Files
- •What’s in a Strings File?
- •The Localized String Macro
- •Real-World iOS: Localizing Your Application
- •Setting Up LocalizeMe
- •Trying Out LocalizeMe
- •Localizing the Nib
- •Localizing an Image
- •Generating and Localizing a Strings File
- •Localizing the App Display Name
- •Auf Wiedersehen
- •Apple’s Documentation
- •Mailing Lists
- •Discussion Forums
- •Web Sites
- •Blogs
- •Conferences
- •Follow the Authors
- •Farewell
- •Index
286 |
CHAPTER 9: Navigation Controllers and Table Views |
The Editable Detail View
The sixth and last of our application’s subcontrollers is shown in Figure 9–8. It shows an editable detail view using a grouped table. This technique for detail views is used widely by the applications that ship on the iPhone.
Figure 9–8. The sixth and last of the Nav application’s subcontrollers implements an editable detail view using a grouped table.
We have so very much to do. Let’s get started!
The Nav Application’s Skeleton
Xcode offers a perfectly good template for creating navigation-based applications, and you will likely use it much of the time when you need to create hierarchical applications. However, we’re not going to use that template today. Instead, we’ll construct our navigation-based application from the ground up so you get a feel for how everything fits together. It’s not really much different from the way we built the tab bar controller in Chapter 7, so you shouldn’t have any problems keeping up.
In Xcode, press N to create a new project, select Empty Application from the iOS Application template list, and then click Next to continue. Set Nav as the Product Name, com.apress as the Company Identifier, and BID as the Class Prefix. Make sure that Use
www.it-ebooks.info
CHAPTER 9: Navigation Controllers and Table Views |
287 |
Core Data and Include Unit Tests are not checked, that Use Automatic Reference Counting is checked, and that Device Family is set to iPhone.
As you’ll see if you select the project navigator and open the Nav folder, this template gives you an application delegate and not much else. At this point, there are no view controllers or navigation controllers.
To make this app run, we’ll need to add a navigation controller, which includes a navigation bar. We’ll also need to add a series of views and view controllers for the navigation bar to show. The first of these views is the top-level view shown in Figure 9–2.
Each row in that top-level view is tied to a child view controller, as shown in Figures 9–3 through 9–8. Don’t worry about the specifics. You’ll see how those connections work as you make your way through the chapter.
Creating the Top-Level View Controller
In this chapter, we’re going to subclass UITableViewController instead of
UIViewController for our table views. When we subclass UITableViewController, we inherit some nice functionality from that class that will create a table view with no need for a nib file. We can provide a table view in a nib, as we did in the previous chapter, but if we don’t, UITableViewController will create a table view automatically. This table view will take up the entire space available and will connect the appropriate outlets in our controller class, making our controller class the delegate and data source for that table. When all you need for a specific controller is a table, subclassing
UITableViewController is the way to go.
We’ll create one class called BIDFirstLevelController that represents the first level in the navigation hierarchy. That’s the table that contains one row for each of the secondlevel table views. Those second-level table views will each be represented by the BIDSecondLevelViewController class. You’ll see how all this works as you make your way through the chapter.
In your project window, select the Nav folder in the project navigator, and then press N or select File New New File…. When the new file assistant comes up, select Cocoa Touch, select Objective-C class, and then click Next. On the next screen, enter
BIDFirstLevelController in the Class field, and type UITableViewController in the
Subclass of field. As always, be sure to check your spelling carefully before you click Next. Then make sure the Nav folder or group is selected in the file browser, Group, and Target controls before clicking Create.
You may have noticed an entry named UIViewController in the file template selector. That option provides you with a number of empty “stub” methods as a starting point to build a view controller, and even lets you pick a subclass of UIViewController, such as UITableViewController, with even more empty methods just waiting for you to plug in additional functionality. When creating your own applications, feel free to use those templates. We didn’t use any of the view controller templates here, so we wouldn’t need to spend time sorting through all the unneeded template methods, working out where to
www.it-ebooks.info
288 |
CHAPTER 9: Navigation Controllers and Table Views |
insert or delete code. By creating a plain Objective-C object, and simply setting its superclass to UITableViewController, we get a smaller, more manageable file.
Once the files have been created, single-click BIDFirstLevelController.h, and take a look at it.
#import <UIKit/UIKit.h>
@interface BIDFirstLevelController : UITableViewController
@end
Since the class we chose to subclass from is a UIKit class, Xcode handily imported UIKit instead of just Foundation. The two files we just created contain the controller class for the top-level view, as shown in Figure 9–2. Our next step is to set up our navigation controller.
Setting Up the Navigation Controller
Our goal here is to edit the application delegate to add our navigation controller’s view to the application window.
Let’s start by editing BIDAppDelegate.h to add a property, navController, to point to our navigation controller:
#import <UIKit/UIKit.h>
@interface BIDAppDelegate : UIResponder <UIApplicationDelegate>
@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navController;
@end
Next, we need to hop over to the implementation file, where we’ll import a header for the view controller class we just created and add the @synthesize statement for navController. In the application:didFinishLaunchingWithOptions: method, we’ll create navController, set it up with the initial view controller that it’s going to display, and add its view as a subview of our application’s window so that it is shown to the user. We’ll explain each of those steps in a moment. For now, select BIDAppDelegate.m, and make the following changes:
#import "BIDAppDelegate.h"
#import "BIDFirstLevelController.h"
@implementation BIDAppDelegate
@synthesize window = _window;
@synthesize navController;
#pragma mark -
#pragma mark Application lifecycle
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
www.it-ebooks.info
CHAPTER 9: Navigation Controllers and Table Views |
289 |
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; // Override point for customization after application launch
BIDFirstLevelController *first = [[BIDFirstLevelController alloc] initWithStyle:UITableViewStylePlain];
self.navController = [[UINavigationController alloc] initWithRootViewController:first];
[self.window addSubview:navController.view];
self.window.backgroundColor = [UIColor whiteColor]; [self.window makeKeyAndVisible];
return YES;
}
.
.
.
@end
The lines we added to the application:didFinishLaunchingWithOptions: method deserve some attention. The first thing we did was to create an instance of
BIDFirstLevelController.
BIDFirstLevelController *first = [[BIDFirstLevelController alloc] initWithStyle:UITableViewStylePlain];
Since BIDFirstLevelController is a subclass of UITableViewController, it can use the methods defined there, including the handy initWithStyle: method, which lets you create a controller whose table view will be either plain or grouped (whichever you choose), without the need for a nib file. Many iOS applications are built around table views whose appearance is dictated entirely by the cells they contain, and don’t need any nib customization for the table views themselves. Therefore, using the initWithStyle: method is a common shortcut for instantiating table view controllers without much ado.
Next, we created an instance of the navigation controller.
self.navController = [[UINavigationController alloc] initWithRootViewController:first];
Here, we see that UINavigationController, like UITableViewController, has its own special initializer. Here, the initWithRootViewController: method lets us pass in the top-level controller that the navigation control should use to display its initial content—in this case, the BIDFirstLevelController referenced by the first variable.
Finally, we added navController’s view to our window in order to display it.
[self.window addSubview:navController.view];
It’s worth taking a moment to think about this. What exactly is the view we’re passing with the addSubview: method? It’s a composite view provided by the navigation controller, which contains a combination of two things: the navigation bar at the top of the screen (which usually contains some sort of title and often a back button of some kind on the left),
www.it-ebooks.info
290 |
CHAPTER 9: Navigation Controllers and Table Views |
and the content of whatever the navigation controller’s current view controller wants to display. In our case, the lower part of the display will be filled with the table view associated with the BIDFirstLevelController instance we created a few lines ago.
You’ll learn more about how to control what the navigation controller shows in the navigation bar as we go forward. You’ll also gain an understanding of how the navigation controller shifts focus from one subordinate view controller to another. For now, we’ve laid enough groundwork here that we can start defining what our own custom view controllers are going to do.
Now, we need a list of rows for our BIDFirstLevelController to display. In the previous chapter, we used simple arrays of strings to populate our table rows. In this application, the first-level view controller will manage a list of its subcontrollers, which we will be building throughout the chapter.
When we were designing this application, we decided that we wanted our first-level view controller to display an icon to the left of each of its subcontroller names. Instead of adding a UIImage property to every subcontroller, we’ll create a subclass of UITableViewController that has a UIImage property to hold the row icon. We will then subclass this new class instead of subclassing UITableViewController directly. As a result, all of our subclasses will get that UIImage property for free, which will make our code much cleaner.
NOTE: We will never actually create an instance of our new UITableViewController subclass. It exists solely to let us add a common item to the rest of the controllers we’re going to write. In many languages, we would declare this as an abstract class, but Objective-C doesn’t include any syntax to support abstract classes. We can make classes that aren't intended to be
instantiated, but the Objective-C compiler won't actually prevent us from writing code that creates instances of such a class, the way that the compilers for many other languages might. Objective-C is much more permissive than most other popular languages, and this can be a little
hard to get used to.
Single-click the Nav folder in Xcode, and then press N to bring up the new file assistant. Select Cocoa Touch from the left pane, select Objective-C class, and click Next. On the next screen, name the new class BIDSecondLevelViewController, and enter UITableViewController for Subclass of. Then click Next again, and go on and save the class files as usual. Once the new files are created, select
BIDSecondLevelViewController.h, and make the following changes:
#import <UIKit/UIKit.h>
@interface BIDSecondLevelViewController : UITableViewController
@property (strong, nonatomic) UIImage *rowImage;
@end
Over in BIDSecondLevelViewController.m, add the following line of code:
www.it-ebooks.info
CHAPTER 9: Navigation Controllers and Table Views |
291 |
#import "BIDSecondLevelViewController.h"
@implementation BIDSecondLevelViewController
@synthesize rowImage;
@end
Any controller class that we want to implement as a second-level controller—in other words, any controller that the user can navigate to directly from the first table shown in our application—should subclass BIDSecondLevelViewController instead of
UITableViewController. Because we’re subclassing BIDSecondLevelViewController, all of those classes will have a property they can use to store a row image, and we can write our code in BIDFirstLevelController before we’ve actually written any concrete second-level controller classes by using BIDSecondLevelViewController as a placeholder.
Let’s implement our BIDFirstLevelController class now. Be sure to save the changes you made to BIDSecondLevelViewController. Then make these changes to
BIDFirstLevelController.h:
#import <UIKit/UIKit.h>
@interface BIDFirstLevelController : UITableViewController
@property (strong, nonatomic) NSArray *controllers;
@end
The array we just added will hold the instances of the second-level view controllers. We’ll use it to feed data to our table.
Add the following code to BIDFirstLevelController.m, and then come on back and gossip with us, ’K?
#import "BIDFirstLevelController.h"
#import "BIDSecondLevelViewController.h"
@implementation BIDFirstLevelController
@synthesize controllers;
-(void)viewDidLoad { [super viewDidLoad];
self.title = @"First Level";
NSMutableArray *array = [[NSMutableArray alloc] init];
self.controllers = array;
}
-(void)viewDidUnload { [super viewDidUnload]; self.controllers = nil;
}
#pragma mark -
#pragma mark Table Data Source Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.controllers count];
}
www.it-ebooks.info
292 |
CHAPTER 9: Navigation Controllers and Table Views |
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *FirstLevelCell = @"FirstLevelCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: FirstLevelCell];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier: FirstLevelCell];
}
// Configure the cell
NSUInteger row = [indexPath row]; BIDSecondLevelViewController *controller =
[controllers objectAtIndex:row]; cell.textLabel.text = controller.title; cell.imageView.image = controller.rowImage;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; return cell;
}
#pragma mark -
#pragma mark Table View Delegate Methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
BIDSecondLevelViewController *nextController = [self.controllers objectAtIndex:row];
[self.navigationController pushViewController:nextController animated:YES];
}
@end
First, notice that we’ve imported that new BIDSecondLevelViewController.h header file. Doing that lets us use the BIDSecondLevelViewController class in our code so that the compiler will know about the rowImage property.
Next comes the viewDidLoad method. The first thing we do is set self.title. A navigation controller knows what to display in the title of its navigation bar by asking the currently active controller for its title. Therefore, it’s important to set the title for all controller instances in a navigation-based application, so the users know where they are at all times.
We then create a mutable array and assign it to the controllers property we declared earlier. Later, when we’re ready to add rows to our table, we will add view controllers to this array, and they will show up in the table automatically. Selecting any row will automatically cause the corresponding controller’s view to be presented to the user.
www.it-ebooks.info
CHAPTER 9: Navigation Controllers and Table Views |
293 |
TIP: Did you notice that our controllers property is declared as an NSArray, but that we’re creating an NSMutableArray? It’s perfectly acceptable to assign a subclass to a property like this. In this case, we use the mutable array in viewDidLoad to make it easier to add new controllers in an iterative fashion, but we leave the property declared as an immutable array as a message to other code that it shouldn’t be modifying this array.
The final piece of the viewDidLoad method is the call to [super viewDidLoad]. We do this because we are subclassing UITableViewController. You should always call [super viewDidLoad] when you override the viewDidLoad method, because there’s no way to know if your parent class does something important in its own viewDidLoad method.
The tableView:numberOfRowsInSection: method here is identical to ones you’ve seen before. It simply returns the count from our array of controllers. The tableView:cellForRowAtIndexPath: method is also very similar to ones we’ve written in the past. It gets a dequeued cell, or creates a new one if none exists, and then grabs the controller object from the array corresponding to the row being asked about. It then sets the cell’s textLabel and image properties using the title and rowImage from that controller. Note that in this case, since we are using one of UITableViewCell’s built-in styles instead of laying out a subclass of our own in a nib file, we have no nib file to register with the table view, and therefore can’t rely on the dequeue... method returning anything. So, we need to include the check for nil and the resulting cell-creation code, as you’ve seen before.
Notice that we are assuming the object retrieved from the array is an instance of BIDSecondLevelViewController and are assigning the controller’s rowImage property to a UIImage. This step will make more sense when we declare and add the first concrete second-level controller to the array.
The last method we added is the most important one here, and it’s the only functionality that’s truly new. You’ve seen the tableView:didSelectRowAtIndexPath: method before—it’s the one that is called after a user taps a row. If tapping a row needs to trigger a drill-down, this is how we do it. First, we get the row from indexPath.
NSUInteger row = [indexPath row];
Next, we grab the correct controller from our array that corresponds to that row.
BIDSecondLevelViewController *nextController = [self.controllers objectAtIndex:row];
Then we use our navigationController property, which points to our application’s navigation controller, to push the next controller—the one we pulled from our array— onto the navigation controller’s stack.
[self.navigationController pushViewController:nextController animated:YES];
That’s really all there is to it. Each controller in the hierarchy needs to know only about its children. When a row is selected, the active controller is responsible for getting or creating a new subcontroller, setting its properties if necessary (it’s not necessary here),
www.it-ebooks.info