1. Trang chủ
  2. » Công Nghệ Thông Tin

Phát triển ứng dụng cho iPhone và iPad - part 24 potx

10 274 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 10
Dung lượng 2,77 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

[self.navigationController pushViewController:elc animated:YES]; [elc release]; break; } case 4: { UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:@”Hi-Pri Tasks” message:ni

Trang 1

// Create a date formatter to format the date from the picker NSDateFormatter* df = [[NSDateFormatter alloc] init];

[df setDateStyle:NSDateFormatterLongStyle];

cell.textLabel.text = [df stringFromDate:datePicker.date ];

[df release];

} return cell;

}

EditDateController.m

You can see that you are once again using an NSDateFormatter to convert the NSDate object into a string for display in the TableViewCell

In the dealloc method, release the member variables that correspond to the class properties:

- (void)dealloc { [managedTaskObject release];

[managedObjectContext release];

[datePicker release];

[tv release];

[super dealloc];

}

EditDateController.m

Finishing Up the Editing Controllers

You have now fi nished implementing all of the edit controllers The last thing that you need to do before you are ready to run the program is go back and add code to the ViewTaskController.m to use the new subcontrollers to edit the task data

In ViewTaskController.m , add an import statement for each subcontroller:

#import “EditTextController.h”

#import “EditPriorityController.h”

#import “EditDateController.h”

#import “EditLocationController.h”

ViewTaskController.m

You will also need to add an import for the App delegate because you need to get a reference to the managedObjectModel in order to use your stored fetch request:

#import “TasksAppDelegate.h”

You can now implement the didSelectRowAtIndexPath method that you left out when you were implementing the ViewTaskController earlier in the chapter This method runs when a user selects

a row in the table The method should display the correct edit View Controller based on which row the user selects

Trang 2

200 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION

The last two buttons in the table do not use edit View Controllers The Hi - Pri Tasks button

demonstrates how to use a fetched property to get a list of high - priority tasks The “ Tasks due

sooner ” button shows you how to use a stored fetch request

The following is the code for didSelectRowAtIndexPath :

- (void)tableView:(UITableView *)tableView

didSelectRowAtIndexPath:(NSIndexPath *)indexPath {

// Deselect the currently selected row according to the HIG

[tableView deselectRowAtIndexPath:indexPath animated:NO];

// Based on the selected row, choose which controlelr to push

switch (indexPath.row) {

case 0:

{

EditTextController* etc = [[EditTextController alloc]

initWithStyle:UITableViewStyleGrouped];

etc.managedObject = self.managedTaskObject;

etc.keyString=@”text”;

etc.managedObjectContext = self.managedObjectContext;

[self.navigationController pushViewController:etc animated:YES];

[etc release];

break;

}

case 1:

{

EditPriorityController* epc =

[[EditPriorityController alloc]

initWithStyle:UITableViewStyleGrouped];

epc.managedTaskObject = self.managedTaskObject;

epc.managedObjectContext = self.managedObjectContext;

[self.navigationController pushViewController:epc animated:YES];

[epc release];

break;

}

case 2:

{

EditDateController* edc = [[EditDateController alloc] init];

edc.managedTaskObject = self.managedTaskObject;

edc.managedObjectContext = self.managedObjectContext;

[self.navigationController pushViewController:edc animated:YES];

[edc release];

break;

}

case 3:

{

EditLocationController* elc = [[EditLocationController alloc] init];

elc.managedObjectContext = self.managedObjectContext;

elc.managedTaskObject = self.managedTaskObject;

Trang 3

[self.navigationController pushViewController:elc animated:YES];

[elc release];

break;

} case 4:

{ UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:@”Hi-Pri Tasks”

message:nil delegate:self cancelButtonTitle:@”OK”

otherButtonTitles:nil ] autorelease];

// Use Fetched property to get a list of high-pri tasks NSArray* highPriTasks = managedTaskObject.highPriTasks;

NSMutableString* alertMessage = [[[NSMutableString alloc] init] autorelease];

// Loop through each hi-pri task to create the string for // the message

for (Task * theTask in highPriTasks) {

[alertMessage appendString:theTask.text];

[alertMessage appendString:@”\n”];

} alert.message = alertMessage;

[alert show];

break;

} case 5:

{ UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:@”Tasks due sooner”

message:nil delegate:self cancelButtonTitle:@”OK”

otherButtonTitles:nil ] autorelease];

NSMutableString* alertMessage = [[[NSMutableString alloc] init] autorelease];

// need to get a handle to the managedObjectModel to use the stored // fetch request

TasksAppDelegate* appDelegate = [UIApplication sharedApplication].delegate;

NSManagedObjectModel* model = appDelegate.managedObjectModel;

// Get the stored fetch request NSDictionary* dict =

[[NSDictionary alloc]

Trang 4

202 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION

initWithObjectsAndKeys:managedTaskObject.dueDate,

@”DUE_DATE”,nil];

NSFetchRequest* request =

[model fetchRequestFromTemplateWithName:@”tasksDueSooner”

substitutionVariables:dict];

[dict release];

NSError* error;

NSArray* results =

[managedObjectContext executeFetchRequest:request error: & error];

// Loop through eachtask to create the string for the message

for (Task * theTask in results)

{

[alertMessage appendString:theTask.text];

[alertMessage appendString:@”\n”];

}

alert.message = alertMessage;

[alert show];

break;

}

default:

break;

}

}

ViewTaskController.m

In the code for this method, cases 0 through 3 are very similar Each creates an instance of the

appropriate edit controller, populates the necessary properties, and then pushes the controller onto

the navigation stack

Case 4 implements the Hi - Pri Tasks feature, which uses the highPriTasks fetched property of the

Task object that you defi ned in the previous chapter If you don ’ t remember, this property simply

returns a list of all tasks that are marked as High Priority

The interesting thing to notice about using the fetched property is that it returns an array of objects

instead of a set You can also see that using a fetched property is as simple as using a regular

property The code loops through each Task object returned from the fetched property and appends

the Task name to a string The code then displays the string using a UIAlertView

Case 5 uses a stored fetch request to get a list of tasks that are due sooner than the current task

There are a couple of points of interest when using a stored fetch request First, you need a reference

to the managed object model because stored fetch requests reside in the model and not in your

managed object class

Next, if you specifi ed substitution variables in the fetch request, you need to provide them to the

fetch request in an NSDictionary that contains the objects and the keys You can see that you are

Trang 5

creating an NSDictionary using the dueDate property of the current Task object and the key text DUE_DATE The key text is the same as the variable name that you specifi ed in the previous chapter when defi ning the stored fetch request

The code then creates an NSFetchRequest It uses the fetchRequestFromTemplateWithName:

method by supplying the name of the stored fetch request tasksDueSooner and the NSDictionary containing your substitution variables and keys

The code then executes the fetch request against the context Finally, the code iterates over the results, creating a string with the text from each returned Task object, and displays the string using

a UIAlertView You are now ready to build and run the application You should get a clean build with no errors

or warnings You should be able to add new tasks and edit all of the attributes of your tasks You should also be able to create new locations and delete existing locations Clicking the “ hi - pri tasks ” button in the task viewer will use the fetched property to display all of your high - priority tasks The “ Tasks due sooner ” feature won ’ t quite work yet because you have to implement date defaulting in the Task object If you try to select “ Tasks due sooner ” and all of your tasks do not have due dates, you will get an error

DISPLAYING RESULTS IN THE ROOTVIEWCONTROLLER

In this section, you are going to implement the fi ltering and sorting buttons on the RootViewController The easiest way to implement this functionality is to modify the sort descriptor or predicate of the fetched results controller, and then execute the fetch

Sorting Results with NSSortDescriptor

When the user taps the Asc or Dsc buttons, the toolbarSortOrderChanged method will run

In this method, you get a reference to the fetch request used by the fetched results controller and change the sort descriptor to match the sort order that the user selected Then, you need to tell the fetchedResultsController to perform the fetch with the revised sort descriptor Finally, you tell the TableView to reload its data The following is the code for the toolbarSortOrderChanged method:

-(IBAction)toolbarSortOrderChanged:(id)sender;

{ NSLog(@”toolbarSortOrderChanged”);

// Get the fetch request from the controller and change the sort descriptor NSFetchRequest* fetchRequest = self.fetchedResultsController.fetchRequest;

// Edit the sort key based on which button was pressed BOOL ascendingOrder = NO;

UIBarButtonItem* button = (UIBarButtonItem*) sender;

if ([button.title compare:@”Asc”]== NSOrderedSame) ascendingOrder=YES;

else ascendingOrder=NO;

Trang 6

204 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION

NSSortDescriptor *sortDescriptor =

[[NSSortDescriptor alloc] initWithKey:@”text” ascending:ascendingOrder];

NSArray *sortDescriptors =

[[NSArray alloc] initWithObjects:sortDescriptor, nil];

[sortDescriptor release];

[fetchRequest setSortDescriptors:sortDescriptors];

[sortDescriptors release];

NSError *error = nil;

if (![[self fetchedResultsController] performFetch: & error]) {

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

}

[taskTableView reloadData];

}

RootViewController.m

You use the same method regardless of which sort button the user tapped At runtime, the code

checks the title of the button to determine if the user wants the data sorted in ascending or

descending order Then, you use the compare method of the NSString class to perform the sort

You can also add additional sort descriptors to sort your data using multiple fi elds Core Data

applies the sort descriptors to the results in the order that you specify them in the array that

you pass into the setSortDescriptors function You will learn more about implementing sort

descriptors in the next chapter

Filtering Results with NSPredicate

When the user taps the Hi Pri button, your code should fi lter the list of tasks to show only high

priority tasks Conversely, when the user selects the All button, you need to clear the fi lter to show

all of the tasks again You can build these features as you did with the sorting functionality in the

last section However, instead of modifying the sort descriptor, you will be modifying the fetch

request ’ s predicate You can use predicates to fi lter data in all sorts of data structures Their use is

not limited to Core Data You learn more about predicates in the next chapter

The toolbarFilterHiPri method needs to set the predicate used by the fetch request to return

tasks with a priority of 3 Then, the method has to tell the TableView to reload its data The

following is the code for the toolbarFilterHiPri method:

-(IBAction)toolbarFilterHiPri:(id)sender{

NSLog(@”toolbarFilterHiPri”);

// Change the fetch request to display only high pri tasks

// Get the fetch request from the controller and change the predicate

NSFetchRequest* fetchRequest = self.fetchedResultsController.fetchRequest;

NSPredicate *predicate = [NSPredicate predicateWithFormat:@”priority == 3”];

[fetchRequest setPredicate:predicate];

Trang 7

NSError *error = nil;

if (![[self fetchedResultsController] performFetch: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

} [taskTableView reloadData];

}

RootViewController.m

The code gets a reference to the predicate used by the fetchedResultsController Then, you create a new predicate with the criteria of priority == 3 Next, you set the predicate of the fetch request to your new predicate, perform the fetch, and tell the table to reload its data

The toolbarFilterAll method simply removes the predicate from the fetch request You do that

by setting the predicate to nil , as follows:

-(IBAction)toolbarFilterAll:(id)sender {

NSLog(@”toolbarFilterAll”);

// Change the fetch request to display all tasks // Get the fetch request from the controller and change the predicate NSFetchRequest* fetchRequest = self.fetchedResultsController.fetchRequest;

// nil out the predicate to clear it and show all objects again [fetchRequest setPredicate:nil];

NSError *error = nil;

if (![[self fetchedResultsController] performFetch: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

} [taskTableView reloadData];

}

RootViewController.m

This looks very similar to the toolbarFilterHiPri method except that here, you set the predicate

to nil instead of creating a new predicate to apply to the fetchRequest Removing the predicate effectively un - fi lters the data

GENERATING GROUPED TABLES USING

THE NSFETCHEDRESULTSCONTROLLER

In Chapter 3, you learned how to use the UILocalizedIndexedCollation class to create a TableView that organized data within sections When you work with Core Data, you can use the NSFetchedResultsController to achieve the same results In this section, you will build the

Trang 8

206 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION

LocationTasksViewController The application displays the LocationTasksViewController

when the user selects the Location button on the RootViewController The

LocationTasksViewController displays all of the tasks grouped by location

The fi rst step is to create a new UITableviewController without XIB called

LocationTasksViewController Modify the header fi le by adding instance variables and

properties for the context and a fetched results controller Also, mark your class as implementing

the NSFetchedResultsControllerDelegate protocol The header should look like Listing 7 - 7

#import < UIKit/UIKit.h >

@interface LocationTasksViewController :

UITableViewController < NSFetchedResultsControllerDelegate >

NSManagedObjectContext *managedObjectContext;

NSFetchedResultsController *fetchedResultsController;

}

@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;

@property (nonatomic, retain)

NSFetchedResultsController *fetchedResultsController;

@end

Moving into the implementation fi le, add #import directives for the Location.h , Task.h , and

ViewTaskController.h headers:

#import “Location.h”

#import “Task.h”

#import “ViewTaskController.h”

LocationTasksViewController.m

Next, synthesize the properties that you declared in the header:

@synthesize managedObjectContext,fetchedResultsController;

Now, implement viewDidLoad to perform the fetch on the fetched results controller and set the title

of the screen in the nav bar:

- (void)viewDidLoad {

[super viewDidLoad];

NSError* error;

if (![[self fetchedResultsController] performFetch: & error]) {

NSLog(@”Unresolved error %@, %@”, error, [error userInfo]);

abort();

Trang 9

} // set the title to display in the nav bar self.title = @”Tasks by Location”;

}

LocationTasksViewController.m

In viewDidUnload , set the properties that you declared in the header to nil :

- (void)viewDidUnload { self.managedObjectContext=nil;

self.fetchedResultsController = nil;

[super viewDidUnload];

}

LocationTasksViewController.m

Next, you will write the fetchedResultsController accessor method You have implemented this method several times before The difference in this case is that you will need to specify a sectionNameKeyPath when you initialize the NSFetchedResultsController

The sectionNameKeyPath parameter allows you to specify a key path that the fetched results controller will use to generate the sections for your table The fetched results controller will contain the entire set of Task objects You want the tasks grouped by location Remember that the Task object has a location property that refers to a related Location object The Location object has

a name property that contains the name of the Location You really want the tasks grouped by the name property of the contents of the location property Because you are holding a reference to a Task object, the key path to the Location ’ s name property is location.name The following is the code for the fetchedResultsController accessor:

- (NSFetchedResultsController *)fetchedResultsController {

if (fetchedResultsController != nil) { return fetchedResultsController;

} /*

Set up the fetched results controller

*/

// Create the fetch request for the entity

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];

// Edit the entity name as appropriate

NSEntityDescription *entity = [NSEntityDescription entityForName:@”Task”

inManagedObjectContext:managedObjectContext];

[fetchRequest setEntity:entity];

// Edit the sort key as appropriate

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”location.name”

ascending:YES];

Trang 10

208 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION

NSArray *sortDescriptors =

[[NSArray alloc] initWithObjects:sortDescriptor, nil];

[fetchRequest setSortDescriptors:sortDescriptors];

// Edit the section name key path and cache name if appropriate

// nil for section name key path means “no sections”

NSFetchedResultsController *aFetchedResultsController =

[[NSFetchedResultsController alloc]

initWithFetchRequest:fetchRequest

managedObjectContext:managedObjectContext

sectionNameKeyPath:@”location.name” cacheName:@”Task”];

aFetchedResultsController.delegate = self;

self.fetchedResultsController = aFetchedResultsController;

[aFetchedResultsController release];

[fetchRequest release];

[sortDescriptor release];

[sortDescriptors release];

return fetchedResultsController;

}

LocationTasksViewController.m

If you look at the defi nition of the sort descriptor, you can see that you are also using the key path to

the Location object ’ s name property In order for the fetched results controller to create the sections

properly, you need to sort the result set using the same key that you use to generate the sections As

mentioned previously, the section key is set when initializing the NSFetchedResultsController in

the sectionNameKeyPath parameter

Next, you need to code the controllerDidChangeContent method to reload the data in the

TableView when the contents of the fetched results controller changes:

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {

// In the simplest, most efficient, case, reload the table view

[self.tableView reloadData];

}

LocationTasksViewController.m

The next step is to implement the TableView methods The numberOfSectionsInTableView

method will look just like the corresponding method in the RootViewController However,

because you are using sections on this screen, the fetched results controller will not just return 1 for

the number of sections Instead, the fetched results controller will calculate the number of sections

based on the number of different values in the location.name property

The tableView:numberOfRowsInSection: method also uses the fetch results controller to

populate the TableView In this case, you get an NSFetchedResultsSectionInfo object from

the fetched results controller that corresponds to the current section The section info object has a

numberOfObjects property that returns the number of objects in the section This value is returned

as the number of rows in the section

Ngày đăng: 04/07/2014, 21:20

TỪ KHÓA LIÊN QUAN

w