0

I've a BaseController (BVC) where I've started to listen to notifications in viewWillAppear & stop listening to them at viewWillDisappear methods.

BVC
- CustomView (Notification received updates this VC) 

Now I've subclassed 4 different controllers from BVC.

BVC
|-- FirstVC (FVC)
|-- SecondVC (SVC)
|-- ThirdVC (TVC)

Now, I've one TarBarController which has these 3 VC's as its items via a NavigationViewController (NVC)

TabBarController
|- NVC->FVC
|- NVC->SVC
|- NVC->TVC

My problem is, I've sending across a playWillBegin notification from a Singleton object which is a AVPlayer instance. Notification is received from the VC which is active on top, but if I dont switch tabs quickly the notifications are not received in the other controllers.

I also read in SO, that other VC are not instantiated and that is reason the notifications are not received. But I can't use init method in VC because its asking me to use initWithaCoder

My project is all code and does not use storyboard etc. So my TabBarController is a swift class, has the items there and TabBarController instance in AppDelegate

EDIT 1: Relevant SO QA links Link1 Link2

EDIT 2: To clarify why I'm not being able to add anything in init() method of VC BaseViewController - Snippet

init() {
    super.init() //Please refer image for error message
}

required init?(coder aDecoder: NSCoder) {

}

TabBarController

class TabBarController: UITabBarController, UITabBarControllerDelegate {

    override func viewDidLoad() {

        tabBar.tintColor = UIColor.whiteColor()
        self.delegate = self

        let FNVC = UINavigationController(rootViewController: FVC())
        //Other initialization code 

        let SNVC = UINavigationController(rootViewController: SVC())
        //Other initialization code 

        let TNVC = UINavigationController(rootViewController: TVC())
        //Other initialization code 
        viewControllers = [FNVC, SNVC, TNVC]

     } 
}

enter image description here

Community
  • 1
  • 1
user6083088
  • 1,047
  • 1
  • 9
  • 27

1 Answers1

1

The first reason for the notification not being received by non-active tabs is that invisible controllers are unsubscribed. When you're switching, say, from tab #2 (SecondVC) to tab #1 (FirstVC), the -[viewWillDisappear:] is called for the SecondVC and it stops listening thus that controller won't receive notifications anymore.

I propose to start listening in -[viewDidLoad:] and stop in -[dealloc] (or deinit in Swift):

Obj-C:

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

- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Swift:

override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(BaseController.someFunction), name:Notification.Name("foobar"), object: nil)
}

deinit {
    NotificationCenter.default.removeObserver(self)
}

The second reason is that view might not be loaded yet, therefore [-viewDidLoad:] is not called and the corresponding view controller is not subscribed. To solve this you may force load VC's view when you are creating you view controllers for tabs, e.g.:

Obj-C:

FirstVC *vc1 = [FirstVC new];
[vc1 view];    // loading view, subscribing

Swift:

let vc1 = FirstVC()
let _ = vc1.view

UPDATE

Assuming that you can use initializers (as explained by @danh in the comments below), it is better to subscribe in the init method instead of viewDidLoad. In this case there is no need to force view loading and you can omit [vc view] calls:

Obj-C:

- (instancetype) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])){
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notified:) name:@"foobar" object:nil];
    }

    return self;
}

Swift:

override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
    super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
    NotificationCenter.default.addObserver(self, selector: #selector(BaseViewController.someFunc), name:Notification.Name("foobar"), object: nil)
}
degapps
  • 794
  • 5
  • 10
  • Great answer, but your first theory is correct and second is confusing. The OP is almost certainly incorrect that the vcs are not yet allocated. – danh Jan 04 '17 at 17:14
  • 1
    More simply put, the answer is: have the vcs observe notifications for their entire lifecycle, not just when visible. – danh Jan 04 '17 at 17:14
  • @danh You are right – VCs should be allocated, but it does not mean that their views are loaded. The latter will generally happen when the tab's controller is going to appear for the first time. – degapps Jan 04 '17 at 17:42
  • Then the init method that the OP uses can be overridden and observation can begin there. That's a better bookend with dealloc anyway. Forcing the view load because we want viewDidLoad to run is icky. If we must stick our fingers in there, then follow `vc1 = [FirstVC new];` with a direct add: `[[NSNotificationCenter defaultCenter] addObserver: vc1` – danh Jan 04 '17 at 17:50
  • I just assumed that there is kind of problem with `init` methods behind the scenes and the OP wants to avoid touching them for some reason ("But I can't use init method in VC because its asking me to use initWithaCoder"). If that's not the case then, surely, it is better to subscribe in `init`. – degapps Jan 04 '17 at 18:02
  • I have just updated my post with reason why I'm facing a difficulty with init() method. @danh Please would you explain it a bit more, I'm a beginner and am not following on this one – user6083088 Jan 04 '17 at 18:35
  • @user6083088 I have updated my answer for the case with `init` methods. – degapps Jan 04 '17 at 19:31
  • @degapps - Thank you for the links, I'm a bit shocked that if I'm using to instantiate a VC using code. I have to call the nib method! Thank you I've implemented your suggestion and it is working as expected. Now though not part of the original question, how do I transfer the notification status to any new controller than I can push onto any NVC of any tab bar item? – user6083088 Jan 04 '17 at 19:38