Tuesday, September 20, 2011

Adding the "Congrats" view



Ok, now that we've figured out the percent correct and decided what to do when the answers aren't all correct, we've finally arrived at the point of figuring out what to do when the answers *are* all correct. And for that, we want to display a kind of "congratulations" view which shows a nice image of original art, and then pops them back to the start display.

So, it looks like we will build a quick view controller. That we can do with new, file and choose ui view controller subclass. We'll call it "NextLevelController".

Ok, move the xlb to resources, and then do what? Open up the xlb, then add a label saying "Congratulations - you have advanced to the next level!". Then, add a UIImageViewControl. Add a couple more labels underneath, one for the image title and one to the link for the url for the artist's site. And an ok button, or "Next Level" button.

Ok, here's the header:


#import


@interface NextLevelController : UIViewController {
UIImageView *image;
UILabel *art_title;
UILabel *url;

}

@property (nonatomic, retain) IBOutlet UIImageView *image;
@property (nonatomic, retain) IBOutlet UILabel *url;
@property (nonatomic, retain) IBOutlet UILabel *art_title;


@end


With corresponding @synthesize - I guess title is reserved or something, had to change to art_title.

Ok, making progress. Oh, yeah, let's create a procedure to call the finish.


- (IBAction)nextLevel
{
NSLog(@"NextLevelController, nextLevel called");
}


And a corresponding entry in the header. Ok, now, let's hook them up to the IB (this is always my favorite part).

Ok, let's run it and take a look.

Ah, actually, we haven't yet coded the pop logic. We can just throw this into the 100% correct logic:

QuizAppDelegate *delegate =
(QuizAppDelegate *)[[UIApplication sharedApplication] delegate];

[delegate.navController pushViewController:nextLevelController animated:YES];


The problem is I still need to have some kind of a variable. And in the StartController, the QuestionController and the SettingController were represented like so:

@interface StartController : UIViewController {

QuestionController *questionController;
SettingsController *settingsController;

}

// name the controller as a property

@property (nonatomic, retain) IBOutlet QuestionController *questionController;
@property (nonatomic, retain) IBOutlet SettingsController *settingsController;
@property (nonatomic, retain) AppState *appState;


And in the IB, they were represented in StartController.xlb as UIViews, I believe, and I was able to connect them.

It was under referencing outlets, which you can see by right clicking on the view, like so:




But, when I do the same in QuestionController.xlb, nothing shows up. I *hate* when stuff like this happens with ib.

Ah, I forgot - you have to set the class on the view controller. Phew. I hate this tricky IB stuff. I know, I just said I loved it. But, only when I don't get hung up it. It wasn't too bad this time.

Well, the victory is short-lived. It's crashing on a bad access now:

[delegate.navController pushViewController:nextLevelController animated:YES];



Either the problem is in the navController, or the nextLevelController.

Ok, maybe it's time I ventured off that track and tried a modal view. There's a good thread here:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/3643-pushviewcontroller-versus-presentmodalviewcontroller.html

that talks about it.

The nice thing about it is that he uses the initWithNibName, which I haven't used yet.

AnotherViewController *anotherViewController = [[AnotherViewController alloc] initWithNibName:@"AnotherView" bundle:nil];


[self pushViewController:anotherViewController animated:YES];

versus

[self presentModalViewController:anotherViewController animated:YES];



Let's see if we can work though the initWithNibName:

Oh, I see, maybe, what the problem was - I'm obviously not *creating* the viewController. I guess. I though that attaching to to the view in the nib might do it. Am I creating it in start controller?

No, that's what I thought. Anyway, let's try the modal view.


NextLevelController *nextLevelController = [[NextLevelController alloc] initWithNibName:@"NextLevelController" bundle:nil];
//[self pushViewController:nextLevelController animated:YES];
[self presentModalViewController:nextLevelController animated:YES];



Nooooo - it shows up as a blank. The dreaded blank display.

Well, some searching on the net shows this:


UIView *modalView = [[[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
modalView.opaque = NO;
modalView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5f];

UILabel *label = [[[UILabel alloc] init] autorelease];
label.text = @"Modal View";
label.textColor = [UIColor whiteColor];
label.backgroundColor = [UIColor blueColor];
label.opaque = YES;
[label sizeToFit];
[modalView addSubview:label];

[self.view addSubview:modalView];


So, this is building without the benefit of the IB. It actually does something, but not what I want - it's semi-transparent, no mater what I set opaque to. Anyway, I want to be able to create the view using IB, because it's so much easier.

So, the problem is, I don't really get the whole concept of the Nav View Controller. What is this thing? This thing here:

UINavigationController *navController;

It's defined in the App Delegate.

From the docs:

The UINavigationController class implements a specialized view controller that manages the navigation of hierarchical content. This class is not intended for subclassing. Instead, you use instances of it as-is in situations where you want your application’s user interface to reflect the hierarchical nature of your content. This navigation interface makes it possible to present your data efficiently and also makes it easier for the user to navigate that content.

The screens presented by a navigation interface typically mimic the hierarchical organization of your data. At each level of the hierarchy, you provide an appropriate screen (managed by a custom view controller) to display the content at that level. Figure 1 shows an example of the navigation interface presented by the Settings application in iOS Simulator. The first screen presents the user with the list of applications that contain preferences. Selecting an application reveals individual settings and groups of settings for that application. Selecting a group yields more settings and so on. For all but the root view, the navigation controller provides a back button to allow the user to move back up the hierarchy.


Ok - so it manages the navigation of hierarchical content - provides a back button for all but the root view. A specialized view controller.

More:


A navigation controller object manages the currently displayed screens using the navigation stack.

// so this is what provides the navigation stack.

At the bottom of this stack is the root view controller and at the top of the stack is the view controller currently being displayed.

// ok.

You use the methods of your navigation controller object to modify the stack at runtime.

// ok.

The most common operation is to push new view controllers onto the stack using the pushViewController:animated: method.

// right. That's exactly what I'm doing.

Pushing a new view controller object onto the stack causes the view of that view controller to be displayed and the navigation controls to be updated to reflect the change.

// not sure what navigation controls are - the back button?

You typically push view controllers in response to the user selecting an item that leads to the next level in your information hierarchy.

// Ok. I'm starting to get the picture.

In addition to pushing view controllers onto the navigation stack, you can also pop them using the popViewControllerAnimated: method.

// Go back from them. Release them to the ether, return to the previous view.

Although you can pop view controllers yourself, the navigation controller also provides a back button (when appropriate) that pops the top view controller in response to user interactions.

// Ok, this is all making sense.

A navigation controller object notifies its delegate object in response to changes in the active view controller.

// Hm? Ok, what is the delegate object in my case?

The delegate object is a custom object provided by your application that conforms to the UINavigationControllerDelegate protocol.

// Ok - let's check out the app delegate. No that just conforms to this:



You can use the methods of this protocol to respond to the change and perform additional setup or cleanup tasks.

For more information about how to integrate navigation controllers into your application, see View Controller Programming Guide for iOS.


Well, so, where is this delegate object?

Wait - I just had a *wicked good idea*. Why not pop the question view off the controller, and let the start controller handle it? That's cleaner, anyway.

Ah, here's the method I need:

/* call back method on pop of answer page */

- (void) viewWillAppear:(BOOL)animated{
// won't be animated on the first call


Ok, after a bit of fussing around, I ended up with this:


- (void) viewWillAppear:(BOOL)animated{

if (animated) {

if (appState.correctAnswers == [appState.shuffledQuestionDictKeyList count]){

QuizAppDelegate *delegate =
(QuizAppDelegate *)[[UIApplication sharedApplication] delegate];

[delegate.navController pushViewController:nextLevelViewController animated:YES];

}

}





And I just did a new nib with a new view controller - at it showed up:

No comments:

Post a Comment