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

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

10 244 0

Đ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 3,26 MB

Nội dung

Adding and Editing Locations with the EditLocationController The user navigates to the EditLocationController by tapping the location cell on the ViewTaskController . The EditLocationController , as shown in Figure 7 - 9, allows the user to select a location, add new locations, and delete existing locations. To create the EditLocationController , create a new UITableviewController without a NIB called EditLocationController . Modify your new header fi le to create instance variables and properties to hold the context and Task objects that the parent screen will confi gure. You will also need to add a member variable and property for the NSFetchedResultsController that you will use to display your location list. Additionally, you will need to add #import directives for the Task and Location header fi les. The completed header fi le should look like Listing 7 - 5. LISTING 7 - 5: EditLocationController.h #import < UIKit/UIKit.h > #import “Task.h” #import “Location.h” @interface EditLocationController : UITableViewController < NSFetchedResultsControllerDelegate > { NSFetchedResultsController *fetchedResultsController; NSManagedObjectContext *managedObjectContext; Task* managedTaskObject; } @property (nonatomic, retain) NSFetchedResultsController *fetchedResultsController; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) Task* managedTaskObject; @end Let ’ s move on to the implementation fi le. Add an import statement for EditTextController.h : #import “EditTextController.h” You will use the EditTextController to add the text for newly added locations. Next, synthesize the properties that you declared in the header fi le: @synthesize fetchedResultsController, managedObjectContext, managedTaskObject; FIGURE 7 - 9: EditLocation Controller Screen Building the Editing Controllers ❘ 189 CH007.indd 189CH007.indd 189 9/18/10 9:48:46 AM9/18/10 9:48:46 AM 190 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION Uncomment and implement the viewDidLoad method: - (void)viewDidLoad { [super viewDidLoad]; // Set up the add button UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewLocation)]; self.navigationItem.rightBarButtonItem = addButton; [addButton release]; NSError* error; if (![[self fetchedResultsController] performFetch: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } // set the title to display in the nav bar self.title = @”Location”; } EditLocationController.m This code creates the addButton and sets it to call the insertNewLocation method. It then adds the addButton to the nav bar. Next, you tell the fetched results controller to fetch its data. Finally, you set the title of the screen to Location. Next, you need to set the class properties in the viewDidUnload method to nil : - (void)viewDidUnload { self.fetchedResultsController=nil; self.managedTaskObject=nil; self.managedObjectContext=nil; [super viewDidUnload]; } EditLocationController.m Now, add the insertNewLocation method that runs when the user taps the Add button that you created in viewDidLoad . This method adds a new Location to the context and pushes the text controller on to the navigation stack to allow the user to edit the location name. Here is the insertNewLocation method: - (void)insertNewLocation { NSManagedObjectContext *context = self.managedObjectContext; Location *newLocation = CH007.indd 190CH007.indd 190 9/18/10 9:48:47 AM9/18/10 9:48:47 AM [NSEntityDescription insertNewObjectForEntityForName:@”Location” inManagedObjectContext:context]; EditTextController* textController = [[EditTextController alloc] initWithStyle:UITableViewStyleGrouped]; textController.managedObject=newLocation; textController.managedObjectContext = self.managedObjectContext; textController.keyString=@”name”; [self.navigationController pushViewController:textController animated:YES]; [textController release]; } EditLocationController.m Next, implement the fetched results controller accessor method to fetch the Location entities and sort them in ascending order by name: - (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:@”Location” inManagedObjectContext:managedObjectContext]; [fetchRequest setEntity:entity]; // Edit the sort key as appropriate. NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@”name” ascending:YES]; 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:nil cacheName:nil]; Building the Editing Controllers ❘ 191 CH007.indd 191CH007.indd 191 9/18/10 9:48:48 AM9/18/10 9:48:48 AM 192 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION aFetchedResultsController.delegate = self; self.fetchedResultsController = aFetchedResultsController; [aFetchedResultsController release]; [fetchRequest release]; [sortDescriptor release]; [sortDescriptors release]; return fetchedResultsController; } EditLocationController.m Implement the controllerDidChangeContent delegate method to reload the table data: // NSFetchedResultsControllerDelegate method to notify the delegate // that all section and object changes have been processed. - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView reloadData]; } EditLocationController.m Change the numberOfSectionsInTableView and tableView:numberOfRowsInSection: methods to use the fetchedResultsController : - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[fetchedResultsController sections] count]; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { id < NSFetchedResultsSectionInfo > sectionInfo = [[fetchedResultsController sections] objectAtIndex:section]; return [sectionInfo numberOfObjects]; } EditLocationController.m Now, implement the tableView:cellForRowAtIndexPath: method to show the locations from the fetched results controller: // Customize the appearance of table view cells. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @”Cell”; CH007.indd 192CH007.indd 192 9/18/10 9:48:48 AM9/18/10 9:48:48 AM UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } Location *managedLocationObject = [fetchedResultsController objectAtIndexPath:indexPath]; // If the location in the task object is the same as the location object // draw the checkmark if (managedTaskObject.location == managedLocationObject) { cell.accessoryType=UITableViewCellAccessoryCheckmark; } cell.textLabel.text = managedLocationObject.name; return cell; } EditLocationController.m You can see that the code does the usual cell setup and dequeuing. Then, it obtains a Location object for the cell from the fetchedResultsController . The code then checks to see if the location that it will use is also the location in the Task . If it is, the code displays the checkmark accessory for the cell. Last, you use the name property of the Location object as the cell text. Next, you need to implement tableView:didSelectRowAtIndexPath: to save the selected location: tableView:didSelectRowAtIndexPath: to save off the selected location - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Deselect the currently selected row according to the HIG [tableView deselectRowAtIndexPath:indexPath animated:NO]; // set the Task’s location to the chosen location Location *newLocationObject = [fetchedResultsController objectAtIndexPath:indexPath]; managedTaskObject.location=newLocationObject; // Save the context. NSError *error = nil; if (![self.managedObjectContext save: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } Building the Editing Controllers ❘ 193 CH007.indd 193CH007.indd 193 9/18/10 9:48:49 AM9/18/10 9:48:49 AM 194 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION else { // pop the view [self.navigationController popViewControllerAnimated:YES]; } } EditLocationController.m This code gets the selected Location object from the fetchedResultsController , sets the location in the Task object, saves the context, and pops the view from the navigation stack. In order to allow the user to delete locations, uncomment and implement the tableView: commitEditingStyle: method: // Override to support editing the table view. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the managed object for the given index path NSManagedObjectContext *context = [fetchedResultsController managedObjectContext]; [context deleteObject:[fetchedResultsController objectAtIndexPath:indexPath]]; // Save the context. NSError *error = nil; if (![context save: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } } } EditLocationController.m This code enables the delete editing style that displays the Delete button when a user swipes across a row. The code then gets the context, deletes the Location that the user selected, and saves the context. Finally, implement dealloc to release your properties: - (void)dealloc { [fetchedResultsController release]; [managedObjectContext release]; [managedTaskObject release]; [super dealloc]; } EditLocationController.m CH007.indd 194CH007.indd 194 9/18/10 9:48:49 AM9/18/10 9:48:49 AM Modifying Dates with the EditDateController The last edit controller that you will implement is the EditDateController , which you can see in Figure 7 - 10. Like the RootViewController , you will need to create the interface for this controller using Interface Builder and a NIB because the screen will need to hold a UIDatePicker control in addition to the TableView that will display the selected date. Create a new UIViewController with NIB called EditDateController . Make sure that you have unchecked “ UITableViewController subclass ” and that you have checked “ With XIB for user interface. ” I like to keep my projects organized, so I moved the XIB fi le into the Resources folder. In the EditDateController.h header fi le, add an import statement for your Task object: #import “Task.h” Add references to the UITableViewDelegate and UITableViewDataSource protocols in the interface defi nition to indicate that you plan to implement these protocols: @interface EditDateController : UIViewController < UITableViewDelegate, UITableViewDataSource > Add member variables and properties for a Task object, the context, a UITableView , and a UIDatePicker . Finally, add an action method, dateChanged , that will run when the user changes the date in the UIDatePicker . The fi nished header should look like Listing 7 - 6. LISTING 7 - 6: EditDateController.h #import < UIKit/UIKit.h > #import “Task.h” @interface EditDateController : UIViewController < UITableViewDelegate, UITableViewDataSource > { Task* managedTaskObject; NSManagedObjectContext *managedObjectContext; UIDatePicker* datePicker; UITableView* tv; } @property (nonatomic, retain) Task* managedTaskObject; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) IBOutlet UIDatePicker* datePicker; @property (nonatomic, retain) IBOutlet UITableView* tv; -(IBAction)dateChanged:(id)sender; @end FIGURE 7 - 10: Edit DateController Screen Building the Editing Controllers ❘ 195 CH007.indd 195CH007.indd 195 9/18/10 9:48:50 AM9/18/10 9:48:50 AM 196 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION Make sure that you save your completed header. You need to save because Interface Builder will only read the last saved version of the header, and you need IB to see your DatePicker , TableView , and dateChanged methods. Now it ’ s time to move into Interface Builder and work on the UI. Double - click on the EditDateController.xib fi le to open it using IB. Drag a UIDatePicker control into the view widow and place it at the bottom of the view. In the attributes inspector for the DatePicker , set the Mode to Date. There are various date and time display modes, but you only need to allow the user to select a date. Drag a UITableView into the view window and position it at the top of the view. Stretch the TableView to fi ll the whole screen. Then send it to the back using Layout ➪ Send to Back. In the attributes inspector for the TableView , set the Style to Grouped. Next, you need to connect the interface to the code class. Hook up the TableView ’ s dataSource and delegate to File ’ s Owner. Hook up the File ’ s Owner tv variable to the TableView and datePicker variable to the UIDatePicker . Finally, hook up the UIDatePicker ’ s Value Changed action to the File ’ s Owner dateChanged: method. You can now move on to the EditDateController.m implementation fi le. First, you need to synthesize the properties that you defi ned in the header: @synthesize managedTaskObject,managedObjectContext,datePicker,tv; Uncomment and implement the viewDidLoad method: - (void)viewDidLoad { [super viewDidLoad]; // Add the save button UIBarButtonItem* saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector (saveButtonPressed:)]; self.navigationItem.rightBarButtonItem = saveButton; [saveButton release]; // Set the date to the one in the managed object, if it is set // else, set it to today NSDate* objectDate = managedTaskObject.dueDate; if (objectDate!=nil) { datePicker.date = objectDate; } else { datePicker.date = [NSDate date]; } } EditDateController.m CH007.indd 196CH007.indd 196 9/18/10 9:48:50 AM9/18/10 9:48:50 AM This method creates the Save button and adds it to the nav bar. It also sets the date in the date picker control to the date in the Task object, if it exists, or to the current date. Implement the viewDidUnload method to set the class ’ s properties to nil : - (void)viewDidUnload { self.managedObjectContext=nil; self.managedTaskObject = nil; self.datePicker = nil; self.tv=nil; [super viewDidUnload]; } EditDateController.m The user invokes the saveButtonPressed method when she clicks the Save button created in viewDidLoad . You need to implement saveButtonPressed to save the currently selected date to the Task object. Here is the code: -(void) saveButtonPressed: (id) sender { // Configure the managed object managedTaskObject.dueDate=[datePicker date]; // Save the context. NSError *error = nil; if (![self.managedObjectContext save: & error]) { // There was an error validating the date // Display error information UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@”Invalid Due Date” message:[[error userInfo] valueForKey:@”ErrorString”] delegate:nil cancelButtonTitle:@”OK” otherButtonTitles:nil ]; [alert show]; [alert release]; // Roll back the context to // revert back to the original date [self.managedObjectContext rollback]; } else{ // pop the view [self.navigationController popViewControllerAnimated:YES]; } } EditDateController.m Building the Editing Controllers ❘ 197 CH007.indd 197CH007.indd 197 9/18/10 9:48:51 AM9/18/10 9:48:51 AM 198 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION Because you are going to add a validation rule that includes the date, there is a possibility that the new date can fail the validation. If the validation fails, the save method will fail, so you need to roll the context back to its old state before the failed save. If the save method fails, you revert the dueDate back to the originally selected date by calling the rollback method of the context. Then, you show an alert to the user. If the save is successful, you simply pop the controller from the navigation stack. Next, implement the dateChanged method to reload the TableView and update the date text: -(IBAction)dateChanged:(id)sender{ // Refresh the date display [tv reloadData]; } EditDateController.m Because there is only one cell in the TableView , implement the ViewTaskController screen and tableView:numberOfRowsInSection: to return 1: - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } EditDateController.m Implement tableView:cellForRowAtIndexPath: to show the date chosen in the DatePicker : - (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]; } // Set up the cell if (indexPath.row == 0) { CH007.indd 198CH007.indd 198 9/18/10 9:48:51 AM9/18/10 9:48:51 AM . Task and Location header fi les. The completed header fi le should look like Listing 7 - 5. LISTING 7 - 5: EditLocationController.h #import < UIKit/UIKit.h > #import “Task.h” #import. fetchedResultsController : - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return [[fetchedResultsController sections] count]; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView. changes the date in the UIDatePicker . The fi nished header should look like Listing 7 - 6. LISTING 7 - 6: EditDateController.h #import < UIKit/UIKit.h > #import “Task.h” @interface

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

TỪ KHÓA LIÊN QUAN