Problems with UINavigationController inside of UITabBarController, viewWillAppear not called
Asked Answered
R

5

7

As an overview, I'm having issues with a UINavigationController inside of a UITabBarController calling viewWillAppear whenever a view is popped from the stack.

From the delegate, a UITabBarController is made programmatically:

// Create views for Tab Bar
    UINavigationController *view1   = [[UINavigationController alloc] initWithRootViewController:[[newsFeedNavigationController alloc] initWithStyle:UITableViewStylePlain]];
    resizedTabBatItem *tabBarItem1 = [[resizedTabBatItem alloc] initWithTitle:nil image:[UIImage imageNamed:@"newspaper.png"] tag:0];
    [view1 setTabBarItem:tabBarItem1];
    [tabBarItem1 release];

    UIViewController *view2   = [UIViewController new];
    resizedTabBatItem *tabBarItem2 = [[resizedTabBatItem alloc] initWithTitle:nil image:[UIImage imageNamed:@"speechbubble.png"] tag:1];
    [view2 setTabBarItem:tabBarItem2];
    [tabBarItem2 release];

....

// Create the tab bar controller
    bookTabBarController = [BookTabBarController new];
    [[bookTabBarController view] setFrame:CGRectMake(0, 0, 320, 460)];

    // Add the views to it
    NSArray *viewControllers = [NSArray arrayWithObjects:view1, view2, view3, view4, view5, nil];

    [[bookTabBarController tabBarController] setViewControllers:viewControllers];

My newsFeedNavigationController is just a subclassed UITableViewController (and the subclass is not interfering with viewWillAppear, as it's never called in newsFeedNavigationController). In it, items that when clicked will push a new UIViewController into the stack.

The problem is that whenever views are popped off the stack, viewWillAppear is never called in newsFeedNavigationController, and the items in the list remain highlighted. I've been messing with this for a few hours am at the point where I need some help to find out what I am doing wrong.

In my newsFeedNavigationController, I tried to add an NSLog to see if it is called or I did something, but it is never even called.

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"is viewWillAppear called?");
    [super viewWillAppear:animated];
}

Edit:

Okay, now here is something weird I noticed:

If I run:

[self presentModalViewController:(any UIview) animated:YES];

and then dismiss it, viewWillAppear begins to work properly when popping and pushing views... So now I am stumped. It's not really a solution but maybe an inside of something that is going on.

Rese answered 17/6, 2010 at 16:33 Comment(0)
R
1

To answer my own question, I found out what the problem was.

In order to abide by Apple's "No UITabBarController inside of a UINavigationController" I wrote my own tab bar controller (bookTabBarController) which is based off of a standard View Controller. My problem was that the class wasn't passing viewDidAppear down to the class that managed the view controllers, so it never knew it was being shown or not.

Rese answered 19/6, 2010 at 21:21 Comment(5)
I'm confused. In your post you said "UINavigationController inside of a UITabBarController" and in your follow-up you said "UITabBarController inside of a UINavigationController"... which? Because I'm having an identical problem with a Nav Controller as a tab within a tab bar controller... which, as I understand, should be supported. But ViewDidAppear only starts working after I present a modal view from one of the views pushed to the nav controller. Very strange.Delldella
See my answer for a general solution to this problem.Vertebrate
This was the same as my problem. I'm sorry for confusing. What I initially had was a UINavigationController w/ a view controller, and then the "home" screen would contain what was essentially a UITabBarController next in the stack. Then, you could push a new view into the stack. Ex: UINavigationController w/ login view as root, when in, a uitabbar was pushed in, and views from a table could be pushed into the "main" nav. My problem was that viewWillAppear wasn't being passed down the stack (It would go from the UINavigationController to the UITabBarController, but not to the Tab Bar's view).Rese
Could you add the code that shows how to solve this?Trauma
@Trauma - Apple has introduced View Containment API to UIViewController since I asked this question, which mitigates most of the work in managing child view responses. If you are wanting to roll your own Tab Bar Controller (or any view containing other views), then look in that direction for the most effective solutionRese
T
1

Another solution is to set the navigation controller's delegate. Within the delegate, implement the following method:

- (void)navigationController:(UINavigationController *)navigationController  willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    [viewController viewWillAppear:animated];
}

This will ensure that viewWillAppear will get called on any view controller whose view is about to appear in the navigation controller. If you do it this way, viewWillAppear gets called regardless of whether the view is appearing because it is being pushed, or it is appearing because a subview is being popped.

Toots answered 28/6, 2010 at 0:39 Comment(1)
Unfortunately, I tried this method and it didn't work. Well, it did work, but it didn't. The problem was that even though the viewWillAppear:animated was called, it wasn't being passed onto the subview contained in the tab bar because it wasn't being explicitly called in the tab bar's viewWillAppear. But for others run into problems, this is a potential solution. Though, if viewWillAppear:animated isn't working, it probably means that something else is wrong (as in my case).Rese
V
1

A solution to this problem is to have the UIViewController containing the UINavigationController pass the desired messages to it. The UINavigationController will forward the messages to the appropriate view controller. It seems counterintuitive but it works.

@interface NavigationWrapperViewController : UIViewController {
    // navigationController must be a subview of this view controller's view
    UINavigationController *navigationController;
}
@property (nonatomic, assign) UINavigationController *navigationController;
@end

@implementation NavigationWrapperViewController
@synthesize navigationController;

-(void)viewWillAppear:(BOOL)animated {
    [navigationController viewWillAppear:animated];
}
-(void)viewDidAppear:(BOOL)animated {
    [navigationController viewDidAppear:animated];
}
-(void)viewWillDisappear:(BOOL)animated {
    [navigationController viewWillDisappear:animated];
}
-(void)viewDidDisappear:(BOOL)animated {
    [navigationController viewDidDisappear:animated];
}

@end

You can find a more complete solution on Pastebin (which I did not post).

Thanks to davidbenini.it and jaekwon for this solution.

Vertebrate answered 20/9, 2010 at 19:2 Comment(1)
This solution is a bit incomplete and the paste-bin link is a bit of a syntactical mess, but I'll be damned if this doesn't fix my problem! You Rock!Chatham
A
1

An even simpler trick:

In your subclass of UITabBarController, override this:

-(void)loadView{

    [super loadView];

    //here goes the trick:
    [self setSelectedIndex:1];
    [self setSelectedIndex:0];
}
Anschluss answered 19/9, 2012 at 3:1 Comment(1)
This work-around helped me out but I found that setting the index back to 0 immediately didn't work. I had to wait until viewDidAppear. The other page doesn't show or flicker and works a treat because manually selecting another tab first meant the problematic view would work. One caveat is that the index you set it to must contain a very simple page. I originally tried a view with a sub-view and the sub-view disappeared but could still take user interaction, i.e. problem shifted to that tab instead.Kenwood
O
0

OK, this is old, very old, but I ended up here with a similar problem.

  UITabViewController
     UINavigationController
       UITableViewController1
         UITableViewController2

When popping out of the UITableViewController2, the viewWillAppear function in UITableViewController1 was never called.

The problem: My UITabViewController custom class was overriding viewWillAppear without calling the super implementation.

Organist answered 19/7, 2016 at 1:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.