call popToRootViewController from another tab - ios

I have tab bar with navigation controller app using storyboard ,
my purpose is to press a button in tab3 and in the background I want tab1 to "popToRootViewController"
the button in tab3 viewcontroller:
- (IBAction)Action:(id)sender {
vc1 * first = [[vc1 alloc]init];
[first performSelector:#selector(popToRootViewController) withObject:Nil];
}
the code in the tab1 viewcontroller
-(void)popToRootViewController{
[self.navigationController popToRootViewControllerAnimated:NO];
NSLog(#"popToRootViewController");
}
I get the popToRootViewController in logs, but the action didn't perform.
that solve the problem:
- (IBAction)Action:(id)sender {
[[self.tabBarController.viewControllers objectAtIndex:0]popToRootViewControllerAnimated:NO];
}

The way you are doing it:
vc1 * first = [[vc1 alloc]init];
[first performSelector:#selector(popToRootViewController) withObject:Nil];
is not correct. Indeed, you are creating a whole new controller here, completely independent from your existing controllers and not belonging to any navigation controller. For this reason, self.navigationController is nil in popToRootViewController.
You might try doing something like:
//-- this will give you the left-most controller in your tab bar controller
vc1 * first = [self.tabBarController.viewControllers objectAtIndex:0];
[first performSelector:#selector(popToRootViewController) withObject:Nil];

Bind TabBar with tabBarViewController-
In tabBarViewController.m
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
NSArray *array = [tabBarController viewControllers];
if([[array objectAtIndex:tabBarController.selectedIndex] isKindOfClass:[UINavigationController class]])
[(UINavigationController *)[array objectAtIndex:tabBarController.selectedIndex] popToRootViewControllerAnimated: NO];
}
It worked perfectly for me.

To press a button in tab3 and in the background I want tab1 to "popToRootViewController"
If you want to perform popToRootViewController in tab1 by pressing button in tab3 then i would like to suggest use NSNotificationCenter. For example follow below code:-
In your firstViewController class add the observer of NSNotification
- (void)viewDidLoad
{
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(yourMethod:)
name:#"popToRootViewControllerNotification" object:nil];
}
-(void)yourMethod:(NSNotification*)not
{
[self.navigationController popToRootViewControllerAnimated:NO];
}
In your ThirdViewController class post the notification in below code:-
- (IBAction)Action:(id)sender {
// vc1 * first = [[vc1 alloc]init];
// [first performSelector:#selector(popToRootViewController) withObject:Nil];
//Post your notification here
[[NSNotificationCenter defaultCenter] postNotificationName:#"popToRootViewControllerNotification" object:nil];
}

If your tab1 and tab2 arein different navigationController, then try this in - (IBAction)action:(id)sender
NSArray *viewControllers = [self.tabbarController viewControllers];
for (UIViewController *viewController in viewControllers) {
if ([viewController isKindOfClass:[vc1 class]]) {
vc1 * first = (vc1 *) viewController;
[first.navigationController popToRootViewControllerAnimated:NO];
}
}

Related

Navigation Back shows other Viewcontroller during the transition

I have three Viewcontrollers : ViewControllerA, ViewControllerB and ViewControllerC.
When I am in ViewControllerC, I click on Navigation Back button to go back to ViewControllerA directly, skipping ViewControllerB.
I have tried following approaches, both of them work. But, I wonder while transiting from ViewController C to ViewController A, it shows ViewController B in one second for during the transition.
Is there a way just directly navigate from ViewController C to ViewController A skipping ViewControllerB.
Approach 1:
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
NSLog(#"back button pressed");
[self.navigationController popViewControllerAnimated:YES];
}
[super viewWillDisappear:animated];
}
Approach 2:
-(void) viewWillDisappear:(BOOL)animated {
if ([self.navigationController.viewControllers indexOfObject:self]==NSNotFound) {
NSLog(#"back button pressed");
//[self.navigationController popViewControllerAnimated:YES];
NSMutableArray *allViewControllers = [NSMutableArray arrayWithArray:[self.navigationController viewControllers]];
for (UIViewController *aViewController in allViewControllers) {
if ([aViewController isKindOfClass:[ViewControllerA class]]) {
[self.navigationController popToViewController:aViewController animated:NO];
}
}
}
[super viewWillDisappear:animated];
}
You need to use setViewControllers method, and pass only the viewControllerA that is the first element in your navigationController.viewControllers array
Code
- (IBAction)backAction:(id)sender {
UIViewController * viewControllerA = [self.navigationController.viewControllers firstObject]; //we get the first viewController here
[self.navigationController setViewControllers:#[viewControllerA] animated:YES];
}
similar answer here How to start from a non-initial NavigationController scene but in swift
Try popToRootViewControllerAnimated . It will move to First ViewController which NavigationController embed.
[self.navigationController popToRootViewControllerAnimated:YES];
NSArray *arrayViewControllers = [self.navigationController viewControllers];
for (UIViewController *viewcontroller in arrayViewControllers) {
if ([viewcontroller isKindOfClass:[ViewControllerA class]]) {
[self.navigationController popToViewController:viewcontroller animated:true];
}
}

How to change destination of back button to another storyboard iOS?

I have an application with the flow bellow:
Main storyboard: Navigation A -> ViewController A1 (first screen of my app) ... go to some screens and go to other storyboard.
Second storyboard: Navigation B -> ViewController B1 -> ViewController B2 -> ViewController B3.
The normal logic when I'm in B3, tap back button back to B2 and tap back again back to B1 ...
Now I want from B3 go to A1 by click back button. I use this code
UIStoryboard *Main = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
UIViewController *theTabBar = (UIViewController *)[Main instantiateViewControllerWithIdentifier:#"PageController"];
[self.navigationController pushViewController:theTabBar animated:YES];
But it not work as I want (on Main appeared the back button, that was wrong), I changed pushViewController to popViewController but it not work too.
Just right this code on back button of b3:-
[[NSNotificationCenter defaultCenter] postNotificationName:#"takeBack" object:nil];
and write this code on view viewwillappear of A1:-
-(void)viewWillAppear:(BOOL)animated{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(popping) name:#"takeBack" object:nil];
}
-(void)popping{
NSUInteger index=-1;
for (UIViewController *view in [self.navigationController viewControllers]) {
if ([view isKindOfClass:[self class]]) {
index=[[self.navigationController viewControllers] indexOfObject:view];
}
}
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:index] animated:YES];
}

How can I pop to any view controller as I wish?

view controller A B C D
A -> B -> C-> D
popViewController only form D to C
popViewTopController only form D to A;
Any way can I pop to any view as I wish if I have 10 view controllers?
Thanks for everyone. will the popViewController pop to a new view Controller ?
Option 1: Select by class
To tell the navigationController to pop to a specific class, you can do as follows:
NSArray *allViewControllers = [self.navigationController viewControllers];
for (UIViewController *aViewController in allViewControllers)
{
if ([aViewController isKindOfClass:[B class]])
{
[self.navigationController popToViewController:aViewController animated:YES];
}
}
Take into account that you should only use this, if you are not pushing instances of the same class several times.
Option 2: Select by level
If you want to pop to a specific level, you can just select it by index at self.navigationController.viewControllers since it correspond to the levels. The first pushed UIViewController will be at index 0, the second at index 1 and so on:
NSArray *allViewControllers = [self.navigationController viewControllers];
UIViewController *aViewController = [allViewControllers objectAtIndex:level];
[self.navigationController popToViewController:aViewController animated:YES];
if you want to pop any view you want to change objectAtIndex:1,2,3..etc
it will pop to first,second etc... from the any views.
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:1] animated:YES];
This is the method you are looking for (reference)
In Obj-c
- (NSArray *)popToViewController:(UIViewController *)viewController
animated:(BOOL)animated
You should pass in the view controller that you want pop to
Use the following UINavigationController method to go to any view controller on the current stack.
- (NSArray *)popToViewController:(UIViewController *)viewController
animated:(BOOL)animated
For example, if you are in a UIViewController and you want to pop back to the third one in the stack:
UINavigationController * nc = self.navigationController;
UIViewController * popToVC = [nc.viewControllers objectAtIndex:2];
[nc popToViewController:popToVC animated:YES];
SecondViewController *sec = [SecondViewController alloc] init];
[self.navigationController popViewController:Sec animated:YES];

Dismiss all Detail Views when switching Tabs on TabBarController

My iOS app has:
TabBarController
NavigationController1
TableView1
ViewController1 (Details View)
NavigationController2
TableView2
ViewController2 (Details View)
Behavior:
When the app loads, I see the TableView1.
I select an Item in the table, and it takes me via Show (Push) segue the details view 1.
I switch to the second tab on the bottom, and see TableView2.
I select an item and it takes me to details view 2
I navigate back to first tab, and see details view 1
Desired:
When performing last step, I'd like to dismiss the details view and see the first TableView1, and when switching back to second tab, I want that one to be dismissed and to see the table view.
I've tried different combinations of dismissViewControllerAnimated and popToRootViewControllerAnimated but I just don't seem to figure it out.
MainTabBarController.h
#interface MainTabBarController : UITabBarController <UITabBarControllerDelegate>
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
self.delegate = self;
}
...
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// None of the lines below achieve the desired result
[viewController.navigationController popToRootViewControllerAnimated:YES];
[viewController dismissViewControllerAnimated:YES completion:nil];
[tabBarController.navigationController popToRootViewControllerAnimated:YES];
[tabBarController dismissViewControllerAnimated:YES completion:nil];
}
One option is to make use of the UITabBarControllerDelegate. Listen for changes to the tab selection. Based on the new tab, get the tab's navigation controller and call its popToRootViewControllerAnimated: method.
Update based on the code added to the question:
The problem is with how you try to pop the view controllers. You want this:
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController{
// NSLog Works fine, and displays information in the output
NSLog (#"%# %lu", tabBarController.selectedViewController.title, tabBarController.selectedIndex);
// If the selected tab's root controller is a navigation controller
// pop to the top view controller in the tab's navigation stack
if ([viewController isKindOfClass:[UINavigationController class]]) {
UINavigationController *nav = (UINavigationController *)viewController;
[nav popToRootViewControllerAnimated:NO];
}
}
Here is a simple solution for this.
Try to implement the following methods of UIViewContorller
- (void)viewWillDisappear:(BOOL)animated; // Called when the view is dismissed, covered or otherwise hidden. Default does nothing
- (void)viewDidDisappear:(BOOL)animated; // Called after the view was dismissed, covered or otherwise hidden. Default does nothing
Go to your detail-1 view controller and implement the method - (void)viewWillDisappear:(BOOL)animated.
Do a pop for that controller.
Same you should do for the detail-2
Here is the code snippet that will help you.
In Appdelegate.m
#interface AppDelegate ()<UITabBarControllerDelegate>
#property(nonatomic, strong) MainTabBarController *rootTabBarController;
#end
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootTabBarController = [[MainTabBarController alloc]init];
self.rootTabBarController.delegate = self;
self.window.rootViewController = self.rootTabBarController;
[self.window makeKeyAndVisible];
}
TabBarController delegate implementation
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger index = [self.rootTabBarController.viewControllers indexOfObject:viewController];
NSLog(#"Index : %lu", (unsigned long)index);
switch (index) {
case 0:
// pop other tab barcontrollers pushed or modal windows
[self.rootTabBarController flushViewControllerStackForIndex:1];
break;
case 1:
[self.rootTabBarController flushViewControllerStackForIndex:0];
break;
default:
break;
}
}
MainTabBarController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[self setViewControllers:#[
[[UINavigationController alloc] initWithRootViewController:[[FirstViewController alloc]init]],
[[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc]init]]
] animated:YES];
}
-(void)flushViewControllerStackForIndex:(NSUInteger )index {
[[self.viewControllers objectAtIndex:index] popToRootViewControllerAnimated:NO];
}
Here is screenshot in sequence for the sample I ran.
Here is the Sample code.
That should solve your purpose & is the right approach.
Now you may need to fine tune your own logic in flushViewControllerStackForIndex to check if there is just only controller being pushed on stack or a combination of push & modal. So better try to navigate on the Stack & do-a-dismiss-if-a-modal or do-a-pop-if-a-push.
Hope that helps.
You can directly set the view controllers currently on the navigation stack. All you have to is directly set the viewControllers property of the navigation controllers when switch tabs in the tabbar controller.
Set NavigationController1.viewcontrollers = #[tableView1] when you switch to tab1

How to know which viewcontroller is presented on app start

The situation is :
App is on background
The user click on icon app
App open and show the view controller where we were before apps entered background last time.
I'd like to know which view controller is about to be presented. I'm looking for something like :
- (void)applicationDidBecomeActive:(UIApplication *)application {
if ([self.window.viewControllerOnScreen isKindOfClass:[HomeViewController class]]) {
//do sthg
}
}
Because in case, it's the home view controller (embed in a navigation controller and i use storyboards) i would perform some reload method.
[[self.navigationController viewControllers] lastObject];
The first part will give you an array of all of the viewControllers on the stack, with the last object being the one that is currently display. Check its class type to see is it the homeViewController
As per this link Each object receive a UIApplicationDidEnterBackgroundNotification notification when the app goes in background. Similarly UIApplicationWillEnterForegroundNotification gets fired when app comes in foreground.
so you can use it to keep track of which view controller is opened when app enters foreground
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(appEnteredForeground:)
name:UIApplicationDidEnterForegroundNotification
object:nil];
Try this
- (UIViewController *)topViewController{
return [self topViewController:[UIApplication sharedApplication].keyWindow.rootViewController];
}
- (UIViewController *)topViewController:(UIViewController *)rootViewController
{
if (rootViewController.presentedViewController == nil) {
return rootViewController;
}
if ([rootViewController.presentedViewController isMemberOfClass:[UINavigationController class]]) {
UINavigationController *navigationController = (UINavigationController *)rootViewController.presentedViewController;
UIViewController *lastViewController = [[navigationController viewControllers] lastObject];
return [self topViewController:lastViewController];
}
UIViewController *presentedViewController = (UIViewController *)rootViewController.presentedViewController;
return [self topViewController:presentedViewController];
}
I have done this for getting the current viewController
if (![[appDelegate.rootNavController topViewController] isMemberOfClass:NSClassFromString(#"LGChatViewController")]) {}
self.tabBarController.viewControllers = [NSArray arrayWithObjects: [self LoadAccount], [self LoadContacts], [self LoadPhoneLine], [self LoadSettings], nil];
if you use tab bar then you set like this and show first account and go on......

Resources