Navigating with UINavigationController

Now you have a navigation controller, a navigation bar, and two view controllers. Time to put all the pieces together. The user should be able to tap a row in ItemsViewController’s table view and have the ItemDetailViewController’s view slide onto the screen and display the properties of the selected Possession instance.

Pushing view controllers

Of course, you need to create an instance of ItemDetailViewController. Where should this object be created? Think back to previous exercises where you instantiated all of your controllers in the method application:​didFinishLaunchingWithOptions:. For example, in the Chapter 7 chapter, you created both view controllers and immediately added them to tab bar controller’s viewControllers array.

However, when using a UINavigationController, you cannot simply store all of the possible view controllers in its stack. The viewControllers array of a navigation controller is dynamic – you start with a root view controller and add view controllers depending on user input. Therefore, some object other than the navigation controller needs to create the instance of ItemDetailViewController and be responsible for adding it to the stack.

This object must meet two requirements: it needs to know when to push ItemDetailViewController onto the stack, and it needs a pointer to the navigation controller. Why must this object have a pointer to the navigation controller? If it is to dynamically add view controllers to the navigation controller’s stack, it must be able to send the navigation controller messages, namely, pushViewController:animated:.

ItemsViewController fills both requirements. First, it knows when a row is tapped in a table view because, as the table view’s delegate, it receives the message tableView:didSelectRowAtIndexPath:. Second, any view controller in a navigation controller’s stack can get a pointer to the navigation controller by sending itself the message navigationController. As the root view controller, ItemsViewController is always in the navigation controller’s stack and thus can always access it. Therefore, ItemsViewController will be responsible for creating the instance of ItemDetailViewController and adding it to the stack.

At the top of ItemsViewController.h, import the interface file for ItemDetailViewController.

#​i​m​p​o​r​t​ ​"​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​.​h​"​

@​i​n​t​e​r​f​a​c​e​ ​I​t​e​m​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​T​a​b​l​e​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

When a row is tapped, its delegate is sent tableView:didSelectRowAtIndexPath:, which contains the index path of the selected row. In ItemsViewController.m, implement this method to allocate a ItemDetailViewController and then push it on top of the navigation controller’s stack.

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​I​t​e​m​s​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

-​ ​(​v​o​i​d​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​a​T​a​b​l​e​V​i​e​w​
 ​ ​ ​ ​d​i​d​S​e​l​e​c​t​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​[​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​P​u​s​h​ ​i​t​ ​o​n​t​o​ ​t​h​e​ ​t​o​p​ ​o​f​ ​t​h​e​ ​n​a​v​i​g​a​t​i​o​n​ ​c​o​n​t​r​o​l​l​e​r​'​s​ ​s​t​a​c​k​
 ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​]​ ​p​u​s​h​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​
}​

Build and run the application. Select a row from the UITableView. Not only will you be taken to ItemDetailViewController’s view, but you will get a free animation and a button in the UINavigationBar titled Homepwner. Tapping this button will take you back to ItemsViewController.

Also, notice that the instance of ItemDetailViewController is autoreleased after it is instantiated. The UINavigationController will retain it and then release it when it is popped from the stack. Therefore, when the user taps the back button, the ItemDetailViewController will be deallocated. This is why you implemented the dealloc method for ItemDetailViewController – instances of this class definitely get destroyed while this application is running.

Having a view controller push the next view controller is a common pattern. In any application with a UINavigationController, the navigation controller has one root view controller. The root view controller typically creates the next view controller, and the next view controller creates the one after that, and so on. Some applications may have view controllers that can push different view controllers depending on user input. For example, the Photos will push a video view controller or an image view controller onto the navigation stack depending on what type of media was selected.

(The iPad-only class UISplitViewController calls for a different pattern. The iPad’s larger screen size allows two view controllers in a drill-down interface to appear on screen simultaneously instead of being pushed onto the same stack. You’ll learn more about UISplitViewController in Chapter 26.)

Passing data between view controllers

Of course, the UITextFields on the screen are currently empty. To fill these fields, you need a way to pass data between the ItemsViewController and the ItemDetailViewController. How do you pass data between UIViewControllers?

What you’re going to do is implement a method in ItemDetailViewController that accepts a Possession instance and fills the contents of its UITextFields. ItemsViewController will select the appropriate possession from its array and pass it through that method to the ItemDetailViewController.

In ItemDetailViewController.h, add an instance variable to hold the Possession that is being edited and declare a method to set that instance variable. The class declaration should now look like this:

#​i​m​p​o​r​t​ ​<​U​I​K​i​t​/​U​I​K​i​t​.​h​>​

@​c​l​a​s​s​ ​P​o​s​s​e​s​s​i​o​n​;​

@​i​n​t​e​r​f​a​c​e​ ​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​:​ ​U​I​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
{​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​T​e​x​t​F​i​e​l​d​ ​*​n​a​m​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​T​e​x​t​F​i​e​l​d​ ​*​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​T​e​x​t​F​i​e​l​d​ ​*​v​a​l​u​e​F​i​e​l​d​;​
 ​ ​ ​ ​I​B​O​u​t​l​e​t​ ​U​I​L​a​b​e​l​ ​*​d​a​t​e​L​a​b​e​l​;​

 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​o​s​s​e​s​s​i​o​n​;​
}​
@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​r​e​t​a​i​n​)​ ​P​o​s​s​e​s​s​i​o​n​ ​*​p​o​s​s​e​s​s​i​o​n​;​
@​e​n​d​

In ItemDetailViewController.m, synthesize the accessors for possession and import Possession’s header file.

#​i​m​p​o​r​t​ ​"​P​o​s​s​e​s​s​i​o​n​.​h​"​

@​i​m​p​l​e​m​e​n​t​a​t​i​o​n​ ​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​

@​s​y​n​t​h​e​s​i​z​e​ ​p​o​s​s​e​s​s​i​o​n​;​

Since an instance of ItemDetailViewController will retain its possession, this instance variable must be released when the view controller is deallocated. An instance of ItemDetailViewController will be deallocated when the user goes back to the ItemsViewController. That user action removes the ItemDetailViewController from the stack, which causes the navigation controller, its only owner, to release it. In ItemDetailViewController.m, add this to the dealloc method.

-​ ​(​v​o​i​d​)​d​e​a​l​l​o​c​
{​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​[​n​a​m​e​F​i​e​l​d​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​v​a​l​u​e​F​i​e​l​d​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​d​a​t​e​L​a​b​e​l​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​s​u​p​e​r​ ​d​e​a​l​l​o​c​]​;​
}​

When the ItemDetailViewController’s view appears on the screen, it needs to set the values of its subviews to match the properties of the possession. Override viewWillAppear: in ItemDetailViewController.m to transfer the possession’s properties to the various UITextFields.

-​ ​(​v​o​i​d​)​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​(​B​O​O​L​)​a​n​i​m​a​t​e​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​a​n​i​m​a​t​e​d​]​;​

 ​ ​ ​ ​[​n​a​m​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​p​o​s​s​e​s​s​i​o​n​ ​p​o​s​s​e​s​s​i​o​n​N​a​m​e​]​]​;​
 ​ ​ ​ ​[​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​r​i​a​l​N​u​m​b​e​r​]​]​;​
 ​ ​ ​ ​[​v​a​l​u​e​F​i​e​l​d​ ​s​e​t​T​e​x​t​:​[​N​S​S​t​r​i​n​g​ ​s​t​r​i​n​g​W​i​t​h​F​o​r​m​a​t​:​@​"​%​d​"​,​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​v​a​l​u​e​I​n​D​o​l​l​a​r​s​]​]​]​;​

 ​ ​ ​ ​/​/​ ​C​r​e​a​t​e​ ​a​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​t​h​a​t​ ​w​i​l​l​ ​t​u​r​n​ ​a​ ​d​a​t​e​ ​i​n​t​o​ ​a​ ​s​i​m​p​l​e​ ​d​a​t​e​ ​s​t​r​i​n​g​
 ​ ​ ​ ​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​*​d​a​t​e​F​o​r​m​a​t​t​e​r​ ​=​ ​[​[​[​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​[​d​a​t​e​F​o​r​m​a​t​t​e​r​ ​s​e​t​D​a​t​e​S​t​y​l​e​:​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​M​e​d​i​u​m​S​t​y​l​e​]​;​
 ​ ​ ​ ​[​d​a​t​e​F​o​r​m​a​t​t​e​r​ ​s​e​t​T​i​m​e​S​t​y​l​e​:​N​S​D​a​t​e​F​o​r​m​a​t​t​e​r​N​o​S​t​y​l​e​]​;​

 ​ ​ ​ ​/​/​ ​U​s​e​ ​f​i​l​t​e​r​e​d​ ​N​S​D​a​t​e​ ​o​b​j​e​c​t​ ​t​o​ ​s​e​t​ ​d​a​t​e​L​a​b​e​l​ ​c​o​n​t​e​n​t​s​
 ​ ​ ​ ​[​d​a​t​e​L​a​b​e​l​ ​s​e​t​T​e​x​t​:​
 ​ ​ ​ ​ ​ ​ ​ ​[​d​a​t​e​F​o​r​m​a​t​t​e​r​ ​s​t​r​i​n​g​F​r​o​m​D​a​t​e​:​[​p​o​s​s​e​s​s​i​o​n​ ​d​a​t​e​C​r​e​a​t​e​d​]​]​]​;​

 ​ ​ ​ ​/​/​ ​C​h​a​n​g​e​ ​t​h​e​ ​n​a​v​i​g​a​t​i​o​n​ ​i​t​e​m​ ​t​o​ ​d​i​s​p​l​a​y​ ​n​a​m​e​ ​o​f​ ​p​o​s​s​e​s​s​i​o​n​
 ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​I​t​e​m​]​ ​s​e​t​T​i​t​l​e​:​[​p​o​s​s​e​s​s​i​o​n​ ​p​o​s​s​e​s​s​i​o​n​N​a​m​e​]​]​;​
}​

Now you must invoke this method when the ItemDetailViewController is being pushed onto the navigation stack. In ItemsViewController.m, add the following line of code to tableView:didSelectRowAtIndexPath:.

-​ ​(​v​o​i​d​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​a​T​a​b​l​e​V​i​e​w​
 ​ ​ ​ ​d​i​d​S​e​l​e​c​t​R​o​w​A​t​I​n​d​e​x​P​a​t​h​:​(​N​S​I​n​d​e​x​P​a​t​h​ ​*​)​i​n​d​e​x​P​a​t​h​
{​
 ​ ​ ​ ​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​=​
 ​ ​ ​ ​ ​ ​ ​ ​[​[​[​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​ ​i​n​i​t​]​ ​a​u​t​o​r​e​l​e​a​s​e​]​;​


 ​ ​ ​ ​N​S​A​r​r​a​y​ ​*​p​o​s​s​e​s​s​i​o​n​s​ ​=​ ​[​[​P​o​s​s​e​s​s​i​o​n​S​t​o​r​e​ ​d​e​f​a​u​l​t​S​t​o​r​e​]​ ​a​l​l​P​o​s​s​e​s​s​i​o​n​s​]​;​

 ​ ​ ​ ​/​/​ ​G​i​v​e​ ​d​e​t​a​i​l​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​ ​a​ ​p​o​i​n​t​e​r​ ​t​o​ ​t​h​e​ ​p​o​s​s​e​s​s​i​o​n​ ​o​b​j​e​c​t​ ​i​n​ ​r​o​w​
 ​ ​ ​ ​[​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​P​o​s​s​e​s​s​i​o​n​:​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​s​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​[​i​n​d​e​x​P​a​t​h​ ​r​o​w​]​]​]​;​

 ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​]​ ​p​u​s​h​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​
}​

Many programmers new to iOS struggle with how data is passed between UIViewControllers. The technique you just implemented, having all of the data in the root view controller and passing subsets of that data to the next UIViewController, is a clean and efficient way of performing this task.

Build and run your application. Select one of the rows in the UITableView, and the view that appears will contain the information for the Possession in that row. While you can edit this data, the UITableView won’t have changed when you return to it. To fix this problem, you need to implement code to update the properties of the Possession being edited. Next, we’ll look at when to do this.

Appearing and disappearing views

Whenever a UINavigationController is about to swap views, it sends out two messages: viewWillDisappear: and viewWillAppear:. The UIViewController that is about to be popped off the stack is sent the message viewWillDisappear:. The UIViewController that will then be on top of the stack is sent viewWillAppear:.

When ItemDetailViewController is popped off the stack, you will set the properties of the possession to the values in the UITextFields. When implementing these methods for views appearing and disappearing, it is important to call the superclass’s implementation – it has some work to do as well. In ItemDetailViewController.m, implement viewWillDisappear:.

-​ ​(​v​o​i​d​)​v​i​e​w​W​i​l​l​D​i​s​a​p​p​e​a​r​:​(​B​O​O​L​)​a​n​i​m​a​t​e​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​W​i​l​l​D​i​s​a​p​p​e​a​r​:​a​n​i​m​a​t​e​d​]​;​

 ​ ​ ​ ​/​/​ ​C​l​e​a​r​ ​f​i​r​s​t​ ​r​e​s​p​o​n​d​e​r​
 ​ ​ ​ ​[​[​s​e​l​f​ ​v​i​e​w​]​ ​e​n​d​E​d​i​t​i​n​g​:​Y​E​S​]​;​

 ​ ​ ​ ​/​/​ ​"​S​a​v​e​"​ ​c​h​a​n​g​e​s​ ​t​o​ ​p​o​s​s​e​s​s​i​o​n​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​P​o​s​s​e​s​s​i​o​n​N​a​m​e​:​[​n​a​m​e​F​i​e​l​d​ ​t​e​x​t​]​]​;​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​S​e​r​i​a​l​N​u​m​b​e​r​:​[​s​e​r​i​a​l​N​u​m​b​e​r​F​i​e​l​d​ ​t​e​x​t​]​]​;​
 ​ ​ ​ ​[​p​o​s​s​e​s​s​i​o​n​ ​s​e​t​V​a​l​u​e​I​n​D​o​l​l​a​r​s​:​[​[​v​a​l​u​e​F​i​e​l​d​ ​t​e​x​t​]​ ​i​n​t​V​a​l​u​e​]​]​;​
}​

Notice the use of endEditing:. When the message endEditing: is sent to a view, if it or any of its subviews are currently the first responder, it will resign its first responder status, and the keyboard will be dismissed. (The argument passed determines whether the first responder is forced into retirement. Some first responders might refuse to resign, and passing YES ignores that refusal.)

Now the values of the Possession will be updated when the user taps the Homepwner back button on the UINavigationBar. When ItemsViewController appears back on the screen, it is sent the message viewWillAppear:. Take this opportunity to reload its UITableView so the user can immediately see the changes. In ItemsViewController.m, override viewWillAppear:.

-​ ​(​v​o​i​d​)​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​(​B​O​O​L​)​a​n​i​m​a​t​e​d​
{​
 ​ ​ ​ ​[​s​u​p​e​r​ ​v​i​e​w​W​i​l​l​A​p​p​e​a​r​:​a​n​i​m​a​t​e​d​]​;​
 ​ ​ ​ ​[​[​s​e​l​f​ ​t​a​b​l​e​V​i​e​w​]​ ​r​e​l​o​a​d​D​a​t​a​]​;​
}​

Build and run your application now. Now you can move back and forth between the UIViewControllers you created and change the data with ease.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.144.65