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

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

10 236 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,14 MB

Nội dung

managedObjectContext properties. Last, you push the new ViewTaskController onto the navigation stack and then release it. The last change in the RootViewController is to implement the TableView methods. You can leave the default numberOfSectionsInTableView: , tableView:numberOfRowsInSection: , tableView: commitEditingStyle:forRowAtIndexPath: and tableView:canMoveRowAtIndexPath: methods. You do need to implement the tableView:cellForRowAtIndexPath: to display the text property of the Task object for the row. You will also add some code to check the isOverdue transient property and display overdue tasks in red. Here is the code for tableView:cellForRowAtIndexPath: - (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. Task *managedTaskObject = [fetchedResultsController objectAtIndexPath:indexPath]; cell.textLabel.text = managedTaskObject.text; // Change the text color if the task is overdue if (managedTaskObject.isOverdue==[NSNumber numberWithBool: YES]) { cell.textLabel.textColor = [UIColor redColor]; } else { cell.textLabel.textColor = [UIColor blackColor]; } return cell; } RootViewController.m You should be familiar with how this code works. The beginning of the method tries to dequeue a cell and if it cannot, it creates a new one. Next, the code gets the Task object from the fetchedResultsController that corresponds to the requested cell. Then, the cell ’ s textLabel is set with the text from the Task object. Finally, use the value of the Task ’ s isOverdue property to determine the color of the text. Build and run the application. You should not get any errors or warnings. The application should now allow you to create new default tasks and then navigate to the ViewTaskController screen. You should also be able to select tasks in the Tasks screen and view them in the ViewTaskController screen. Adding and Viewing Tasks ❘ 179 CH007.indd 179CH007.indd 179 9/18/10 9:48:41 AM9/18/10 9:48:41 AM 180 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION BUILDING THE EDITING CONTROLLERS Your Tasks application now has the capability to create new tasks, and view existing tasks. Although there is some decent functionality there, the application is useless without being able to edit the contents of your tasks. In this section, you will implement the screens shown in Figure 7 - 6. These screens will allow the user to edit each individual piece of data in a task. Create a new group under Classes called Sub Controllers to hold all of your edit controller code. FIGURE 7 - 6: Task data editing screens (a) Text Editing Screen - EditTextController (b) Priority Selection Screen - EditPriorityController (c) Location Selection Screen - EditLocationController (d) Date Editing Screen - EditDateController CH007.indd 180CH007.indd 180 9/18/10 9:48:41 AM9/18/10 9:48:41 AM Editing Text with the EditTextController The EditTextController screen, as you can see in Figure 7 - 7, will allow the user to edit the text used in the Task and Location objects. The screen consists of a UITableView with one cell. Embedded in that cell is a UITextField . This design is consistent with the behavior of the text editing screens in the stock iPhone applications such as Contacts. In the new Sub Controllers group, create a new UITableviewController without NIB called EditTextController . Open the EditTextController.h header fi le and add instance variables for an NSManagedObject and the NSManagedObjectContext : NSManagedObject* managedObject; NSManagedObjectContext *managedObjectContext; EditTextController.h The parent screen will set the managed object and the context before it pushes the EditTextController on to the navigation stack. You also need to add an instance variable to hold an NSString* called keyString . Because this screen supports editing both the text property of the Task object and the name property of the Location object, you will use key - value coding (KVC) to take the text entered on the screen and update the managed object. This is also the reason that the screen accepts an NSManagedObject instead of one of your custom subclasses. That way, the screen is generic enough to edit text fi elds on any Managed Objects and is not limited to editing only Task or Location objects. Add the keyString member variable to the following header: NSString* keyString; Finally, add a member variable to hold the UITextField that you will embed in the TableView : UITextField* textField; The last thing that you need to do is add property declarations for your instance variables. Your fi nished header fi le should look like Listing 7 - 3. LISTING 7 - 3: EditTextController.h #import < UIKit/UIKit.h > @interface EditTextController : UITableViewController { NSManagedObject* managedObject; FIGURE 7 - 7: EditText Controller Screen continues Building the Editing Controllers ❘ 181 CH007.indd 181CH007.indd 181 9/18/10 9:48:42 AM9/18/10 9:48:42 AM 182 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION LISTING 7 - 3 (continued) NSManagedObjectContext *managedObjectContext; NSString* keyString; UITextField* textField; } @property (nonatomic, retain) NSManagedObject* managedObject; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @property (nonatomic, retain) NSString* keyString; @end Now, you have to work on the implementation fi le, EditTextController.m . The fi rst thing that you will need to do is synthesize the properties that you declared in the header: @synthesize managedObject,keyString,managedObjectContext; Next, uncomment and implement the viewDidLoad method: - (void)viewDidLoad { [super viewDidLoad]; // Create the textfield textField = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 200, 20)]; // Notice how you use KVC here because you might get a Task or a Location in // this generic text editor textField.text = [managedObject valueForKey:keyString]; textField.clearsOnBeginEditing=YES; // Add the save button UIBarButtonItem* saveButton =[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave target:self action:@selector (saveButtonPressed:)]; self.navigationItem.rightBarButtonItem = saveButton; [saveButton release]; } EditTextController.m The fi rst thing that you need to do in viewDidLoad is call super viewDidLoad . In general, you want to call the superclass version of a method before you do anything in your method to ensure that everything in the superclass is set up properly before you begin your work. Conversely, you CH007.indd 182CH007.indd 182 9/18/10 9:48:43 AM9/18/10 9:48:43 AM may have noticed that in viewDidUnload and dealloc , you do your work fi rst and then call the superclass version of those methods at the end. Next, you move on to create and confi gure the textfield instance variable. The code sets the text of the textfield using key - value coding, not the specifi c text or name property of the Task or Location object. Remember that you are building this screen generically to be able to handle the text input for any fi eld in any managed object. Last, the code creates the Save button and sets it as the rightBarButtonItem in the nav bar. The next task is to implement the saveButtonPressed method. Pressing the Save button in the nav bar calls the saveButtonPressed method. In this method, you will get the text from the text fi eld and use KVC to set the appropriate key in the managed object. Remember that the previous screen set the keyString before displaying the EditTextController . Then, you save the context and pop the View Controller off the navigation stack. Here is the code: -(void) saveButtonPressed: (id) sender { // Configure the managed object // Notice how you use KVC here because you might get a Task or a Location // in this generic text editor [managedObject setValue:textField.text forKey:keyString]; // Save the context. NSError *error = nil; if (![self.managedObjectContext save: & error]) { NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } // pop the view [self.navigationController popViewControllerAnimated:YES]; } EditTextController.m In the viewDidUnload method, set the properties of the class to nil : - (void)viewDidUnload { self.managedObjectContext=nil; self.managedObject = nil; self.keyString=nil; [super viewDidUnload]; } EditTextController.m Building the Editing Controllers ❘ 183 CH007.indd 183CH007.indd 183 9/18/10 9:48:43 AM9/18/10 9:48:43 AM 184 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION Leave the numberOfSectionsInTableView method with the default implementation. The table will have only one section. Change the tableView:numberOfRowsInSection: method to return one row: - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } // Customize the number of rows in the table view. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 1; } EditTextController.m Next, you should implement the tableView:cellForRowAtIndexPath: method to show the textField in the tableView cell: // 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]; } // Set up the cell if (indexPath.row == 0) { UIView* cv = cell.contentView; [cv addSubview:textField]; } return cell; } EditTextController.m CH007.indd 184CH007.indd 184 9/18/10 9:48:44 AM9/18/10 9:48:44 AM Implement tableView:didSelectRowAtIndexPath: to deselect the selected cell. You don ’ t necessarily have to do this to complete the functionality of your application, but the Apple Human Interface Guidelines suggest that you deselect a tableview cell after its selection. Therefore, I ’ m including the following code: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Deselect the currently selected row according to the HIG [tableView deselectRowAtIndexPath:indexPath animated:NO]; } EditTextController.m Last, but not least, you need to implement the dealloc method to clean up any memory that your class has allocated: - (void)dealloc { [managedObject release]; [managedObjectContext release]; [keyString release]; [super dealloc]; } EditTextController.m Setting Priorities with the EditPriorityController The EditPriorityController screen, which you can see in Figure 7 - 8, allows the user to choose the priority for a task. Again, you will implement the screen as a TableView . This time, there will be a row for each priority level. In the Sub Controllers group, create a new UITableviewController without a NIB called EditPriorityController . In the header fi le, you will need to add instance variables and properties for a Task object and the context. You will also need to add a #import directive for the Task.h header fi le. Your header should look like Listing 7 - 4. LISTING 7 - 4: EditPriorityController.h #import < UIKit/UIKit.h > #import “Task.h” @interface EditPriorityController : UITableViewController { continues FIGURE 7 - 8: EditPriority Controller Screen Building the Editing Controllers ❘ 185 CH007.indd 185CH007.indd 185 9/18/10 9:48:44 AM9/18/10 9:48:44 AM 186 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION LISTING 7 - 4 (continued) Task* managedTaskObject; NSManagedObjectContext *managedObjectContext; } @property (nonatomic, retain) Task* managedTaskObject; @property (nonatomic, retain) NSManagedObjectContext *managedObjectContext; @end In the implementation fi le, you fi rst need to synthesize the properties that you declared in the header: @synthesize managedTaskObject,managedObjectContext; As in the EditTextController , you should implement the viewDidUnload method to set the properties defi ned in the class to nil : - (void)viewDidUnload { self.managedObjectContext=nil; self.managedTaskObject = nil; [super viewDidUnload]; } EditPriorityController.m Leave the numberOfSectionsInTableView method with the default implementation because the table will have only one section. Change tableView:numberOfRowsInSection: to return four rows, one for each priority level. Next, implement the tableView:cellForRowAtIndexPath: method to show the priority options in the appropriate cell: // 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]; } CH007.indd 186CH007.indd 186 9/18/10 9:48:45 AM9/18/10 9:48:45 AM // Set up the cell switch (indexPath.row) { case 0: cell.textLabel.text = @”None”; break; case 1: cell.textLabel.text = @”Low”; break; case 2: cell.textLabel.text = @”Medium”; break; case 3: cell.textLabel.text = @”High”; break; default: break; } // place the checkmark next to the existing priority if (indexPath.row == [managedTaskObject.priority intValue] ) { cell.accessoryType=UITableViewCellAccessoryCheckmark; } return cell; } EditPriorityController.m This method should be familiar to you. The fi rst few lines try to dequeue a cell as usual. Then, the code determines the text of the cell based on which cell you are providing. The last bit of code displays a checkmark next to the currently chosen priority for the task. When a user taps a row, you need to save that selection in the Task object. You will do that in the tableView:didSelectRowAtIndexPath: method: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Deselect the currently selected row according to the HIG [tableView deselectRowAtIndexPath:indexPath animated:NO]; // Configure the managed object managedTaskObject.priority=[NSNumber numberWithInt:indexPath.row]; // Save the context. NSError *error = nil; if (![self.managedObjectContext save: & error]) { // There was an error validating the date Building the Editing Controllers ❘ 187 CH007.indd 187CH007.indd 187 9/18/10 9:48:46 AM9/18/10 9:48:46 AM 188 ❘ CHAPTER 7 BUILDING A CORE DATA APPLICATION // Display error information NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); 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 old priority [self.managedObjectContext rollback]; } else { // pop the view [self.navigationController popViewControllerAnimated:YES]; } } EditPriorityController.m The fi rst thing that this method does is deselect the selected row as I explained in the last section. The next line sets the Task object ’ s priority fi eld to the priority level selected on the screen. Then, the code saves the context. Because you are going to add a validation rule that includes the priority, there is a possibility that the new priority could fail the validation. If the validation fails, the save method will fail, so you need to roll the context back to its state before the failed save. If the save method fails, you revert the priority back to its original state using the rollback method of the context. The rollback method undoes all changes to the context that have not yet been committed with a successful save call. If an error occurs, such as a validation failure, you show the user an alert to inform him that a problem has occurred. If there is no problem, the code pops the View Controller from the stack. Finally, implement the dealloc method to release the member variables that you allocated in the class: - (void)dealloc { [managedTaskObject release]; [managedObjectContext release]; [super dealloc]; } EditPriorityController.m CH007.indd 188CH007.indd 188 9/18/10 9:48:46 AM9/18/10 9:48:46 AM . code. FIGURE 7 - 6: Task data editing screens (a) Text Editing Screen - EditTextController (b) Priority Selection Screen - EditPriorityController (c) Location Selection Screen - EditLocationController (d). Listing 7 - 3. LISTING 7 - 3: EditTextController.h #import < UIKit/UIKit.h > @interface EditTextController : UITableViewController { NSManagedObject* managedObject; FIGURE 7 - 7: EditText Controller. Listing 7 - 4. LISTING 7 - 4: EditPriorityController.h #import < UIKit/UIKit.h > #import “Task.h” @interface EditPriorityController : UITableViewController { continues FIGURE 7 - 8:

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

TỪ KHÓA LIÊN QUAN