Modal View Controllers

In this part of the exercise, you will update Homepwner to present the ItemDetailViewController modally – but only when the user creates a new Possession. When the user selects an existing Possession, the ItemDetailViewController will be pushed onto the UINavigationController’s stack as before.

Figure 14.5  New item

New item

To implement this dual usage of ItemDetailViewController, you will give it a new designated initializer, initForNewItem:isNew:. This initializer will check whether the instance is being used for creating a new Possession or for showing an existing one and then configure the interface accordingly.

In ItemDetailViewController.h, declare this initializer.

}​
-​ ​(​i​d​)​i​n​i​t​F​o​r​N​e​w​I​t​e​m​:​(​B​O​O​L​)​i​s​N​e​w​;​

@​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​;​

If the ItemDetailViewController is being used to create a new Possession, it will show a Done button and a Cancel button on its navigation item. Implement this method in ItemDetailViewController.m.

-​ ​(​i​d​)​i​n​i​t​F​o​r​N​e​w​I​t​e​m​:​(​B​O​O​L​)​i​s​N​e​w​
{​
 ​ ​ ​ ​s​e​l​f​ ​=​ ​[​s​u​p​e​r​ ​i​n​i​t​W​i​t​h​N​i​b​N​a​m​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​"​ ​b​u​n​d​l​e​:​n​i​l​]​;​

 ​ ​ ​ ​i​f​ ​(​s​e​l​f​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​i​s​N​e​w​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​I​B​a​r​B​u​t​t​o​n​I​t​e​m​ ​*​d​o​n​e​I​t​e​m​ ​=​ ​[​[​U​I​B​a​r​B​u​t​t​o​n​I​t​e​m​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​B​a​r​B​u​t​t​o​n​S​y​s​t​e​m​I​t​e​m​:​U​I​B​a​r​B​u​t​t​o​n​S​y​s​t​e​m​I​t​e​m​D​o​n​e​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​a​r​g​e​t​:​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​o​n​:​@​s​e​l​e​c​t​o​r​(​s​a​v​e​:​)​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​I​t​e​m​]​ ​s​e​t​R​i​g​h​t​B​a​r​B​u​t​t​o​n​I​t​e​m​:​d​o​n​e​I​t​e​m​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​d​o​n​e​I​t​e​m​ ​r​e​l​e​a​s​e​]​;​


 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​U​I​B​a​r​B​u​t​t​o​n​I​t​e​m​ ​*​c​a​n​c​e​l​I​t​e​m​ ​=​ ​[​[​U​I​B​a​r​B​u​t​t​o​n​I​t​e​m​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​B​a​r​B​u​t​t​o​n​S​y​s​t​e​m​I​t​e​m​:​U​I​B​a​r​B​u​t​t​o​n​S​y​s​t​e​m​I​t​e​m​C​a​n​c​e​l​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​t​a​r​g​e​t​:​s​e​l​f​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​a​c​t​i​o​n​:​@​s​e​l​e​c​t​o​r​(​c​a​n​c​e​l​:​)​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​I​t​e​m​]​ ​s​e​t​L​e​f​t​B​a​r​B​u​t​t​o​n​I​t​e​m​:​c​a​n​c​e​l​I​t​e​m​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​c​a​n​c​e​l​I​t​e​m​ ​r​e​l​e​a​s​e​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​

 ​ ​ ​ ​r​e​t​u​r​n​ ​s​e​l​f​;​
}​

In the past, when you’ve changed the designated initializer of a class from its superclass’ designated initializer, you’ve overridden the superclass’ initializer to call the new one. In this case, you’re going to make it illegal to use the superclass’ designated initializer by throwing an exception when anyone calls it.

In ItemDetailViewController.m, override UIViewController’s designated initializer.

-​ ​(​i​d​)​i​n​i​t​W​i​t​h​N​i​b​N​a​m​e​:​(​N​S​S​t​r​i​n​g​ ​*​)​n​i​b​N​a​m​e​ ​b​u​n​d​l​e​:​(​N​S​B​u​n​d​l​e​ ​*​)​b​u​n​d​l​e​
{​
 ​ ​ ​ ​@​t​h​r​o​w​ ​[​N​S​E​x​c​e​p​t​i​o​n​ ​e​x​c​e​p​t​i​o​n​W​i​t​h​N​a​m​e​:​@​"​W​r​o​n​g​ ​i​n​i​t​i​a​l​i​z​e​r​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​r​e​a​s​o​n​:​@​"​U​s​e​ ​i​n​i​t​F​o​r​N​e​w​I​t​e​m​:​"​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​u​s​e​r​I​n​f​o​:​n​i​l​]​;​
 ​ ​ ​ ​r​e​t​u​r​n​ ​n​i​l​;​
}​

This code creates an autoreleased instance of NSException with a name and a reason and then throws an exception. This halts the application and shows the exception in the console.

To confirm that this exception will be thrown, let’s return to where initWithNibName:bundle: is currently called – the tableView:didSelectRowAtIndexPath: method of ItemsViewController. In this method, ItemsViewController creates an instance of ItemDetailViewController and sends it the message init, which eventually calls initWithNibName:bundle:. Therefore, selecting a row in the table view will result in the Wrong initializer exception being thrown.

Build and run the application and tap a row. You will see an exception in the console, and your application will halt. Notice that the name and the reason are part of the console message. The debugger will show you that sending init to the ItemDetailViewController was the cause.

You don’t want to see this exception again, so in ItemsViewController.m, update tableView:didSelectRowAtIndexPath: to use the new initializer.

-​ ​(​v​o​i​d​)​t​a​b​l​e​V​i​e​w​:​(​U​I​T​a​b​l​e​V​i​e​w​ ​*​)​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​F​o​r​N​e​w​I​t​e​m​:​N​O​]​ ​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​]​;​

Build and run the application again. Nothing new and exciting will happen, but your application should no longer crash when you select a row in the table.

Now that we’ve got our new initializer in place, let’s change what happens when the user adds a new possession.

In ItemsViewController.m, edit the addNewPossession: method to create an instance of ItemDetailViewController in a UINavigationController and present the navigation controller modally.

-​ ​(​I​B​A​c​t​i​o​n​)​a​d​d​N​e​w​P​o​s​s​e​s​s​i​o​n​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​n​e​w​P​o​s​s​e​s​s​i​o​n​ ​=​ ​[​[​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​]​ ​c​r​e​a​t​e​P​o​s​s​e​s​s​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​ ​*​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​F​o​r​N​e​w​I​t​e​m​:​Y​E​S​]​;​

 ​ ​ ​ ​[​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​:​n​e​w​P​o​s​s​e​s​s​i​o​n​]​;​

 ​ ​ ​ ​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​*​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​=​ ​[​[​U​I​N​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​ ​a​l​l​o​c​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​n​i​t​W​i​t​h​R​o​o​t​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​]​;​

 ​ ​ ​ ​[​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​r​e​l​e​a​s​e​]​;​

 ​ ​ ​ ​/​/​ ​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​i​s​ ​r​e​t​a​i​n​e​d​ ​b​y​ ​s​e​l​f​ ​w​h​e​n​ ​p​r​e​s​e​n​t​e​d​
 ​ ​ ​ ​[​s​e​l​f​ ​p​r​e​s​e​n​t​M​o​d​a​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​a​n​i​m​a​t​e​d​:​Y​E​S​]​;​

 ​ ​ ​ ​[​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​r​e​l​e​a​s​e​]​;​
}​

Build and run the application and tap the New button to create a new possession. An instance of ItemDetailViewController will slide up from the bottom of the screen – but now it has a Done button and a Cancel button on its navigation item. (Tapping these buttons, of course, will throw an exception since you haven’t implemented the action methods yet.)

Dismissing modal view controllers

Every view controller has a property named modalViewController. If a view controller presents another view controller modally, then this property holds a pointer to that view controller. Every modally-presented view controller has a pointer named parentViewController that it sets to the view controller that presented it (Figure 14.6). These relationships are useful when it comes time to dismiss modal view controllers.

Figure 14.6  Parent-Modal Relationship

Parent-Modal Relationship

When a view controller is sent the message dismissModalViewControllerAnimated:, it removes its modalViewController’s view from the screen, releases that view controller, and sets its modalViewController property to nil. The modalViewController’s parentViewController property is also set to nil. Therefore, when either of these two buttons is tapped, we need to tell the parentViewController of the ItemDetailViewController to dismissModalViewControllerAnimated:.

At the top of ItemDetailViewController.m, make the following import statement.

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

Then, implement the action methods in ItemDetailViewController.m to dismiss the view controller.

-​ ​(​I​B​A​c​t​i​o​n​)​s​a​v​e​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​m​e​s​s​a​g​e​ ​g​e​t​s​ ​f​o​r​w​a​r​d​e​d​ ​t​o​ ​t​h​e​ ​p​a​r​e​n​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​m​i​s​s​M​o​d​a​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​]​;​
}​

-​ ​(​I​B​A​c​t​i​o​n​)​c​a​n​c​e​l​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​/​/​ ​I​f​ ​t​h​e​ ​u​s​e​r​ ​c​a​n​c​e​l​l​e​d​,​ ​t​h​e​n​ ​r​e​m​o​v​e​ ​t​h​e​ ​P​o​s​s​e​s​s​i​o​n​ ​f​r​o​m​ ​t​h​e​ ​s​t​o​r​e​
 ​ ​ ​ ​[​[​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​]​ ​r​e​m​o​v​e​P​o​s​s​e​s​s​i​o​n​:​p​o​s​s​e​s​s​i​o​n​]​;​

 ​ ​ ​ ​/​/​ ​T​h​i​s​ ​m​e​s​s​a​g​e​ ​g​e​t​s​ ​f​o​r​w​a​r​d​e​d​ ​t​o​ ​t​h​e​ ​p​a​r​e​n​t​V​i​e​w​C​o​n​t​r​o​l​l​e​r​
 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​m​i​s​s​M​o​d​a​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​]​;​
}​

Notice that this code sends dismissModalViewControllerAnimated: to the ItemDetailViewController. This is odd because ItemDetailViewController is not the parentViewController. It turns out that view controllers are smarter than they look. If you send the message dismissModalViewControllerAnimated: to a modal view controller (like ItemDetailViewController), it thinks, I don’t have a modalViewController... I must be the modalViewController. I’ll forward this message to my parent.

Build and run the application. Create a new possession and tap the Cancel button. The instance of ItemDetailViewController will slide off the screen, and nothing will be added to the table view. Then, create a new possession and tap the Done button. The ItemDetailViewController will slide off the screen, and your new Possession will appear in the table view.

Modal view controller styles

On the iPhone or iPod touch, a modal view controller takes over the entire screen. This is the default behavior and the only possibility on these devices. On the iPad, you have two additional options: a form sheet style and a page sheet style. You change the presentation of the modal view controller by setting its modalPresentationStyle property to a pre-defined constant – UIModalPresentationFormSheet or UIModalPresentationPageSheet.

The form sheet style shows the modal view controller’s view in a rectangle in the center of the iPad’s screen and dims out the presenting view controller’s view (Figure 14.7). The page sheet style is the same as the default full-screen style in portrait mode. In landscape mode, it keeps the same width as in portrait mode and dims the left and right edges of the presenting view controller’s view that stick out behind it.

Figure 14.7  Form Sheet

Form Sheet

In ItemsViewController.m, modify the addNewPossession: method to change the presentation style of the UINavigationController that is being presented.

[​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​S​t​y​l​e​:​U​I​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​F​o​r​m​S​h​e​e​t​]​;​

[​s​e​l​f​ ​p​r​e​s​e​n​t​M​o​d​a​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​n​a​v​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 on the iPad simulator or on an iPad. Tap the button to add a new possession and watch the modal view controller slide onto the screen. Add some possession details and then tap the Done button. The table view reappears, but your new Possession isn’t there. What happened?

Before you changed its presentation style, the modal view controller took up the entire screen, which caused the view of the ItemsViewController to disappear. When the modal view controller was dismissed, the ItemsViewController was sent the messages viewWillAppear: and viewDidAppear: and took this opportunity to reload its table to catch any updates to the PossessionStore.

With the new presentation style, the ItemsViewController’s view doesn’t disappear when it presents the view controller. So it isn’t sent the re-appearing messages when the modal view controller is dismissed, and it doesn’t get the chance to reload its table view.

We have to find another opportunity for the ItemsViewController to reload its table view. We know that to dismiss the ItemDetailViewController, we send it the message dismissModalViewControllerAnimated: – a message it forwards to its parentViewController. One potential approach, then, is to override dismissModalViewControllerAnimated: in ItemsViewController and reload the table there.

But the ItemDetailViewController’s parentViewController is not the ItemsViewController; it’s the navigation controller that holds the ItemDetailViewController. No problem you say, I’ll just get the parentViewController of the navigation controller... which is the ItemsViewController, right?

Unfortunately, no. When a view controller presents a modal view controller, the parentViewController of the modal view controller is set to be the parent of the presenting view controller. Thus, the parentViewController of the UINavigationController that holds the ItemDetailViewController is set to the UINavigationController that holds the ItemsViewController. These relationships are shown in Figure 14.8.

Figure 14.8  Parent-Modal Relationships

Parent-Modal Relationships

Writing a view controller delegate protocol

Instead of relying on the (somewhat complicated) relationships that the SDK has established, we’re going to create a new relationship between these two view controllers so that they can send messages to each other directly. But how should we structure this relationship? Let’s consider three options:

  • For view controllers that are closely related, like a table view controller and its detail view controller, you can simply give them instance variables that point to each other. This is the easiest way to send messages between two view controllers, but it is also the least adaptable.
    /​/​ ​S​e​n​d​ ​m​e​s​s​a​g​e​ ​t​o​ ​a​n​o​t​h​e​r​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​,​ ​k​e​p​t​ ​a​s​ ​i​n​s​t​a​n​c​e​ ​v​a​r​i​a​b​l​e​
    [​m​y​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​s​h​o​w​I​t​e​m​:​m​y​I​t​e​m​]​;​
    
  • You can also create a delegate protocol that specifies the messages one view controller can send to the others. This technique allows multiple controllers to adopt the same protocol. It also means the view controller sending the message does not have to know what type of object it is sending the message to; it only needs to know that it conforms to the protocol.
    /​/​ ​A​ ​p​r​o​t​o​c​o​l​ ​s​p​e​c​i​f​i​e​d​ ​b​y​ ​t​h​e​ ​s​e​n​d​i​n​g​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​
    @​p​r​o​t​o​c​o​l​ ​I​t​e​m​S​h​o​w​i​n​g​P​r​o​t​o​c​o​l​
    -​ ​(​v​o​i​d​)​s​h​o​w​I​t​e​m​:​(​i​d​)​s​e​n​d​e​r​;​
    @​e​n​d​
    
    /​/​ ​S​e​n​d​ ​m​e​s​s​a​g​e​ ​t​o​ ​a​n​o​t​h​e​r​ ​v​i​e​w​ ​c​o​n​t​r​o​l​l​e​r​
    i​d​ ​<​I​t​e​m​S​h​o​w​i​n​g​P​r​o​t​o​c​o​l​>​ ​d​e​t​a​i​l​ ​=​
     ​ ​ ​ ​[​[​[​s​e​l​f​ ​n​a​v​i​g​a​t​i​o​n​C​o​n​t​r​o​l​l​e​r​]​ ​v​i​e​w​C​o​n​t​r​o​l​l​e​r​s​]​ ​o​b​j​e​c​t​A​t​I​n​d​e​x​:​1​]​;​
    
    [​d​e​t​a​i​l​ ​s​h​o​w​I​t​e​m​:​m​y​I​t​e​m​]​;​
    
  • The third approach is to use the notification system. This solution is essential when the view controllers are not related. It is also useful when multiple view controllers are interested in a single controller. A view controller can post to the NSNotificationCenter, and interested objects can register for that notification.

In our case, you could choose to give the ItemDetailViewController a pointer to the ItemsViewController that effectively presented it. Then, when the ItemDetailViewController is dismissed, it would send a message to the ItemsViewController to say, I’m being removed from the screen – do what you need to do.

However, it is possible that a different view controller might one day be responsible for presenting the ItemDetailViewController. To prepare for this possibility, you’re going to create a delegate protocol for ItemDetailViewController and give it a delegate property. Although the ItemsViewController will serve as the delegate, using the delegate pattern will allow any object to present the ItemDetailViewController and be informed when it is dismissed.

In ItemDetailViewController.h, create a new protocol at the top of the file.

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

@​p​r​o​t​o​c​o​l​ ​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​l​e​g​a​t​e​ ​<​N​S​O​b​j​e​c​t​>​

@​o​p​t​i​o​n​a​l​
-​ ​(​v​o​i​d​)​i​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​W​i​l​l​D​i​s​m​i​s​s​:​(​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​)​v​c​;​

@​e​n​d​

@​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​

In ItemDetailViewController.h, add a new property to ItemDetailViewController.

-​ ​(​i​d​)​i​n​i​t​F​o​r​N​e​w​I​t​e​m​:​(​B​O​O​L​)​i​s​N​e​w​;​

@​p​r​o​p​e​r​t​y​ ​(​n​o​n​a​t​o​m​i​c​,​ ​a​s​s​i​g​n​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​i​d​ ​<​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​l​e​g​a​t​e​>​ ​d​e​l​e​g​a​t​e​;​

@​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​;​

In ItemDetailViewController.m, synthesize this property.

@​s​y​n​t​h​e​s​i​z​e​ ​d​e​l​e​g​a​t​e​;​

(Notice that we did not create an instance variable for the delegate. Remember that when you synthesize a property that does not have a matching instance variable, the instance variable is automatically created for you.)

Now, in ItemDetailViewController.m, update the two action methods to inform its delegate that it is being dismissed. Because the delegate method is optional, use respondsToSelector: to make sure the delegate implements the method before you call it.

-​ ​(​I​B​A​c​t​i​o​n​)​s​a​v​e​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​m​i​s​s​M​o​d​a​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​]​;​

 ​ ​ ​ ​i​f​(​[​d​e​l​e​g​a​t​e​ ​r​e​s​p​o​n​d​s​T​o​S​e​l​e​c​t​o​r​:​@​s​e​l​e​c​t​o​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​W​i​l​l​D​i​s​m​i​s​s​:​)​]​)​
 ​ ​ ​ ​ ​ ​ ​ ​[​d​e​l​e​g​a​t​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​W​i​l​l​D​i​s​m​i​s​s​:​s​e​l​f​]​;​
}​
-​ ​(​I​B​A​c​t​i​o​n​)​c​a​n​c​e​l​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​[​[​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​]​ ​r​e​m​o​v​e​P​o​s​s​e​s​s​i​o​n​:​p​o​s​s​e​s​s​i​o​n​]​;​

 ​ ​ ​ ​[​s​e​l​f​ ​d​i​s​m​i​s​s​M​o​d​a​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​]​;​

 ​ ​ ​ ​i​f​(​[​d​e​l​e​g​a​t​e​ ​r​e​s​p​o​n​d​s​T​o​S​e​l​e​c​t​o​r​:​@​s​e​l​e​c​t​o​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​W​i​l​l​D​i​s​m​i​s​s​:​)​]​)​
 ​ ​ ​ ​ ​ ​ ​ ​[​d​e​l​e​g​a​t​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​W​i​l​l​D​i​s​m​i​s​s​:​s​e​l​f​]​;​
}​

In ItemsViewController.h, declare that ItemsViewController conforms to this new protocol and import the header 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​
 ​ ​ ​ ​<​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​l​e​g​a​t​e​>​

In ItemsViewController.m, implement the method from the delegate protocol to reload the table.

-​ ​(​v​o​i​d​)​i​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​W​i​l​l​D​i​s​m​i​s​s​:​(​I​t​e​m​D​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​*​)​v​c​
{​
 ​ ​ ​ ​[​[​s​e​l​f​ ​t​a​b​l​e​V​i​e​w​]​ ​r​e​l​o​a​d​D​a​t​a​]​;​
}​

Then, update addNewPossession: in ItemsViewController.m to set the delegate of the presented ItemDetailViewController.

-​ ​(​I​B​A​c​t​i​o​n​)​a​d​d​N​e​w​P​o​s​s​e​s​s​i​o​n​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​P​o​s​s​e​s​s​i​o​n​ ​*​n​e​w​P​o​s​s​e​s​s​i​o​n​ ​=​ ​[​[​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​]​ ​c​r​e​a​t​e​P​o​s​s​e​s​s​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​ ​*​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​F​o​r​N​e​w​I​t​e​m​:​Y​E​S​]​;​

 ​ ​ ​ ​[​d​e​t​a​i​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​D​e​l​e​g​a​t​e​:​s​e​l​f​]​;​

Build and run the application. Create a new Possession and then return to the table view. The table view will update appropriately.

Modal view controller transitions

In addition to the presentation style of a modal view controller, you can change the animation that places it on screen. Just like with the presentation styles, there is a view controller property (modalTransitionStyle) that you can set with a pre-defined constant. By default, the animation will slide the modal view controller up from the bottom of the screen. You can also have the view controller fade in, flip in, or appear underneath a page curl (like in the Maps application).

In ItemsViewController.m, update the addNewPossession: method to use a different transition.

[​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​S​t​y​l​e​:​U​I​M​o​d​a​l​P​r​e​s​e​n​t​a​t​i​o​n​F​o​r​m​S​h​e​e​t​]​;​
[​n​a​v​C​o​n​t​r​o​l​l​e​r​ ​s​e​t​M​o​d​a​l​T​r​a​n​s​i​t​i​o​n​S​t​y​l​e​:​U​I​M​o​d​a​l​T​r​a​n​s​i​t​i​o​n​S​t​y​l​e​F​l​i​p​H​o​r​i​z​o​n​t​a​l​]​;​

[​s​e​l​f​ ​p​r​e​s​e​n​t​M​o​d​a​l​V​i​e​w​C​o​n​t​r​o​l​l​e​r​:​n​a​v​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 and notice the change in animation. Try out some of the other options, but make sure to read the fine print in the documentation. For instance, you can’t use the page curl transition unless the presentation style is full screen. Also, note that these transitions will still work if you switch back to deploying on an iPhone. The presentation style, however, will always be full screen.

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

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