I have the following view controllers built in my app. The presenting controller is "PatientSelectViewController" (let's call it controller A) and it allows to either manually enter the patient ID in a text field or press a "Scan Barcode" button which would perform a segue to another view controller - namely, "BarcodeScanViewController" (let's call it controller B).
When B finishes scanning a barcode and returns a result (a patient ID), I notify the presenting view controller (A) about it and A is responsible for looking up the ID in a database. At this point the controller B should be dismissed. If the ID is found, then we transition to the third view controller - "PatientConfirmViewController" (let's call it C). However, if the ID is not found then I want a pop up message that says so and go again to the controller B to scan the barcode again.
Similarly, if the user decided to manually enter the ID in the text field instead of scanning it, then a successful ID would take me to the controller C while an unsuccessful one would give a pop up message and remain in controller A for another try.
I also want the controllers to be embedded in a navigation controller, so that I always have tabbar buttons that take me back to the previous view controller. For example, I will have a tabbar button to return to A from either B or C. Ideally, if I reach C after a successful barcode scan, I'd like the tabbar button to take me back to B - not A! - in case the user decides that she doesn't want to confirm this ID, the idea being that the user would likely want to rescan a barcode. But this is not critical.
I am having trouble implementing this for some reason. Here is an example of a screwed up behavior: I am calling A then calling B (to scan a barcode) and scan a barcode that I know is in the database. This correctly brings me to C with the patient info displayed. But then I decide to go back to A using the tabbar button "Enter Patient ID" Then I press the "Scan barcode" button again, again scan the same barcode as before but this time instead of a successful transition to C, I am getting this screen - note the screwed up tabbar! It must be saying "Confirm ID" and "Enter Patient ID" at the same time and the buttons go back to Login (this is the controller that invoked A in the first place) and "Scan Barcode" - that is, the controller B as if it were never popped up previously!
This can happen randomly after 2 or 3 or more successful scans. The log displays this:
nested push animation can result in corrupted navigation bar
Unbalanced calls to begin/end appearance transitions for .
Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
Here is how I implemented it:
In view controller A:
-(void)prepareForSegue: (UIStoryboardSegue *)segue sender: (id)sender
{
if ([[segue identifier] isEqualToString:@"BarcodeScanView"])
{
self.p_usingBarcodeScan=YES;
[[segue destinationViewController]setViewDelegate:self]; //sets itself as a delegate for receiving the result of a barcode scan from controller B
}
if ([[segue identifier] isEqualToString:@"ConfirmID"])
{
[[segue destinationViewController] setP_userInfo:p_userInfo] ; //passes the data to the controller C
}
}
The delegate method for receiving a barcode scan result (still in controller A):
- (void) didScanBarcode:(NSString *)result
{
self.p_userID = result;
[self.navigationController popViewControllerAnimated:YES];//Pop B from the navigation stack to return to A - is this right????
//Run the database query
[self lookUpID];
}
The method that looks up the ID in the database (still in A):
- (void) lookUpID{
/*.....
Does something here and gets a result of the lookup...
*/
// Do something with the result
if ([[result p_userName] length] > 0 ){ //Found the user!
p_userInfo = result;
[self performSegueWithIdentifier: @"ConfirmID" sender: self];
}
else {
UIAlertView * messageDlg = [[UIAlertView alloc] initWithTitle:nil message:@"User was not found. Please try again"
delegate:self cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
[messageDlg show];
//Here I'd like to perform this seque to B only if I got here after a barcode scan...
//Otherwise I am just staying in A...
if (self.p_usingBarcodeScan == YES ){
[self performSegueWithIdentifier: @"BarcodeScanView" sender: self];
}
}
return;
}
Just for completeness, in B once I managed to scan a barcode, I am calling this:
- (void)decodeResultNotification: (NSNotification *)notification {
if ([notification.object isKindOfClass:[DecoderResult class]])
{
DecoderResult *obj = (DecoderResult*)notification.object;
if (obj.succeeded)
{
decodeResult = [[NSString alloc] initWithString:obj.result];
[[self viewDelegate] didScanBarcode:decodeResult];
}
}
}
I am using push seques from A to B and from A to C and using storyboards. Here is a snapshot of the storyboard, with the segues from A to B ("BarcodeScan") and A to C ("ConfirmID") visible. Both are push segues:
Thanks a lot in advance!
OK, I am trying to partially answer my own question. Even after implementing He Was suggestion above, my troubles persisted and even multiplied (some details on these are in my comment in the discussion thread https://chat.stackoverflow.com/rooms/23918/discussion-between-peterd-and-he-was)
However, by some change I googled the log message I was getting: "nested push animation can result in corrupted navigation bar" and wound up reading this answer: https://stackoverflow.com/a/5616935/1959008, which suggested that my issue was using
[self.navigationController popViewControllerAnimated:YES];
that is with animated set to YES. Once I set it to NO, the issues with the tabbar disappeared (some small quirks remain and I hope to solve them soon). This is really strange - looks more like a bug than a feature to me, but I could be wrong of course...