3

I am a novice programmer for the iPhone. In developing my first game/app, I have come up with a problem (created a problem) for myself. I have researched this and think I have seen answers but I don't understand how to make them work for my application.

I have a game that has a few view controllers: Welcome, Play, High Scores, Preferences and About. I don't have a navigation controller or a root controller of which I am aware. EDIT - I now have a rootview controller - I added [self.window setRootViewController:viewController]; to my appDelegate.m file

From the Welcome screen/viewcontroller I can go to the Play view controller, the about view controller, the high scores view controller or the prefs view controller. I would like to make whichever new controller I go to now be in charge, so that I can dismiss the Welcome view controller. In fact, any time I go to a view controller, I would like to be able to dismiss from memory any of the other view controllers - especially the one I just came from. I can go to most of the viewcontrollers from most of the viewcontrollers.

I go to the new view controllers using this:

- (IBAction)playGameAction:(id)sender {
    FlipTestViewController *myViewController = [[FlipTestViewController alloc]

   initWithNibName:@"FlipTestViewController" bundle:nil];

    [UIView beginAnimations:@"flipview" context:nil];
    [UIView setAnimationDuration:1];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft 
                           forView:self.view cache:YES];

    [self.view addSubview:myViewController.view];

    [UIView commitAnimations];
}

What do I do to let the WeclomeViewController go, once I am safely in the FlipTestViewController (the play controller)? And how do I tell the new controller that it is now in charge and the others can go away? Am I even asking a question that makes sense? Please use small words and concrete examples when responding! Thank you!

On rdelmar's advice, I tried adding a call to change the RootvVewController into my button code - the code I use to bring up the new viewController:

- (IBAction)playGameAction:(id)sender {
    FlipTestViewController *myViewController = [[FlipTestViewController alloc]
                                                      initWithNibName:@"FlipTestViewController"
                                                      bundle:nil];

    [UIApplication sharedApplication].delegate.window.rootViewController = myViewController;
    [UIView beginAnimations:@"flipview" context:nil];
    [UIView setAnimationDuration:1];
    [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
    [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft 
                           forView:self.view cache:YES];

    [self.view addSubview:myViewController.view];

    [UIView commitAnimations];

}

Sure enough, then I look in the comments, the FlipTestViewController (my Play Controller) is the new RootViewController. Unfortunately, I get a blank white screen, no transition of any kind and log message telling me that my ViewDidDisappear in my FlipTestViewController is being called somehow. Probably because the Welcome Screen is still there somehow. Memory management problem perhaps? Ought I to take this to a new question?

I just changed the background color of the main.xib file and apparently that is the screen that is showing up (the blank white screen).

Augustus S-R
  • 67
  • 1
  • 8
  • How do you want to go from one view controller to another? Will you have 3 or 4 buttons in each view that allow you to go to any other view controller? What's wrong with a tab bar controller? Do you just not want that tab bar at the bottom? That seems like the most logical way to give you the freedom to move from any controller to any other. – rdelmar Aug 18 '12 at 02:57

3 Answers3

2

To present a view controller modally, you might want to try this method:

- (IBAction)playGameAction:(id)sender {
    FlipTestViewController *myViewController = [[FlipTestViewController alloc] initWithNibName:@"FlipTestViewController" bundle:nil];

    myViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
    [self presentViewController:myViewController animated:YES completion:nil];
}

This puts FlipTestViewController on top of the current view with a flip transition style.

Then to dismiss the view controller, you'd hook this up to a control (usually a button) in FlipTestViewController:

- (IBAction)dismissViewController:(id)sender {
    [self dismissViewControllerAnimated:YES completion:nil];
}

This dismisses the view controller with the same flip animation

Scott Bossak
  • 2,471
  • 20
  • 17
  • Thank you Scott, but I am not doing modal, am I? I can go from the second view controller to any of the other view controllers. I don't want to have to always go back to the welcomeviewcontroller to get to any of the others - which is what I would have to do if I brought a controller in as a modal view, right? – Augustus S-R Aug 17 '12 at 20:53
  • If you're looking to go to many views, you might be looking for a UINavigationController. That will let you continue to push views on to the view controller stack. – Scott Bossak Aug 17 '12 at 21:00
  • Thank you again, Scott - I am trying to do this without a navigation controller or a tab controller - kinda like this guy did - http://stackoverflow.com/questions/6817213/how-to-use-multiple-ios-custom-view-controllers-without-a-navigation-controller – Augustus S-R Aug 17 '12 at 21:13
  • Unfortunately, I don't understand in his example, where he gets the name for his controllers. Is there a .name property that can be set somewhere? – Augustus S-R Aug 17 '12 at 21:15
  • 1
    To be clearer, I want to be able to go from any viewcontroller to any other viewcontroller at any point in the application. So, no tab, no navigation controller - I want to be able to roam freely and to dismiss whatever is not currently the current viewcontroller. – Augustus S-R Aug 17 '12 at 21:16
  • Why don't you want to use a navigation controller? – Scott Bossak Aug 17 '12 at 21:36
  • Darn fine question, Scott. I would have to rewrite my entire app - and since I have no singular base view - a Navigation viewcontroller is not appropriate. I want to be able to go from anywhere to anywhere, which is the antithesis of a navigation based application. Or a tab based application. – Augustus S-R Aug 18 '12 at 02:04
  • @AugustusS-R, not true about the tab based application -- it does allow you to move from any controller to any other. Isn't that what you want? – rdelmar Aug 18 '12 at 03:34
  • But don't you have to have a "tab bar" Never have been a fan of tab bars. And in my app, the game takes over the whole window and navigation is done through an alert window type interface. – Augustus S-R Aug 18 '12 at 14:55
1

It sounds like the easiest way to achieve what you want is to just reset the rootViewController property of the app's window. From anywhere in the app you can get a reference to the root view controller with [UIApplication sharedApplication].delegate.window.rootViewController. So, in whatever action method you're using to switch to the next view controller, you could alloc init that controller and then set the window's rootViewController property to that controller. If you keep no other reference to a controller, the old one should be deallocated when you reset the property (which may or may not be what you want -- you might want to have some properties that would keep track of the high score or where the player was in a game, for instance).

In the app delegate there is usually code something like this (when you start with a single view project):

 WelcomeController *welcome = [[WelcomeController alloc] initWithNibName:@"ViewController" bundle:nil];
 self.window.rootViewController = welcome;

So I would put something like that in your app delegate with whatever view controller you want to show at start up. Then in your view controller code (or wherever your putting your buttons to switch view controllers) you would have something similar to (I used a segmented control in my test project):

-(IBAction)selectController:(UISegmentedControl *)sender {
    if (sender.selectedSegmentIndex == 0) {
        PlayViewController *player = [[PlayViewController alloc] initWithNibName:@"PlayViewController" bundle:nil];
        [UIApplication sharedApplication].delegate.window.rootViewController = player;
    }else{
        HighScores *scorer = [[HighScores alloc] initWithNibName:@"HighScores" bundle:nil];
        [UIApplication sharedApplication].delegate.window.rootViewController = scorer;
    }
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • This looks like what I am trying to do. I may be asking more help from you rdelmar! – Augustus S-R Aug 19 '12 at 00:53
  • I have placed this in an NSLog statement to see if I can figure out what the root View Controller is - what is the result of that statement? Is it a number? I tried using %@ but that gave me (null) as a response. – Augustus S-R Aug 19 '12 at 00:59
  • @AugustusS-R, No it should be an object, so I don't know why you would get null -- I think that an app has to have a root view controller. See my edited answer for more details. – rdelmar Aug 19 '12 at 01:10
  • I just posted my code from my app delegate - and I don't see anywhere in my app that I designate a root view controller explicitly. – Augustus S-R Aug 19 '12 at 17:43
  • Well, you should -- I don't know why you don't get an error message. I also don't see where you define viewController in your app delegate. Is that all the code there is? – rdelmar Aug 19 '12 at 20:02
  • Maybe because I did something in Interface Builder? I just added my appdelegate.h code and the only place in the apdelegate.m file that I mention the Welcome view controller. Thank you for sticking with this, rdelmar! – Augustus S-R Aug 19 '12 at 21:54
  • So, I added [self.window setRootViewController:rootViewController] to my app delegate and now I at least have a defined rootViewController. That's a good step, right? – Augustus S-R Aug 19 '12 at 21:57
  • I'm not sure whether that's good or not -- what is rootViewController? Is that an instance of one of your view controller classes? If so, that's not a good choice of a name, since rootViewController is the name of the property belonging to the window -- could be confusing. If you have a Welcome class, and its view is the one you want to come up first then you should set an instance of that class (lets call it "welcome") to be the root view controller: self.window.rootViewController = welcome; – rdelmar Aug 20 '12 at 00:47
  • Mistyped, rdelmar - it is just viewcontroller - which is what the Welcome view controller is called. `[self.window setRootViewController:viewController];` is what the code actually is. Now, when I am in any of my windows, I can call `NSLog(@"Root view controller is %@", [UIApplication sharedApplication].delegate.window.rootViewController);` and I am told that WelcomeViewController is the root view controller. – Augustus S-R Aug 20 '12 at 12:12
  • That's as it should be. So, is your problem solved, or do you still have questions? – rdelmar Aug 20 '12 at 15:28
  • I do - now that I have a rootview controller, how do I a) change whatever viewcontroller I go to to be the new rootview controller. And then b) dismiss the previous view controller? – Augustus S-R Aug 20 '12 at 17:39
  • I had already put the answer to that in my post. Just alloc initWithNibName: the new view controller and set that to be the root view controller. You don't need to do anything to dismiss the previous view controller, it will go away when you set the new one to be root view controller. – rdelmar Aug 20 '12 at 19:42
  • I have tried adding `[UIApplication sharedApplication].delegate.window.rootViewController = self; NSLog(@"From Flip Test Root view controller is %@", [UIApplication sharedApplication].delegate.window.rootViewController);` in several places - the result is a white screen and a message from my FlipTestViewController that its view has been hidden. I hear the music from the game, but only the white screen... – Augustus S-R Aug 20 '12 at 21:42
  • I tried adding it to the button that takes me to the screen. And in the newly arrived at FlipViewController in the viewDidLoad method. – Augustus S-R Aug 20 '12 at 21:43
  • I have no idea why your view would be hidden -- you should edit your post to show what you currently are doing. Do you have a xib file associated with this controller? I think the method should be in the button method, but I'm not sure where you're implementing that button method. – rdelmar Aug 21 '12 at 00:02
  • Delete the [self.view addSubview:myViewController.view]; line,, you shouldn't need that -- making that view controller the root view controller should put it on screen. If that doesn't fix the problem, try commenting out the animation stuff to see if that works (the code I posted worked fine for me, but I didn't have any animations). Finally, what class is your button code in? – rdelmar Aug 21 '12 at 01:17
  • Works but no animation! I am going to close this question and play around some more before seeing if I need to post another question...thank you rdelmar! – Augustus S-R Aug 21 '12 at 02:36
0

An easy approach would be to use a Navigation Controller. You can get the benefits of pushing/popping, which helps for sequences of screens.

You can then pop to a specific controller with:

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated

This allows you to go to a specific view controller that has already been presented, without going through all the others.

Also, a bit less well known, you can just replace the entire stack of controllers with

- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated

which also replaces the base controller with the controller at index zero.

So, you can easily get the top toolbar and all the nav controller functionality, and when you just want to reset everything to some single controller, you just do...

    [navController setViewControllers:@[viewController] animated:YES];

EDIT

It sounds like you're not really sure what you want. Thus, you should give yourself some leeway, and not try to reinvent the wheel.

I think a navigation controller is the way to go. It already knows how to manage multiple controllers, and with setViewControllers you can make it looks like you are just going from any controller to any other, but you get to keep the same nav controller as your main center of work.

Otherwise, you can still do all this yourself with

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion

and

- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion

You can call them from your root view controller. When you want to change controllers, it will then just dismiss the current controller and present the new one.

Jody Hagins
  • 27,943
  • 6
  • 58
  • 87
  • 1
    Thank you Jody! See my comments above. I don't want a top toolbar. I don't want to have one navigation controller. Let freedom reign! Honestly, both of your code snippets make no sense to me. I don't really understand what popping a view controller means. And what does setting my view controllers to an array do? I don't have a toolbar and don't want a toolbar...do I? – Augustus S-R Aug 18 '12 at 02:07
  • 1
    I think I know what I want and know that I don't know how to achieve it. As for your code, I am not sure that I understand how to use it. – Augustus S-R Aug 18 '12 at 14:57
  • Did you read the documentation for those methods? They are pretty clear on what they do. Maybe some experimentation with them would be beneficial. If you want to manually go between view controllers, that's the easiest way to do it. You could implement your own controller container, but that requires even more knowledge about how controllers work. – Jody Hagins Aug 18 '12 at 15:30
  • 1
    By the way, Jody, in iOS 6, the OP could use unwind segues, which jump back to a particular controller and doesn't require you to use a navigation controller. Admittedly iOS 6+ only. – Rob Dec 16 '12 at 17:38
  • @AugustusS-R, what finally worked for you? Custom view container controllers or resetting the root view controller? – Phil Oct 16 '14 at 16:12