I am current developing an game with multiple viewcontroller.
VC1(intro) -> VC2(game selection) ->VC3,VC4,VC5(games) -> VC6(display marks)
all the arrows are modal segue
I use unwind segue to go back from VC6 to VC2 and would like to replay. However, when select a game to go to VC3, the VC3 is not brand new as expected. It appears as the finished game condition.
If I really want to replay it after showing the marks, how can I achieve this with segue and Viewcontroller?
Moreover, how can I completely remove the instance of a previous viewcontroller?
You can go back on the parentViewController chain:
UIViewController *viewController = nil;
do {
viewController = self.parentViewController;
} while (![self isViewControllerImLookingFor:viewController]);
Or you could implement a custom navigation stack manager, and store an array of navigation controllers, similar to how UINavigationController does.
You can manipulate the view controller array of your navigation controller like so
NSMutableArray *navigarray = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
[navigarray removeLastObject]; //navigarray contains all vcs
[[self navigationController] setViewControllers:navigarray animated:YES];
Related
I have a viewController (let's call it VC1), which is displayed when the application starts, it has a menu button that opens modalVC, in modalVC there are three buttons that open VC1, VC2, VC3 (at VC2, VC3 also have this menu button )
I run my application (on VC1), I press on the menu button (modalVC), turn on VC1.
It turns out that I have now two VC1 open? Because every time I press on VC1, is added to the memory of 1-2 mb. How can I remove them from memory?
Must somehow after selecting the controller menu (modalVC) to remove all that was earlier ..how?
The transition from the menu on the VC1, VC2, VC3 do so:
UINavigationController * navController = [[UINavigationController alloc] initWithRootViewController: VC1];
[self presentViewController: navController animated: YES completion: nil];
If i will push menu button and always select VC2 (10 times will do that) app will slow
i tried this after select VC in menu (modalVC)
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray: self.navigationController.viewControllers];
[allViewControllers removeAllObjects];
self.navigationController.viewControllers = allViewControllers;
Your menu implementation is flawed by design. You are creating new instances of each ViewController whenever the user taps on a button, instead of using the one you had already created before. A menu like that should be implemented using ViewController Containment. This way, the container ViewController will create the VC1 VC2 and VC3 whenever they are needed, and won't re-create them if they are already there.
To find out how exactly you're gonna do that, just read the section called "Implementing a Custom Container View Controller" in Apple docs
In android, switching between activities, is fairly straightforward
you call
Intent intent = new Intent(this,NextActivity.class); <- define the next activity
startActivity(intent); <- start the next activity
finish(); < -get rid of the current activity
now in iOS i know how to do this:
UIViewController *nextviewcontroller = [[UIViewController alloc]initWithNibName:#"nextvc" bundle:nil];
[self presentViewcontroller:nextviewcontroller animated:YES completion:nil];
How do I get rid of the current view controller? so that currentviewcontroller dies after presenting nextviewcontroller ?
[self dismissViewController:YES]; doesnt seem to do the trick
the lifecycle methods viewWillDisappear and viewDidDisappear are called even if I don't call [self dismissViewController:YES];
i want "currentviewcontroller" to be removed from the memory, and from the viewcontroller stack, so that clicking "back" in "nextviewcontroller" will go to some thirdviewcontroller that was before currentviewcontroller
In iOS is different, since there's no concept of Activity and everything is more focused on the app itself (in Android you can mix activities from different apps). Therefore, there's no concept of "view controller stack".
The most similar concept is the "navigation stack" of navigation controllers, where you actually push and pop new view controller into some kind of linear navigation. A navigation bar is automatically created and populated with back buttons.
presentViewController will show your view controller modally upon the current one, but you can't thrash the presenting one since it's holding and containing ("defining context") the new one.
If you use a navigation controller for your navigation hierarchy (I don't know if you can), you can override the back button and use something like
UIViewController * prev = self.navigationController.viewControllers[self.navigationController.viewControllers.count -2 ]
[self.navigationController popToViewController:prev animated:YES]
With a modal view controller, you may try something like (I haven't tried but it may work)
[self.presentingViewController.navigationController popViewControllerAnimated:YES]
You should write one of these code into the target action of your close button.
iOS doesn't maintain a global stack of controllers in the way that Android does. Each app shows a controller at its root, and that one is responsible for showing the other controllers in the app. Controllers can display other controllers modally using presentViewcontroller:animated:completion: but the presenting controller remains underneath the presented one.
If your current controller is the root controller, then instead of using presentViewcontroller:animated:completion: you'd just do this:
self.view.window.rootViewController = nextViewController;
It's very common for the root controller to be a UINavigationController, which does manage a stack of controllers. If that is the case, and if your current controller is at the top of the stack, you'd do this:
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:nextViewController animated:YES];
If your setup is different, you'd do something different; it's hard to say what without knowing more. But it's most likely that you'd be in the UINavigationController case.
In the viewDidAppear of your nextviewcontroller you could add :
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
NSArray *controllers = self.navigationController.viewControllers;
NSMutableArray *newViewControllers = [NSMutableArray arrayWithArray:controllers];
[newViewControllers removeObjectAtIndex:[controllers count]-2];
self.navigationController.viewControllers = newViewControllers;
}
There is nothing available like this in iOS but you can achieve it doing something like below
NSArray *viewControllers=[self.navigationController viewControllers];
NSMutableArray *newControllers=[[NSMutableArray alloc] init];
for(int i=[viewControllers indexOfObject:self];i<viewControllers.count;i++){
[newControllers addObject:[viewControllers objectAtIndex:i]];
}
[self.navigationController setViewControllers:[[NSArray alloc] initWithArray:newControllers]];
I have tried the method of storing all the view controllers in an array but it didn't work for me . When you try popViewController it will move to the View Controller which is last in the stack.
You can make 2 navigation controllers and switch between them and also switch between the view controllers of a particular Navigation Controller.
For eg.
You can switch between 2 Navigation Controller using the following code:
FirstNavController *fisrtView=[storyboard instantiateViewControllerWithIdentifier:#"firstnavcontroller"];
self.window.rootViewController = firstView;
}else{
SecondNavController *secondView=[storyboard instantiateViewControllerWithIdentifier:#"loginnavcontroller"];
self.window.rootViewController = secondView;
}
If your FirstNavController has 2 ViewControllers then you can switch between them using pushViewController
SecondViewController *sc = [self.storyboard instantiateViewControllerWithIdentifier:#"secondviewcontroller"];
[[self navigationController] pushViewController:sc animated:YES];
and popViewController
[self.navigationController popViewControllerAnimated:YES];
I've been looking this up for a while now, it might have a simple answer:
According to the Apple docs, past ios6, we can subclass UINavigationController. How do we perform a segue from identifier when it prevents anything that isn't a UINavigationController. Mainly:
uncaught exception 'NSGenericException', reason: 'Could not find a
navigation controllerfor segue 'profileSegue'. Push segues can only
be used when the source controller is managed by an instance of
UINavigationController.
I'm using JaSidePanels and my center panel (navigation) needed to be subclasses for a delegate as there is a menu on the left panel that I want to switch the views when clicked.
#interface CenterViewController : UINavigationController <MenuDelegate>
Basically, since this object is a CenterViewController at runtime, is there a way to cast it to its superclass? I've tried [self superclass] but that didn't work (same error).
I need to call this code in the CenterViewController. Is it possible to move it to UINavigationController?
- (void)viewDidLoad
{
RootViewController *rootViewController = (RootViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController];
MenuViewController *leftViewController = (MenuViewController *)rootViewController.leftPanel;
// Store a reference to the center view controller in the left view controller's delegate property
leftViewController.menuDelegate = self;
[super viewDidLoad];
}
- (void) MenuItemSelected: (NSString*) item
{
if ([item isEqualToString:#"Home"]) {
//These would throw the error since we're not technically a "UINavigationController"
//[self performSegueWithIdentifier: #"mapViewController" sender: nil];
} else if ([item isEqualToString:#"Profile"]) {
//[self performSegueWithIdentifier: #"profileSegue" sender: self];
}
}
EDIT:
More information with pictures.
I'm curious as to how navigation controllers should work with side panels. I've looked at many other plugins for sidepanels and here is an example. Even though it works, why does it have 2 nav controllers?
Here is our current setup:
So basically, am I thinking about this wrong in the sense that I want to push a new VC to the existing NavVC? Would it be better to push a new NavVC when a menu button is pressed? What would happen when we go into a subview from the Maps view. Would the menu be accessible via sliding?
If you look carefully at the message you will see that your problem isn't caused by subclassing UINavigationController it is because you are executing the segue against your (subclassed) UINavigationController. Push segues are executed against a UIViewController that is embedded in or managed by a UINavigationController, with the system then finding the managing UINavigationController via the view controller's navigationController property in order to execute the push.
The message you have received says
...Push segues can only
be used when the source controller is managed by an instance of
UINavigationController
In your case the source controller is an instance of UINavigationController, it isn't managed by a UINavigationController.
You haven't said exactly how your app navigation works, but I have a suspicion that a UINavigationController isn't the right tool to use anyway. If you are using the side menus to allow the user to select the central content in a random way (i.e. the user could select the first option then the fifth and then go back to the first) then you should probably just have a central view into which you present the selected view. Pushing views onto a UINavigationController will end up with a large 'stack' of views unless you pop the current view controller before pushing the new one, which is even more complicated and not the visual effect you are looking for.
You can still achieve the 'push' style transition but you will need to use a custom segue. If it were me I would probably push from the left if the user selected a menu item that was closer to the top than the current option and from the right if the new item was closer to the bottom than the current, but again I am making assumptions on how your app navigation works.
UPDATE
Yes, I think you are on the right track with the section of your updated question. Navigation controllers are for navigating a series of related views in a hierarchical manner - think of the Settings app - you select "general" or "wall paper" or whatever - each of these then has a series of views that you can navigate through; up and down a stack.
In your app it looks like home, profile and settings should each be navigation controllers. Your root view would then just be a view. Your menu would select which view controller to present in the root view - this is like a tab bar controller, except your menu takes the place of a tab bar.
You can allocate your home, profile & settings view controllers in your appDelegate and store them to properties of the appDelegate. Then you can use something like this:
- (void) MenuItemSelected: (NSString*) item
{
myappDelegate *app=(myappDelegate *)[UIApplication sharedApplication].delegate;
[delegate.currentViewController removeFromParentViewController];
UIViewController *newController;
if ([item isEqualToString:#"Home"]) {
newController=app.homeViewController;
} else if ([item isEqualToString:#"Profile"]) {
newController=app.profileViewController;
}
if (app.currentViewController != newController)
{
[app.currentViewController removeFromParentViewController];
[app.rootViewController addChildViewController:newController];
app.currentViewController = newController;
}
I am building an application in which, i have 3 ViewControllers.
Also i have created custom navigation controller.
A-ViewController -> This contains 2 buttons, 1st button is for opening B-ViewController & 2nd button is for C-ViewController.
From 1st button, I am using pushviewcontroller for B-ViewController, means B-ViewController is pushed from A-ViewController.
From 2nd button C-ViewController is presented using presentviewcontroller.
Now on pressing back button in both B & C ViewControllers, I have to use pop view controller in B-ViewController & dismiss in C-ViewController.
As currently i know the pages, but there should be a generic solution.
Is there any way to identify whether current navigation controller is pushed or popped.
As there are some pages which can be pushed or presented, but i dont want to set any bool variable. I need use the support from apple.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
NSLog(#"[arrViewControllers count] = %d",[arrViewControllers count]);
I am using the above code for fetching the list of view controllers in the stack.
But i am not able to identify whether it is pushed or presented.
Can anybody help me in this ?
You can check if your view controller exists in the navigation view controller's stack. Simply check it as
if([self.navigationController topViewController] == self){
//VC is the top most view controller
[self.navigationController popViewControllerAnimated:YES];
}else{
//You can put some checks here to be dead sure its a modally presented view controller
[self dismissViewControllerAnimated:YES completion:nil];
}
This is the code for Finding the navigation controller is presented or pushed from previous controller.
NSArray *arrViewControllers = [[AppDelegate sharedInstance].navigationController viewControllers];
UIViewController *viewController = [arrViewControllers lastObject];
if (viewController.presentedViewController)
{
[self dismissViewControllerAnimated:YES completion:nil];
}
else{
[self.navigationController popViewControllerAnimated:YES];
}
Hello I am new to iOS and have a question about navigating through my views.
I am using IB wiring up the PREVIOUS and NEXT buttons in the nav bar that pushes my views. This all works fine. However I am having trouble finding out where exactly what I need to do or where I need to place the code so I can skip over a view. To simplify my situation...
-I have 1 Nav Controller and 4 View Controllers named VC1, VC2, VC3, VC4. Each VC has a .H/.M
-Starting from VC1, they follow one after the other.
Say I want to skip VC3 and jump right to VC4 based on a setting in VC2. Where would I put the code to do this? Would I need to unhook the IBAction method from the NAV buttons at VC3?
I do apologize if this has been covered before. If there is a tut or if you know of a post that answers this, please let me know. I did do a search but the search was returning generic posts probably due to me using the wrong terminology.
Thanks in advance.
A couple of thoughts:
If you want to push from VC2 to either VC3 or to VC4, rather than having VC3 immediately push to VC4 in special cases, I think it's better to just have VC2 push directly to the appropriate view controller. Thus you might have an IBAction in VC2 that would do this for you:
- (IBAction)pushToNext:(id)sender
{
BOOL skipToVC4 = ... // put in whatever logic you'd use to bypass VC3 and go directly to VC4
UIViewController *nextController;
if (skipToVC4)
{
nextController = [self.storyboard instantiateViewControllerWithIdentifier:#"VC4"];
// obviously, if using NIBs, you'd do something like:
// nextController = [[ViewController4 alloc] initWithNibName:#"VC4" bundle:nil];
}
else
{
nextController = [self.storyboard instantiateViewControllerWithIdentifier:#"VC3"];
}
[self.navigationController pushViewController:nextController animated:YES];
}
This way, when you pop back from VC4, you'll pop back directly to the appropriate view controller (e.g. if you pushed from VC2 to VC4, when you pop, you'll pop right back to VC2 automatically.
And, obviously, if you're using storyboards, rather than manually invoking pushViewController, you could have two segues from VC2 (one to VC3 and one to VC4), give them appropriate identifiers, and then just invoke performSegueWithIdentifier to segue to the appropriate view controller. But the idea is the same: You can define an IBAction that performs the appropriate segue depending upon whatever logic you so choose.
You say that you have "PREVIOUS and NEXT buttons in the nav bar that pushes my views", I wonder about your "PREVIOUS" button. Is that doing a popViewControllerAnimated? Generally, a "NEXT" button will push to a new view controller, but the "PREVIOUS" button should not push to the previous view, but pop back to it. If you don't pop back, you can end up with multiple instances of some of your prior view controllers. Thus, the "PREVIOUS" button should be linked to an IBOutlet that does something like:
- (IBAction)popToPrevious:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
}
When popping back, you'll obviously pop back to the view controller that you pushed from. If you want to skip a few of the view controllers as you're popping back in iOS versions prior to 6.0, you would use popToViewController or popToRootViewControllerAnimated. For example, let's say that you pushed from VC1 to VC2, to VC3, to VC4. If you want to pop back from VC4 all the way to VC1, you would hook up and IBAction in VC4 like:
- (IBAction)popToRoot:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES];
}
Or, if you wanted to pop back from VC4 to VC2, you would
- (IBAction)popToVC2:(id)sender
{
for (UIViewController *controller in self.navigationController.viewControllers)
{
if ([controller isKindOfClass:[ViewController2 class]])
{
[self.navigationController popToViewController:controller animated:YES];
return;
}
}
}
You can avoid this iteration through the navigationController.viewControllers if you passed a reference of VC2 to VC3 and then again to VC4, but sometimes the above technique is easier.
By the way, if you're supporting iOS 6 and above, only, and are using storyboards, you can also use unwind segues, which are a more elegant way of popping back to a particular view controller. But it's not clear whether (a) you're using storyboards; and (b) you're supporting iOS 6 and above only, so I'll refrain from a discussion of unwind segues at this point.
First:
Show us some code, where you are pushing new view controllers, maybe your whole navigation controller code
Second (Solution):
I assume:
Your prev/next buttons are linked to your navigationController class
you have the appropriate methods (prevPressed:/nextPressed:), which are called, when you click one of the buttons
I can help you with the following:
you know which controller is visible at the moment with the visibleViewController #property
each time you click on a button in the navBar you can ask the visibleViewController which next/previous view controller should be pushed/popped
Best solution would be, if all of your controllers VC1/2/3/4 are a subclass of a viewController class, which defines a method in it's interface:
- (Class)nextViewControllerClass;
- (Class)previousViewControllerClass;
and in the implementation:
- (Class)nextViewControllerClass {
return [VC4 class];
}
- (Class)previousViewControllerClass {
return [VC1 class];
}
And in your navigationController code the do this:
- (IBAction)next:(id)sender {
UIViewController *nextViewController = [[[self.visibleViewController nextViewControllerClass] alloc] init];
[self pushViewController:nextViewController animated:YES];
}