2

I have a UITabBarController, which has 4 tabs. Each one of those tabs is a separate UIViewController. I have objects on each one of those 4 VC's that use NSNotification's to perform actions upon the press of a certain object. The 4 VC's all respond to the notification in the same way because it is a similar object on each page. When this object is pressed it presents a view onto the current view controller. The problem is that if I move to any of the other 3 tabs now that view is also on their VC. That is because the notification is being responded to on all 4 tabs when it is pressed on any of the VC's. I am needing it to only respond to the VC that the user is currently on and not any of the others that are in the tab bar.

Is there a way to get this to work properly? Maybe a threshold where you can set how many times the notification can perform its selector after being called? That way I could set it to 1 and at any given time if that notification is called the selector can only be called 1 time.

The type of object implementation that I'm using requires me to use NSNotification's so there is no way to change how I interact.

edit:

This viewDidLoad method is on the top level VC for the 4 VC's in my tab bar. All 4 of them either use this directly or inherit from it.

- (void) viewDidLoad
{
    ...
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didSelectItemFromCollectionView:) name:@"didSelectItemFromCollectionView" object:nil];
}

Action Handler:

- (void) didSelectItemFromCollectionView:(NSNotification *)notification
{
    NSDictionary *cellData = [notification object];

    if (cellData)
    {
        NewVC *pushToVC = [self.storyboard instantiateViewControllerWithIdentifier:@"PushToVC"];

        [self.navigationController pushViewController:pushToVC animated:YES];
    }
}

Each of the 4 VC's is a UITableViewController and have cells with an object that can be pressed. This NSNotificationCenter action is what allows the operation to work.

n00bProgrammer
  • 4,261
  • 3
  • 32
  • 60
BlueBear
  • 7,469
  • 6
  • 32
  • 47

2 Answers2

5

You must have implemented the NSNotificationCenter's -addObserver:selector:name:object: method in the -viewDidLoad of every viewController

Example:

- (void)viewDidLoad
{
    //...
    [NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(doSomething:) 
                                                name:@"TestNotification"
                                              object:nil];
}

Instead of having this in -viewDidLoad, move it within -viewWillAppear and implement removeObserver:name:object: in -viewWillDisappear.

This way, only the viewController that is currently on will respond to the notification.

Example:

- (void)viewWillAppear:(BOOL)animated
{
    //...
    [NSNotificationCenter defaultCenter] addObserver:self
                                            selector:@selector(doSomething:) 
                                                name:@"TestNotification"
                                              object:nil];
}

- (void)viewWillDisappear:(BOOL)animated
{
    //...
    [NSNotificationCenter defaultCenter] removeObserver:self
                                                   name:@"TestNotification"
                                                 object:nil];
}

- (void)doSomething:(NSNotification *)userInfo
{
    //...
    //if you push a viewController then the following is all you need
    [self.navigationController pushViewController:vcSomething
                                         animated:YES];

    //however.... if you're instead presenting a viewController modally then
    //you should implement "-removeObserver:name:object: in this method as well

    //[NSNotificationCenter defaultCenter] removeObserver:self
    //                                               name:@"TestNotification"
    //                                             object:nil];
    //[self presentViewController:vcSomething
    //                   animated:YES
    //                 completion:nil];

    //OR... in the completion parameter as:

    //[self presentViewController:vcSomething
    //                   animated:YES
    //                 completion:^{
    //                     [NSNotificationCenter defaultCenter] removeObserver:self
    //                                                                    name:@"TestNotification"
    //                                                                  object:nil];
    //                 }];
}

EDIT:

You (@Jonathan) commented:

I really appreciate your answer and it has helped me out a lot! I actually ran into one more scenario where this issue occur's and I'm not sure how to figure it out. Right now I have a VC that presents another VC modally. Each one has observers for the same NSNotification. Everything performs perfectly well when I'm in the modally presented VC, but once I dismiss that VC and return to the underlying one I have the same issue where the notification is being called multiple times. Do you have an idea for a solution in this case?

Now... regarding this...

FIRSTLY... NOTE:

  1. Multiple -addObserver:selector:name:object: will register the specified notification multiple times (means... same notification being registered for N times will call invoke the target selector N times)
  2. Presenting a ViewController (call it Child) from, say, Parent viewController will NOT invoke the -viewWillDisappear: of the Parent
    where as...
  3. Dismissing the Child viewController will still invoke -viewWillAppear: of the Parent

This creates an imbalance in the logic and if not handled (as per the commented lines in the code example of the doSomething method above), it results in the Parent registering for the notification multiple times (as it's -viewWillAppear: method is called more often than not -viewWillDisappear:)

also see: similar question

Community
  • 1
  • 1
staticVoidMan
  • 19,275
  • 6
  • 69
  • 98
  • This did it for me! Adding/removing the observer's in the viewWillAppear/Disappear. I previously added the observer's in the viewDidLoad method and removed them in the Dealloc method. Does that mean I should just delete the dealloc method and leave it up to viewWillDisappear to remove the observers? Thank you very much for the help! – BlueBear Jan 09 '14 at 20:05
  • 1
    @Jonathan : **in your case, it should be as per the answer** whereas in a general case, refer to [this link](http://stackoverflow.com/a/6469307/2857130). long story short... if you aren't doing `-removeObserver:` for the class anywhere then yes, call it within the `dealloc` method or... call it in `-viewWillDisappear:`. your wish – staticVoidMan Jan 09 '14 at 20:24
  • I really appreciate your answer and it has helped me out a lot! I actually ran into one more scenario where this issue occur's and I'm not sure how to figure it out. Right now I have a VC that presents another VC modally. Each one has observers for the same NSNotification. Everything performs perfectly well when I'm in the modally presented VC, but once I dismiss that VC and return to the underlying one I have the same issue where the notification is being called multiple times. Do you have an idea for a solution in this case? – BlueBear Jan 10 '14 at 22:25
  • 1
    @Jonathan : when you are presenting a `viewController` (say, child) then the parent `-viewController`'s `-viewWillDisappear:` is not called, however, when you are dismissing the child `viewController`, the parent's `viewWillAppear:` is called and eventually you have the parent having multiple `-addObserver:` (and never the `-removeObserver:`) which means, it will respond to the notification equal to the number of times `addObserver` on the same notification is done. For this, right now, all i can think of is doing `-removeObserver` **even** in the method where you present the child – staticVoidMan Jan 11 '14 at 03:21
  • @Jonathan : actually, comments aren't the right place for answers because it limits the quality of explanation (_and may help others_). You should've created a question (_if it doesn't already exist_) but anyways, i hope it helps :) – staticVoidMan Jan 11 '14 at 03:23
  • 1
    @Jonathan : ok, scrath that, i'll update this answer – staticVoidMan Jan 11 '14 at 06:05
  • I really appreciate the updated answer and what you're saying makes sense, but it is still not working. My 'parent' VC is a tab embedded in a tab bar. I present another view modally from that tab and it appears and works correctly. After I return from the modal view with the updated removeObserver: logic the notification fires twice upon pressing the button that fires it. If I'm inside of the modal view and then open another modal view on top of that one and return to the original modal view, there is no problem with the notification. It only happens on the tab bar parent VC. – BlueBear Jan 13 '14 at 18:00
0

it should be called only once so that it never gets called again

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(methodName:) name:@"name" object:nil];
});
ashfauck t
  • 21
  • 3