AL Chapter 1: A User Interface for Adding Hotels and Using the Address Book ✓ Creating a user interface to add a hotel RI In This Chapter TE ✓ Accessing the Address Book to use exiting information MA ✓ Adding a hotel address to the Address Book A TE D lthough the ability to add hotels that you want to stay in is obviously important (staying in the hard-coded default hotel San Francisco is a bit awkward if you’re in Needles, California, for example), it’s also really helpful to integrate your Addr
742 Add AddHotelController Be sure the UITableViewController subclass is not selected and the With XIB for User Interface is selected. You see a new dialog asking for some more information. 3. Enter AddHotelController.m in the File Name field and then click Finish. To make things easier to find, I keep my AddHotelController.m and .h classes in the Classes folder. I also move the AddHotelController.xib to the Resources folder.
Add AddHotelController 743 Isn’t it a beauty? Well, okay, the aesthetics do leave a bit to be desired, but I’ll leave that up to you after I show you how to take care of all the plumbing you need behind it. Adding controls to the view The first pipes you want to lay involve adding controls to the view. Here’s what you need to do: 1. In the Resources folder (if that’s where you placed it), double-click the AddHotelController.xib file. 2. Make sure the Library window is open.
744 Add AddHotelController Figure 1-2: The labels. Figure 1-3: Right justifying text in a label.
Add AddHotelController 745 6. To add blank text fields for each label, drag in five of them from the Library window, as shown in Figure 1-4. While it’s not shown in any of the figures, I have Appears While Editing selected in the Clear Button drop-down menu and, I have Clear When Editing Begins deselected. These are my preferences; you should feel free to use them or experiment with your own settings.
746 Add AddHotelController Figure 1-5: The buttons. Figure 1-6: The simulated interface.
Add AddHotelController 747 10. Make your Xcode window the active window again. If you can’t find it, or you minimized it, just click the Xcode icon in the Dock. The RoadTrip project should still be the active one. (You can always tell the active project by looking at the project name at the top of the Groups & Files list.) This is the general pattern I use as I build my interface — add stuff, and then simulate it to see how it really looks.
748 Add AddHotelController IBAction is one of those cool little techniques, like IBOutlet, that does nothing in the code but provide a way to inform Interface Builder (hence, the IB in both of them) that this method can be used as an action for TargetAction connections. All IBAction does is act as a tag for Interface Builder — identifying this method (action) as one you can connect to an object (namely, the Button) in a nib file. In this respect, this whole IBAction trick is similar to the IBOutlet.
Making the Connections in Interface Builder 749 I’ve also had you declare two new methods (and the usual initialization method), getFromContacts: and saveToContacts:, each with the keyword IBAction as the return type. IBAction is actually defined as a void, so if you think about it, all you’ve done is declare a new method with a return type of void.
750 Making the Connections in Interface Builder Similarly, to execute a method in your code when the user taps a button, you also had to do two things: 1. Declare an IBAction in your code. 2. Use Interface Builder to point the event in the button you created earlier to the IBAction method in your code.
Making the Connections in Interface Builder 751 Figure 1-7: The AddHotelController Outlets. Book VII Chapter 1 A User Interface for Adding Hotels and Using the Address Book Figure 1-8: Connecting the name outlet item to its text field.
752 Making the Connections in Interface Builder Figure 1-9: Add a referencing outlet. Figure 1-10: Setting the delegate.
Making the Connections in Interface Builder 753 This sets the File’s Owner as the UITextFieldDelegate, something you’ll need to do to manage the keyboard. I explain that in a later section. When you’re all done, your screen should look like mine in Figure 1-11. 7. Repeat Steps 4–6 for the rest of the outlets. address, city, state, and zip, to be precise. With that done, you are now ready to connect the buttons. 8.
754 Adding Some Code to Actually Add Some Functionality Figure 1-12: Connecting the button to the action method. Adding Some Code to Actually Add Some Functionality Making all the necessary connections Interface Builder, as spelled out in the last section, ensures that your code will compile and run (and give you a few choice warnings about unimplemented methods in the bargain), but RoadTrip really won’t do anything different now as opposed to what it could do at the start of this chapter.
Adding Some Code to Actually Add Some Functionality 755 - (id) initWithHotel:(Hotel*) theHotel trip:(Trip*) theTrip { if (self = [super initWithNibName:@”AddHotelController” bundle:nil]) { hotel = theHotel; trip = theTrip; } return self; } This is your run-of-the-mill initialization method, and there really isn’t anything left to say about it, other than you’d better not forget the #import statements, because the compiler will be happy to indirectly point out to you that they’re missing.
756 Adding Some Code to Actually Add Some Functionality Listing 1-3 (continued) //addHotelController.delegate = self; [self presentModalViewController:navigationController animated:YES]; [navigationController release]; [addHotelController release]; Annotation *annotation = [NSEntityDescription ins ertNewObjectForEntityForName:@”Annotation” inManagedObjectContext:trip.
Adding Some Code to Actually Add Some Functionality 757 I chose the transition style UIModalTransitionStyleFlipHorizontal — where the current view does a horizontal 3D flip from right-to-left, resulting in the revealing of the new view as if it were on the back of the previous view — but you can use any transition style you like.
758 Adding Some Code to Actually Add Some Functionality Listing 1-4: viewWillDisappear: - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self cleanUpUndoManager]; // Save the context. if (trip.managedObjectContext.hasChanges) { NSError *error = nil; if (![trip.managedObjectContext save:&error]) { /*Replace this implementation with code to handle the error appropriately. */ NSLog(@”Unresolved error %@, %@”, error, [error userInfo]); abort(); } } [trip loadHotels]; [trip.
Adding Some Code to Actually Add Some Functionality 759 Start with adding the state information it needs — a Boolean amEditing — to HotelController.h, as shown in bold in Listing 1-5. Listing 1-5: Adding State Information to HotelController.h @class Trip; @class Hotel; #import “AddHotelController.
760 Adding Some Code to Actually Add Some Functionality Because you can never be too careful, add the code in bold in Listing 1-7 to initWithTrip: in HotelController.m to initialize the amEditing state. Listing 1-7: Initializing the State - (id) initWithTrip: (Trip*) aTrip{ if (self = [super initWithNibName:@”HotelController” bundle:nil]) { trip = aTrip; [trip retain]; amEditing = NO; } return self; } Next, set the state to amEditing in insertNewObject in HotelController.
Entering and Saving the Hotel Information 761 //addHotelController.delegate = self; [self presentModalViewController:navigationController animated:YES]; [navigationController release]; [addHotelController release]; Annotation *annotation = [NSEntityDescription ins ertNewObjectForEntityForName:@”Annotation” inManagedObjectContext:trip.
762 Entering and Saving the Hotel Information Listing 1-9: Implementing textFieldShouldReturn: -(BOOL)textFieldShouldReturn:(UITextField *) theTextField { [theTextField resignFirstResponder]; return YES; } Now, you’ll discover that, when you tap Return on the keyboard, the keyboard kindly lowers itself. You’ll also be aware of a couple of features that come with using a text field.
Entering and Saving the Hotel Information 763 Book VII Chapter 1 Adding Cancel and Save buttons Now you need to add two buttons: one to save any changes, and one to enable you to cancel any changes you’ve made. To add the buttons, you need to decide how to deal with a save or a cancel.
764 Entering and Saving the Hotel Information Listing 1-10: Adding the Save and Cancel Buttons in viewDidLoad - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @”Hotel Information”; UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:@”Cancel” style:UIBarButtonItemStyleBordered target:self action:@selector(cancel:)]; self.navigationItem.
765 Entering and Saving the Hotel Information Listing 1-12: The save: Method - (IBAction)save:(id)sender { hotel.street = street.text ; hotel.state = state.text; hotel.zip = zip.text; hotel.name = name.text; hotel.city = city.text; [delegate addHotelController:self didFinishWithSave:YES]; } As you might expect, the save: message updates the Hotel object you created in the HotelController earlier and then also sends the addHotel Controller;didFinishWithSave: message.
766 Entering and Saving the Hotel Information Listing 1-13 (continued) inManagedObjectContext:trip.managedObjectContext]; [annotation setTitle:@”Annotation”]; [annotation setHotel:hotel]; [hotel setAnnotation:annotation]; Geocoder * geocoder = [[Geocoder alloc] init]; NSString* geocodeString = [[NSString alloc ] initWithFormat: @” %@ %@ %@ %@”, hotel.street, hotel.city, hotel.state, hotel.zip]; CLLocationCoordinate2D theCoordinate = [geocoder geocodeLocation:geocodeString]; hotel.annotation.
Entering and Saving the Hotel Information 767 Listing 1-14: Updating insertNewObject - (void)insertNewObject { NSEntityDescription *entity = [[fetchedResultsController fetchRequest] entity]; hotel = [NSEntityDescription insertNewObjectForEntit yForName:[entity name] inManagedObjectContext:trip. managedObjectContext]; //$$ [self setUpUndoManager]; [hotel setValue:@”Hotel California” forKey:@”name”]; [hotel setValue:@”1 Dr. Carlton B.
768 Entering and Saving the Hotel Information You can now also uncomment out the delegate assignment, because you’ll implement all of that next: addHotelController.delegate = self; Adding the delegation plumbing The final step is to add all the code necessary to implement delegation You’ll start by adding the code in bold in Listing 1-15 in order to add the protocol (you’ll name it AddHotelControllerDelegate) and other required declarations to AddHotelController.h. Listing 1-15: AddHotelController.
Entering and Saving the Hotel Information 769 id tells the compiler to do type checking for any class assigned to this instance variable or property. The idea here is for the compiler to check to make sure that the class has adopted the AddHotelControllerDelegate protocol. This is one of the advantages of using formal protocols. You also need to add the following @synthesize statement to AddHotelController.m.
770 Entering and Saving the Hotel Information But when you displayed the AddHotelController view, there was no data to be seen. That’s because you never copied it from the instance variables in the hotel object to the text fields in the view. Go ahead and do that now by adding the code in bold in Listing 1-17 to viewDidLoad in AddHotelController.m. Listing 1-17: Adding Default Data to the View - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.
Entering and Saving the Hotel Information 771 Figure 1-15: Undo Edit Hotel. Book VII Chapter 1 A User Interface for Adding Hotels and Using the Address Book Figure 1-16: Undo Add Hotel.
772 Entering and Saving the Hotel Information Don’t tell me what to do! If you find autocorrecting annoying, as I do, you can shut it off programmatically. - (void)viewDidLoad { [super viewDidLoad]; self.navigationItem.title = @”Hotel Information”; UIBarButtonItem *cancelButtonItem = [[UIBarButtonItem alloc] initWithTitle:@”Cancel” style:UIBarButtonItemStyleBordered target:self action:@selector(cancel:)]; self.navigationItem.
Interfacing with the Address Book Application 773 Interfacing with the Address Book Application I started this chapter off by musing about how nice it would be to be able to add an existing contact in your Address Book to your Hotels list. Actually, doing that is easy, but in doing so you’re sure to come across some concepts and record types that may seem a little alien. But no worries. Soon you’ll be making your way through them like an old hand.
774 Interfacing with the Address Book Application ✦ ABPeoplePickerNavigationController, which prompts the user to select a person record from their Address Book ✦ ABPersonViewController, which displays a person record to the user and optionally allows editing ✦ ABNewPersonViewController, which prompts the user to create a new person record ✦ ABUnknownPersonViewController, which prompts the user to complete a partial person record, and optionally allows them to add it to the Address Book Actually, you’ll re
Interfacing with the Address Book Application 775 The general outline for using ABPeoplePickerNavigationController is as follows: 1. Create and initialize an instance of the class. 2. Set the delegate, which must adopt the ABPeoplePickerNavigationControllerDelegate protocol. 3. Present the People Picker as a modal view controller by using the present ModalViewController:animated: method. 4.
776 Interfacing with the Address Book Application Listing 1-19: Adopting the Protocol @interface AddHotelController : UIViewController { There are four basic objects you need to understand in order to interact with the Address Book database: ✦ Address Books ✦ Records ✦ Single-value properties ✦ Multi-value properties Address Books let you interact with the Address Book database and save changes to it.
Interfacing with the Address Book Application 777 Listing 1-20: peoplePickerNavigationController: shouldContinueAfterSelectingPerson: - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { name.
778 Interfacing with the Address Book Application As one of the arguments of peoplePickerNavigationController: shouldContinueAfterSelectingPerson:, you are passed the record of the person selected: - (BOOL)peoplePickerNavigationController: (ABPeoplePickerNavigationController *)peoplePicker shouldContinueAfterSelectingPerson:(ABRecordRef)person { As I mentioned, inside the person record are properties, and there are two kinds: single-value properties and multi-value properties.
Interfacing with the Address Book Application 779 NSString *firstName = (NSString*) ABRecordCopyValue(person, kABPersonFirstNameProperty); NSString *lastName = (NSString*) ABRecordCopyValue(person, kABPersonLastNameProperty); if (!(name.text)) name.text = [[NSString alloc] initWithFormat: @”%@ %@”, firstName, lastName ]; As you might expect, other properties that a person can have more than one of, such as street address and phone number, are multi-value properties.
780 Interfacing with the Address Book Application ABMultiValueRef multiValueRef = (NSString*) ABRecordCopyValue(person, kABPersonPhoneProperty); In this case, the property you’re getting is the Phone property (kABPersonPhoneProperty) and is a kABMultiStringPropertyType (think string). Because there can be zero or many phone numbers, you get the count and enumerate through the record.
Interfacing with the Address Book Application 781 city.text = (NSString*)CFDictionaryGetValue (dictionary, kABPersonAddressCityKey); state.text = (NSString*)CFDictionaryGetValue (dictionary, kABPersonAddressStateKey); zip.text = (NSString*)CFDictionaryGetValue (dictionary, kABPersonAddressZIPKey); CFRelease(dictionary); } You could, however, iterate through and find the one with the label you’re interested in, such as home or work.
782 Interfacing with the Address Book Application Listing 1-22: Canceling the addition - (void)peoplePickerNavigationControllerDidCancel: (ABPeoplePickerNavigationController *)peoplePicker { [self dismissModalViewControllerAnimated:YES]; } All you really do here is dismiss the controller. To finish up, you also need to add some imports to AddHotelController.h. #import #import Next, you need to add the AddressBook and AddressBookUI Frameworks.
783 Interfacing with the Address Book Application Listing 1-23: Done with the Record - (void)newPersonViewController: (ABNewPersonViewController *)newPersonViewController didCompleteWithNewPerson:(ABRecordRef)person { [self dismissModalViewControllerAnimated:YES]; } Now they you’ve gotten that out of the way, you can concentrate on what you need to do to actually add the new contact. Adding a contact to the iPhone’s Address Book isn’t horribly complicated, but there’s some work to do.
784 Interfacing with the Address Book Application Listing 1-24 (continued) ABNewPersonViewController *picker = [[ABNewPersonViewController alloc] init]; picker.newPersonViewDelegate = self; picker.
Interfacing with the Address Book Application 785 ABMutableMultiValueRef mutableMultiValueRef = ABMultiValueCreateMutable(kABMultiDictionaryPropertyType); NSMutableDictionary *addressDictionary = [[NSMutableDictionary alloc] init]; Then, you go on to add the fields you’re interested in to the dictionary: [addressDictionary setObject:street.text forKey:(NSString *) kABPersonAddressStreetKey]; [addressDictionary setObject:city.
786 Interfacing with the Address Book Application There’s a lot more functionality here that I haven’t coverd — updating an existing records comes to mind, as well as the ability for your application to be notified when another application makes changes to the Address Book database. I’ll leave it up to you to explore that on your own.