-1

I've written a container controller which I'm using inside a tab bar controller. The container manages a stack of controllers like a UINavigationController, and I'd like it to behave the same way as a navigation controller does, so that when the tab is tapped while already selected the container pops to the root controller.

I'm not looking for a solution which involves being the tab controller's delegate; I want the container to behave the same way as UINavigationController and do it automatically when put inside a tab controller.

Update: To respond to some of the scepticism, I was hoping that there might be some undocumented method that UITabBarController calls on UINavigationController which I could implement; however, having put logging in -respondsToSelector: the only method I've seen is -_appearanceContainer. However, putting logging in -isKindOfClass: shows that it's being tested against UINavigationController in several places.

Simon
  • 25,468
  • 44
  • 152
  • 266
  • "I want to respond when a tab is tapped but I don't want to have to *detect* when the tab is tapped." Uh-huh. – matt May 10 '13 at 17:33
  • Somehow, this behavior must be built in to the navigation controller, which would "know" when it's embedded in a tab bar controller. You will have to do this by having your container controller be the delegate of the tab bar. I don't think there's any way around that. – rdelmar May 10 '13 at 17:51
  • @matt I definitely want to detect when it's tapped - I just don't want my *users* to have to detect when it's tapped. – Simon May 10 '13 at 18:02
  • Using an undocumented method would get you rejected from the app store, if that's where you're headed. I don't understand what you mean by the "users". Do you mean users of your app, or users in the sense of someone (a programmer) adopting your custom container? I don't see how users would know what you're doing to implement this behavior. BTW, I looked for any notifications sent out by the tab bar controller or the tab bar to the navigation controller or any of its content controllers, and I don't see any. It's a mystery to me how Apple is doing this. – rdelmar May 10 '13 at 18:17
  • I don't see why you won't just declare you container vc to be the tab controllers delegate? That is exactly what't done in the `UINavigationController`. It probably checks in it's `didMoveToParent` method, if the parent vc is a `UITabBarController` and if it is, it sets itself as delegate. – Tobi May 10 '13 at 18:23
  • I don't want my container to be the UITC's delegate, because I want the user of my class to be able to set their own delegate. UINC doesn't set itself as the UITC's delegate. – Simon May 10 '13 at 18:29
  • @rdelma I mean users in the second sense. You're probably right about getting rejected for using private APIs, though. – Simon May 10 '13 at 18:29
  • @Tobi, I've logged self.tabBarController.delegate in a typical tabbed app setup with a navigation controller, and it's null (unless you set it yourself). – rdelmar May 10 '13 at 18:30

2 Answers2

1

I think I found a way. I tested this on a navigation controller, but I think it would work on your custom controller as well. Use KVO in your custom container controller.

 - (void)viewDidLoad {
    [super viewDidLoad];
    [self addObserver:self forKeyPath:@"self.tabBarController.selectedViewController" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    if ([change[@"old"] isEqual:change[@"new"]] && [change[@"new"] isEqual:self]) {
        NSLog(@"nav controller's tab was selected again");
    }else{
        NSLog(@"different tab was selected");
    }
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • I had to add the observer in `-didMoveToParentViewController:` rather than `-viewDidLoad`, and it's firing four times per tap, but it's a good start. Thanks! – Simon May 10 '13 at 18:57
  • @Simon, In my tests it worked better with @"self.tabBarController.selectedViewController" as the keyPath -- I only got it firing once with this one (vs twice with my original post). I've updated my answer with this, and some additional logic. – rdelmar May 10 '13 at 19:43
  • Yep, that's working. Thanks! I hope you still get the reputation boost despite the question being closed. – Simon May 11 '13 at 19:22
0

It looks like rdelmar is right - although it's possible, I'm fairly sure this would get my users rejected for using private APIs.

- (BOOL)isKindOfClass:(Class)aClass
{
    if ([aClass isEqual:[UINavigationController class]])
    {
        if ([[NSThread callStackSymbols] objectAtIndex:1] rangeOfString:@"-[UITabBarController _tabBarItemClicked:]"].location != NSNotFound)
        {
            return YES;
        }
    }

    return [super isKindOfClass:aClass];
}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
    // Do the popping...
}

I'd love to see an answer that stays within the guidelines, if there is one.

Community
  • 1
  • 1
Simon
  • 25,468
  • 44
  • 152
  • 266