In the RootViewController , you will implement code to retrieve the products array from the DBAccess class and display it in the TableView.. In the header, RootViewController.h , add imp
Trang 1each time, but parameterized queries offer the performance advantage of preparing and compiling the statement once and the statement being cached for reuse
Writing to the Database
If you modify the sample application, or create your own SQLite application that attempts to write to the database, you will have a problem The version of the database that you are using in the sample code is located in the application bundle, but the application bundle is read - only, so attempting to write to this database will result in an error
To be able to write to the database, you need to make an editable copy On the iPhone, this editable copy should be placed in the documents directory Each application on the iPhone is “ sandboxed ” and has access only to its own documents directory
The following code snippet shows how to check to see if a writable database already exists, and if not, create an editable copy
// Create a writable copy of the default database from the bundle // in the application Documents directory.
- (void) createEditableDatabase { // Check to see if editable database already exists BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
NSString *writableDB = [documentsDir stringByAppendingPathComponent:@”ContactNotes.sqlite”];
success = [fileManager fileExistsAtPath:writableDB];
// The editable database already exists
if (success) return;
// The editable database does not exist // Copy the default DB to the application Documents directory.
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:@”Catalog.sqlite”];
success = [fileManager copyItemAtPath:defaultPath toPath:writableDB error: & error];
if (!success) { NSAssert1(0, @”Failed to create writable database file:’%@’.”, [error localizedDescription]);
} }
You would then need to change your database access code to call this function and then refer to the editable copy of the database instead of the bundled copy
Trang 2Displaying the Catalog
Now that you have the DBAccess class done, you can get on with displaying the catalog In the
RootViewController , you will implement code to retrieve the products array from the DBAccess
class and display it in the TableView
In the header, RootViewController.h , add import statements for the Product and DBAccess
classes:
#import “Product.h”
#import “DBAccess.h”
Add a property and its backing instance variable to hold your products array:
@interface RootViewController : UITableViewController {
// Instance variable to hold products array
NSMutableArray *products;
}
@property (retain,nonatomic) NSMutableArray* products;
Notice that the products property is declared with the retain attribute This means that the
property setter method will call retain on the object that is passed in You must specify the retain
attribute to ensure that the products array is available for use when you need it Recall that in the
DBAccess class, you called autorelease on the array that is returned from the getAllProducts
method If you did not add the retain attribute, the products array would be released on the next
pass through the application event loop Trying to access the products property after the array is
released would cause the application to crash You avoid this by having the setter call retain on
the array
In the RootViewController.m implementation class, you need to synthesize the products property
below the @implementation line:
@synthesize products;
Synthesizing a property causes the compiler to generate the setter and getter methods for the
property You can alternatively defi ne the getter, setter, or both methods yourself The compiler will
fi ll in the blanks by defi ning either of these methods if you don ’ t
Continuing in the RootViewController.m implementation class, you ’ ll add code to the viewDidLoad
method to get your products array from the DBAccess class:
- (void)viewDidLoad {
[super viewDidLoad];
// Get the DBAccess object;
DBAccess *dbAccess = [[DBAccess alloc] init];
// Get the products array from the database
self.products = [dbAccess getAllProducts];
Trang 3
// Close the database because we are finished with it [dbAccess closeDatabase];
// Release the dbAccess object to free its memory [dbAccess release];
}
RootViewController.m
Next, you have to implement your TableView methods The fi rst thing you have to do is tell the
TableView how many rows there are by implementing the numberOfRowsInSection method as you did in the previous chapter You will get the count of items to display in the TableView from your products array:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.products count];
}
RootViewController.m
Finally, you have to implement cellForRowAtIndexPath to provide the TableView with the
TableViewCell that corresponds with the row that the TableView is asking for The code is similar
to the example from the previous chapter The difference is that now you get the text for the cell from the Product object You look up the Product object using the row that the TableView is asking for as the index into the array The following is the cellForRowAtIndexPath method:
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) { cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
} // Configure the cell.
// Get the Product object Product* product = [self.products objectAtIndex:[indexPath row]];
cell.textLabel.text = product.name;
return cell;
}
RootViewController.m
Trang 4To set the Catalog text at the top in the Navigation Controller, double
click MainWindow.xib Then, click on the navigation item in Interface
Builder and set the Title property to Catalog
You should now be able to build and run the application
When you run it, you should see the product catalog as in
Figure 2 - 15 When you touch items in the catalog, nothing happens,
but the application should display the details of the product
that is touched To accomplish this, the TableView function
do that, you need to build the view that you will display when a user
taps a product in the table
Viewing Product Details
When a user taps a product in the table, the application should
navigate to a product detail screen Because this screen will be used
with the NavigationController , it needs to be implemented using a
UIViewController In Xcode, add a new UIViewController subclass
to your project called ProductDetailViewController Make sure that
you select the option “ With XIB for user interface ” This creates your
class along with a NIB fi le for designing the user interface for the class
In the ProductDetailViewController.h header, add Interface Builder outlets for the data that you
want to display You need to add an outlet for each label control like this:
IBOutlet UILabel* nameLabel;
IBOutlet UILabel* manufacturerLabel;
IBOutlet UILabel* detailsLabel;
IBOutlet UILabel* priceLabel;
IBOutlet UILabel* quantityLabel;
IBOutlet UILabel* countryLabel;
You will use the outlets in the code to link to the UI widgets created in Interface Builder The use of
Interface Builder is beyond the scope of this book There are several good books on building user
interfaces for the iPhone including iPhone SDK Programming: Developing Mobile Applications for
Apple iPhone and iPod Touch by Maher Ali (Wiley, 2009)
You will transfer the data about the product that the user selected to the detail view by passing
a Product object from the RootViewController to the ProductDetailViewController Because
you will be referencing the Product object in your code, you need to add an import statement
for the Product class to the ProductDetailViewController header You also need to add the
Catalog application
Trang 5method signature for the setLabelsForProduct method that will be used to receive the Product object from the RootViewController and set the text of the labels The complete header should look like this:
#import < UIKit/UIKit.h >
#import “Product.h”
@interface ProductDetailViewController : UIViewController {
IBOutlet UILabel* nameLabel;
IBOutlet UILabel* manufacturerLabel;
IBOutlet UILabel* detailsLabel;
IBOutlet UILabel* priceLabel;
IBOutlet UILabel* quantityLabel;
IBOutlet UILabel* countryLabel;
} -(void) setLabelsForProduct: (Product*) theProduct;
@end
ProductDetailViewController.h
Now, open up the ProductDetailViewController.xib fi le in Interface Builder Here you will add a series of UILabels to the interface as you designed in the mockup Your interface should look something like Figure 2 - 16
Next, you ’ ll need to hook up your labels in Interface Builder to the outlets that you created in the
ProductDetailViewController.h header fi le Make sure that you save the header fi le before you try to hook up the outlets or the outlets that you created in the header will not be available in Interface Builder
To hook up the outlets, bring up the
ProductDetailViewController.xib fi le in Interface Builder
Select File ’ s Owner in the Document window Press Cmd+2 to bring up the Connections Inspector In this window, you can see all of the class ’ s outlets and their connections Click and drag from the open circle on the right side of the outlet name to the control to which that outlet should be connected in the user interface You should see the name of the control displayed on the right side of the Connections Inspector as in Figure 2 - 17
Trang 6In ProductDetailViewController.m , implement setLabelsForProduct to set the text in the
labels:
-(void) setLabelsForProduct: (Product*) theProduct
{
// Set the text of the labels to the values passed in the Product object
[nameLabel setText:theProduct.name];
[manufacturerLabel setText:theProduct.manufacturer];
[detailsLabel setText:theProduct.details];
[priceLabel setText:[NSString stringWithFormat:@”%.2f”,theProduct.price]];
[quantityLabel setText:[NSString stringWithFormat:@”%d”,
theProduct.quantity]];
[countryLabel setText:theProduct.countryOfOrigin];
}
ProductDetailViewController.m
This code accepts a Product object and uses its properties to set the text that is displayed in the
interface labels
In order to be able to navigate to your new screen, you need to add code to the RootViewController
to display this screen when a user selects a product
Trang 7In the RootViewController header, you must add an import for ProductDetailViewController because you will create an instance of this class to push onto the navigation stack:
#import “ProductDetailViewController.h”
In the RootViewController implementation, add code to the tableView:
didSelectRowAtIndexPath: method to instantiate a ProductDetailViewController , populate the data, and push it onto the navigation stack:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// Get the product that corresponds with the touched cell Product* product = [self.products objectAtIndex:[indexPath row]];
// Initialize the detail view controller from the NIB ProductDetailViewController *productDetailViewController = [[ProductDetailViewController alloc]
initWithNibName:@”ProductDetailViewController” bundle:nil];
// Set the title of the detail page [productDetailViewController setTitle:product.name];
// Push the detail controller on to the stack [self.navigationController
pushViewController:productDetailViewController animated:YES];
// Populate the details [productDetailViewController setLabelsForProduct:product];
// release the view controller becuase it is retained by the // Navigation Controller
[productDetailViewController release];
}
ProductDetailViewController.m
That ’ s all there is to it Now you should be able to build and run your application Try tapping on
an item in the catalog The application should take you to the detail page for that item Tapping the Catalog button in the navigation bar should take you back to the catalog Tapping another row in the TableView should take you to the data for that item
You now have a fully functioning catalog application! I know that it doesn ’ t look very nice, but you ’ ll work on that in the next chapter where you dive into customizing the UITableView
MOVING FORWARD
Now that you have a functioning application, feel free to play with it as much as you like! Spruce up the interface or add additional fi elds to the database tables and Product class
Trang 8There are a lot of good books on the SQL language, so if you were confused by any of the SQL used
in this chapter, it may be a good idea to pick up a copy of SQL For Dummies by Allen Taylor
Another resource that you should be aware of is the SQLite web site at http://www.sqlite.org/
There you will fi nd extensive documentation of the database and the C language APIs that are used
to access the database
Although you ’ ve learned how to get your data out of SQLite and into an iPhone application, the
catalog doesn ’ t look that great In the next chapter, you will learn how to display your data with
more fl air by customizing the TableView
Trang 9Displaying Your Data:
The UITableView
WHAT ’ S IN THIS CHAPTER?
Customizing the TableView by creating your own TableView cells Searching and fi ltering your result sets
Adding important UI elements to your tables such as indexes and section headers
Avoiding and troubleshooting performance issues with your TableViews
The focus of the book thus far has been on how to get your data on to the iPhone and how to access that data on the device This chapter focuses on how you can enhance the display of your data by customizing the TableViewCell It also examines how to make your data easier
to use by adding an index, section headings, and search functionality to the UITableView In the next several sections, you take a closer look at how to use the TableView to display your data in ways that make it more useful to your target audience
CUSTOMIZING THE TABLEVIEW
You begin by taking a look at the default TableView styles that are available in the iPhone SDK
Then, you learn the technique of adding subviews to the content view of a TableViewCell In the event that neither of these solutions meets your needs for customizing the display of your data, you will examine how to design your own TableViewCell from scratch using Interface Builder If you had trouble with IB in the previous chapter, now is a good time to review Apple ’ s documentation on using IB, which you can fi nd at http://developer.apple.com/
➤
➤
➤
➤
3
Trang 10TableViewCell Styles
Several pre - canned styles are available to use for a TableViewCell :
UITableViewCellStyleDefault : This style displays a cell with a black, left - aligned text label with an optional image view
UITableViewCellStyleValue1 : This style displays a cell with a black, left - aligned text label
on the left side of the cell and an additional blue text, right - aligned label on the right side
UITableViewCellStyleValue2 : This style displays a cell with a blue text, right - aligned label on the left side of the cell and an additional black, left - aligned text label on the right side of the cell
UITableViewCellStyleSubtitle : This style displays a cell with a black, left - aligned text label across the top with a smaller, gray text label below
In each style, the larger of the text labels is defi ned by the textLabel property and the smaller is
defi ned by the detailTextLabel property
Let ’ s change the Catalog application from the last chapter to see what each of these styles looks
like You will change the application to use the name of the part manufacturer as the subtitle
In the RootViewController.m implementation fi le, add a line to the tableView:cellForRowAt
IndexPath: method that sets the cell ’ s detailTextLabel text property to the product ’ s manufacturer:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @”Cell”;
UITableViewCell *cell = [tableView
dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell.
// Get the Product object
Product* product = [self.products objectAtIndex:[indexPath row]];
cell.textLabel.text = product.name;
cell.detailTextLabel.text = product.manufacturer;
return cell;
}
RootViewController.m
When you run the application, you will see something like Figure 3 - 1 (a) Nothing has changed You
may be wondering what happened to the subtitle When you initialized the cell, the style was set to
UITableViewCellStyleDefault In this style, only the cell ’ s textLabel is displayed, along with an
optional image, which you will add in a moment
➤
➤
➤
➤