Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS - unwind to my initial view controller

I am using an unwind segue to unwind to the initial view controller in my storyboard. The unwind works great, I implemented this method in my initial view controller:

- (IBAction) unwindToInitialViewController:(UIStoryboardSegue *) unwindSegue {

}

However if I try an segue to another view controller after I do the unwind I get the following error:

Warning: Attempt to present on whose view is not in the window hierarchy!

It seems like this only occurs if I unwind to the view controller that is checked as 'Initial View Controller' in the storyboard. Is this a bug? Should I be able to unwind to that initial controller? Other ideas?

EDIT:

Here is how I perform the second segue:

[self performSegueWithIdentifier:@"mySegue" sender:nil];

I should note that this is a login/logout problem. When I log in the first time the segue from my login controller to my next controller works. When I logout I unwind to the initial view controller. I then log in again and the segue from my login controller to the next controller does not work.

EDIT 2:

From more research I have found its because I am using a delegate for my login. Login is async, I make a call with AFNetworking and when its done I call my login delegate (the login VC in this case). At that point the login VC can segue to the view.

Login code:

- (void) login: (NSDictionary *) parameters {
    [http.manager POST:url parameters:parameters success:^(AFHTTPRequestOperation *operation, NSDictionary *response) {
       [self.loginDelegate loginSuccess:response]; 
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
       [self.loginDelegate loginFailure:error]; 
    }];
}

My login VC which is the delegate:

- (void) loginSuccess:(NSDictionary *) response {
    // setup user info based on response
    ...
    // Segue 
    [self performSegueWithIdentifier:@"loginSuccessSegue" sender:nil];
}

I have checked that I am in the main thread when and I segue and still no luck. I know that AFNetworking always calls the success/failure blocks on the main thread too.

The tricky part. If I change that above code to use a block and not a delegate the storyboard/segue does not get messed up and I can login and logout many times with no problem.

Why does the segue work the first time with the delegate pattern, but on logout (unwind), can I not use that segue again?

EDIT 3:

More investigation shows that on unwind my login VC viewDidAppear is called twice. On initial unwind the view looks to still be on the stack, show it shows quickly and viewDidAppear is called. However this is animated away quickly and viewWillAppear is called a second time with a different VC. I think this might be the root of the problem. Why when I unwind to that VC is it animated away only to be animated back in?

like image 576
lostintranslation Avatar asked Nov 09 '22 04:11

lostintranslation


1 Answers

Please check, whether your loginDelegate is nil during the second login attempt. If it is nil the "delegate calls" will just go to nowhere. Also please check whether the loginDelegate points to the instance you expect. If it points to an "old" instance, the wrong views may be tried to be presented.

The set of methods viewDidLoad, viewDidAppear, viewWillAppear, etc. can be called in unexpected order especially when going back in navigation or presenting an ad and coming back from it. If you have different initialization/setup tasks distributed among these methods, you may end up with a partly initialized view controller.

(Thinking about the problem I lost your statement about the error encountered, so probably the delegate is not nil.)

EDIT:

I ran one of my tiny unwind test projects and there log the viewDidAppear calls:

viewDidAppear: <ViewController: 0x7a687700>
viewDidAppear: <VC2: 0x7a70e970>
viewDidAppear: <VC3: 0x7a694d50>
unwind target
viewDidAppear: <VC2: 0x7a70e970>
viewDidAppear: <ViewController: 0x7a687700>
viewDidAppear: <VC2: 0x7a71b790>
viewDidAppear: <VC3: 0x7a694d20>
unwind target
viewDidAppear: <VC2: 0x7a71b790>
viewDidAppear: <ViewController: 0x7a687700>

Doing the unwind in VC3 briefly shows VC2 and eventually ends up at the target ViewController. Now the second "login" leads to different instances of the view controllers.

Do you keep references to "old" view controllers?

Another reason might be, that your "logout" detection fires twice (once when coming along the unwind and one more time when an intermediate or the initial view controller detects the need to login?).

like image 116
Rainer Schwarze Avatar answered Nov 15 '22 11:11

Rainer Schwarze