iPhone viewWillAppear not firing

2019-01-01 14:33发布

I've read numerous posts about people having problems with viewWillAppear when you do not create your view hierarchy just right. My problem is I can't figure out what that means.

If I create a RootViewController and call addSubView on that controller, I would expect the added view(s) to be wired up for viewWillAppear events.

Does anyone have an example of a complex programmatic view hierarchy that successfully receives viewWillAppear events at every level?

Apple's Docs state:

Warning: If the view belonging to a view controller is added to a view hierarchy directly, the view controller will not receive this message. If you insert or add a view to the view hierarchy, and it has a view controller, you should send the associated view controller this message directly. Failing to send the view controller this message will prevent any associated animation from being displayed.

The problem is that they don't describe how to do this. What does "directly" mean? How do you "indirectly" add a view?

I am fairly new to Cocoa and iPhone so it would be nice if there were useful examples from Apple besides the basic Hello World crap.

21条回答
十年一品温如言
2楼-- · 2019-01-01 15:10

Firstly, the tab bar should be at the root level, ie, added to the window, as stated in the Apple documentation. This is key for correct behavior.

Secondly, you can use UITabBarDelegate / UINavigationBarDelegate to forward the notifications on manually, but I found that to get the whole hierarchy of view calls to work correctly, all I had to do was manually call

[tabBarController viewWillAppear:NO];
[tabBarController viewDidAppear:NO];

and

[navBarController viewWillAppear:NO];
[navBarController viewDidAppear:NO];

.. just ONCE before setting up the view controllers on the respective controller (right after allocation). From then on, it correctly called these methods on its child view controllers.

My hierarchy is like this:

window
    UITabBarController (subclass of)
        UIViewController (subclass of) // <-- manually calls [navController viewWill/DidAppear
            UINavigationController (subclass of)
                UIViewController (subclass of) // <-- still receives viewWill/Did..etc all the way down from a tab switch at the top of the chain without needing to use ANY delegate methods

Just calling the mentioned methods on the tab/nav controller the first time ensured that ALL the events were forwarded correctly. It stopped me needing to call them manually from the UINavigationBarDelegate / UITabBarControllerDelegate methods.

Sidenote: Curiously, when it didn't work, the private method

- (void)transitionFromViewController:(UIViewController*)aFromViewController toViewController:(UIViewController*)aToViewController 

.. which you can see from the callstack on a working implementation, usually calls the viewWill/Did.. methods but didn't until I performed the above (even though it was called).

I think it is VERY important that the UITabBarController is at window level though and the documents seem to back this up.

Hope that was clear(ish), happy to answer further questions.

查看更多
公子世无双
3楼-- · 2019-01-01 15:11

I finally found a solution for this THAT WORKS!

UINavigationControllerDelegate

I think the gist of it is to set your nav control's delegate to the viewcontroller it is in, and implement UINavigationControllerDelegate and it's two methods. Brilliant! I'm so excited i finally found a solution!

查看更多
大哥的爱人
4楼-- · 2019-01-01 15:11

I just had the same issue. In my application I have 2 navigation controllers and pushing the same view controller in each of them worked in one case and not in the other. I mean that when pushing the exact same view controller in the first UINavigationController, viewWillAppear was called but not when pushed in the second navigation controller.

Then I came across this post UINavigationController should call viewWillAppear/viewWillDisappear methods

And realized that my second navigation controller did redefine viewWillAppear. Screening the code showed that I was not calling

[super viewWillAppear:animated];

I added it and it worked !

The documentation says:

If you override this method, you must call super at some point in your implementation.

查看更多
爱死公子算了
5楼-- · 2019-01-01 15:11

I'm not sure this is the same problem that I solved.
In some occasions, method doesn't executed with normal way such as "[self methodOne]".

Try

- (void)viewWillAppear:(BOOL)animated
{
    [self performSelector:@selector(methodOne) 
           withObject:nil afterDelay:0];
}
查看更多
零度萤火
6楼-- · 2019-01-01 15:11

You should only have 1 UIViewController active at any time. Any subviews you want to manipulate should be exactly that - subVIEWS - i.e. UIView.

I use a simlple technique for managing my view hierarchy and have yet to run into a problem since I started doing things this way. There are 2 key points:

  • a single UIViewController should be used to manage "a screen's worth" of your app
  • use UINavigationController for changing views

What do I mean by "a screen's worth"? It's a bit vague on purpose, but generally it's a feature or section of your app. If you've got a few screens with the same background image but different overlays/popups etc., that should be 1 view controller and several child views. You should never find yourself working with 2 view controllers. Note you can still instantiate a UIView in one view controller and add it as a subview of another view controller if you want certain areas of the screen to be shown in multiple view controllers.

As for UINavigationController - this is your best friend! Turn off the navigation bar and specify NO for animated, and you have an excellent way of switching screens on demand. You can push and pop view controllers if they're in a hierarchy, or you can prepare an array of view controllers (including an array containing a single VC) and set it to be the view stack using setViewControllers. This gives you total freedom to change VC's, while gaining all the advantages of working within Apple's expected model and getting all events etc. fired properly.

Here's what I do every time when I start an app:

  • start from a window-based app
  • add a UINavigationController as the window's rootViewController
  • add whatever I want my first UIViewController to be as the rootViewController of the nav controller

(note starting from window-based is just a personal preference - I like to construct things myself so I know exactly how they are built. It should work fine with view-based template)

All events fire correctly and basically life is good. You can then spend all your time writing the important bits of your app and not messing about trying to manually hack view hierarchies into shape.

查看更多
残风、尘缘若梦
7楼-- · 2019-01-01 15:15

Correct way to do this is using UIViewController containment api.

- (void)viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view.
     UIViewController *viewController = ...;
     [self addChildViewController:viewController];
     [self.view addSubview:viewController.view];
     [viewController didMoveToParentViewController:self];
}
查看更多
登录 后发表回答