Switch views quickly in iOS - ios

I have a view A, a view B and a view C. The idea is : I'm presenting view B on view A, view B has a delegate in view A that performs a segue : view C.
My problem is I have the message "Attempt to present view B on view A while a presentation is in progress!".
I can make it work adding a delay in the method of view A that performs the segue, but isn't there a better way to make it work ?
In view A, the delegate method :
- (void)addItemViewController:(NSString *)string text:(NSString *)textfield{
[self barcodeData:string type:1 :^(BOOL finished) {
if(finished){
[self performSegueWithIdentifier:#"viewC" sender:self];
}
}];
}
In view B
[self.delegate addItemViewController:saisieManuelleTextView.text text:nil];
[self dismissViewControllerAnimated:NO completion:nil];

Try invoking the delegate in the animation completion handler (some overlap in postings as redent84 also suggested this):
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate addItemViewController:saisieManuelleTextView.text text:nil];
}];
That way you're still getting the benefits of the delegate and avoid the possibility of simultaneous view controller animations.
Better yet, if view A presents view B, then make viewA responsible for dismissing viewB as well, instead of having viewB dismiss itself. That way viewB doesn't have to know how it was presented in the first place:
- (void)addItemViewController:(NSString *)string text:(NSString *)textfield{
[viewB dismissViewControllerAnimated:YES completion:^{
[self barcodeData:string type:1 :^(BOOL finished) {
if(finished){
[self performSegueWithIdentifier:#"viewC" sender:self];
}
}];
}];
}

The problem that you are seeing is related to the animation of the transitions. You are trying to animate the transition from view A to view C while there's already a transition active from view B back to view A.
Try using the completion block of the dismissViewControllerAnimated method:
[self dismissViewControllerAnimated:YES completion:^{
[self performSegueWithIdentifier:#"MySegue" sender:self];
}];
Or try disabling the animation of the transition from the view B to view A so it doesn't affect at all:
// For navigation controllers
[self.navigationController popViewControllerAnimated:NO];
// For modal controllers
[self dismissViewControllerAnimated:NO completion:nil];
EDIT:
You are presenting the new controller before dismissing the previous one, change the following lines:
[self.delegate addItemViewController:saisieManuelleTextView.text text:nil];
[self dismissViewControllerAnimated:NO completion:nil];
to this:
[self dismissViewControllerAnimated:NO completion:nil];
[self.delegate addItemViewController:saisieManuelleTextView.text text:nil];
Alternatively, you can maintain the animation using the completion block as described above:
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate addItemViewController:saisieManuelleTextView.text text:nil];
}];

Related

Simultaneously dismissing a view controller and then presenting one

So I have a VC that i call on when a button is clicked (It's a MFMailComposeViewController) and when the message is sent, It dismisses the ViewController and i want it to present a different one once it's done sending. But instead of doing this it crashes after sending the email every time. i know I'm doing something wrong but i'm not sure what.
Here's my code that dismisses and presents the new one.
- (void) mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
[self dismissViewControllerAnimated:true completion:nil];
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];}
I was thinking that the problem was that I used self but I'm not sure what I use in place of that.
It is crashing because you are asking for both dismiss and present animations to begin simultaneously. So you can fix this in 2 ways:
Wait for the first animation to complete before starting the next one. This will animate the dismissal of the current VC and animate the presentation of the new one. Of course the user would have to wait for both animations to complete before they can continue interacting with the app. To do this, present _emailConfirmationPage in the completion block of dismissing the current VC like this:
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
}];
Animate either dismiss OR present, but not both. This might be better because the user would have to wait only for 1 animation and that 1 animation will most probably be sufficient to ensure a fluid user experience.
[self dismissViewControllerAnimated:NO completion:nil];
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
Try put your [self presentViewController:_emailConfirmationPage animated:YES completion:nil];} into completion: block in the [self dismissViewControllerAnimated:true completion:nil]; , it will execute present vc after it has complete dismiss the other vc
Like this:
[self dismissViewControllerAnimated:YES completion:^{
[self presentViewController:_emailConfirmationPage animated:YES completion:nil];
}];
Your code did not work because its still animating dismissViewController and there can't be 2 animation at the same time

Display RootViewController only when dismiss current ViewController

I'm doing an ios project without using any story board and xibs.
Here I've come out one problem on presenting my UIViewController.
Here is the senario:
My RootViewController has 2 UIButton's that can present ViewControllerA and ViewControllerB when being pressed.
-(IBAction)btnAclicked:(id)sender{
[self.navigationController presentViewController:ViewControllerA];
}
-(IBAction)btnBclicked:(id)sender{
[self.navigationController presentViewController:ViewControllerB];
}
And now inside ViewControllerA
I have a button to presentViewContollerB as well, and I want only the RootViewController to be displayed when I dismiss ViewContollerB.
In order to do this, I need to dismiss ViewControllerA first then presentViewControllerB. I know there is ways like using delegate to make it works, but I just want to know if there is any easier way to do this.
To be emphasised is that I want to use presentViewController only, not pushViewController. Thanks
I have an answer for your question,just do the following code,
in RootViewController.m
- (IBAction)gotoViewA:(id)sender
{
ViewControllerA *viewControllerA = [[ViewControllerA alloc]initWithNibName:#"ViewControllerA" bundle:nil];
[self presentViewController:viewControllerA animated:YES completion:nil];
}
- (IBAction)gotoViewB:(id)sender
{
ViewControllerB *viewControllerB = [[ViewControllerB alloc]initWithNibName:#"ViewControllerB" bundle:nil];
[self presentViewController:viewControllerB animated:YES completion:nil];
}
in ViewControllerA.m
- (IBAction)actionBackFromA:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
in ViewControllerB.m
- (IBAction)actionBackFromB:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Use the completion block of presentviewcontroller,viz will executed first
[self presentViewController:goTo_B animated:YES completion:^{
//Dismiss A
[self dismissViewControllerAnimated:YES completion:nil];
}];
If this does not work,write dismiss and just after it present your new controller
[self dismissViewControllerAnimated:YES completion:nil];
[self.view.window.rootViewController presentViewController:goTo_B animated:YES completion:^{ }];
There's another way out, but i don't know whether its appropriate or not.
Create a property of type View Controller A in view controller B.
While presenting view controller B, assign an instance of View Controller A to the property in view Controller B.
[self presentViewController:goTo_B animated:YES completion:^{
goTo_B.propertyOfViewControllerA = self;
}];
After that while dismissing view controller B you can do the either of the two:
[self dismissViewControllerAnimated:YES completion:nil];
[propertyOfViewControllerA dismissViewControllerAnimated:YES completion:nil]
or
[self dismissViewControllerAnimated:YES completion:^{
[propertyOfViewControllerA dismissViewControllerAnimated:YES completion:nil]
}];

Pop view controller after dismiss

I have a navigation controller A on which i push the view controller B. From B i present modally the view controller C. I need to dismiss C and pop B at the same time. I would like to that in sequence, keeping the dismiss animation first and then the pop animation from B to A. I tried without success this code:
[self dismissViewControllerAnimated:YES completion:^{
[self.presentingViewController.navigationController popViewControllerAnimated:YES];
}];
Any suggestion on how can i achieve this?
If you are writing in C viewcontoller then :
UIViewController *pvc = self.presentingViewController;
UINavigationController *navController = [pvc isKindOfClass:[UINavigationController class]] ? (UINavigationController *)pvc : pvc.navigationController;
[self dismissViewControllerAnimated:YES completion:^{
[navController popViewControllerAnimated:YES];
}];
or if in B view controller
[self.presentedViewController dismissViewControllerAnimated:YES completion:^{
[self.navigationController popViewControllerAnimated:YES];
}];
I had tried popping two times in succession before but not dismissing one and popping another one. You can try what I did and see if it works for you.
In Subview B:
- (void)subViewCController:(SubViewCController *)controller didSelectID:(NSNumber *)theID
{
// do something with theID...
// for my case, I popped
// [self.navigationController popViewControllerAnimated:YES];
// for your case
[self dismissViewControllerAnimated:YES completion:nil];
// Somehow, adding a small delay works for me and the animation is just nice
[self performSelector:#selector(backToSubViewA) withObject:nil afterDelay:0.6];
}
- (void)backToSubViewA
{
[self.navigationController popViewControllerAnimated:YES];
}
Are you using storyboards and segues? If so, you can use unwind segue:
In your first view controller (the one you want to jump back to, not the one you're returning from), create an unwind segue action:
- (IBAction)gotoMainMenu:(UIStoryboardSegue *)segue
{
// if you need to do anything when you return to the main menu, do it here
}
Then in storyboard, you can create a segue from the "dismiss" button to the exit outlet icon () in the bar above the scene, and you'll see main menu listed there.

How to move back to the viewcontroller?

There are 3 view controllers View1,View2 and View3.
From view3 I have to navigate to view1.
I have tried the following code but it doesn't work.
//in View3.m
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
{
appDelegate.isComingFromCountries = YES;
[self dismissViewControllerAnimated:YES completion:nil];
}
//in View2.m
-(void)viewWillAppear:(BOOL)animated
{
if (appDelegate.isComingFromCountries == YES)
{
appDelegate.isComingFromCountries = NO;
[self dismissViewControllerAnimated:YES completion:nil];
}
}
But this code doesn't work. How do I handle this?
You can use use presentingViewController for dismissing it,
try this -
[self.presentingViewController.presentingViewController dismissModalViewControllerAnimated:YES];
A -> B -> C
Running the above code in modal C will take you back to A.
You have to use delay method to perform animation so that main thread should start performing in B viewController
[self performSelector:#selector(methodForDissmiss) withObject:nil afterDelay:0.5];
then write dismiss code in selector method to work by your logic.
If you are returning to the root view controller from a series of modally presented view controllers then the following code will work in iOS6
[self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
I think, It may help you. According to your code( in comments area), You second view controller push from first view controller, then present third from second. So you have to set delegate as second in third. And do your code as below in third VC.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex;
{
appDelegate.isComingFromCountries = YES;
[self dismissViewControllerAnimated:YES completion:^{
[self.delegate dismissModal];
};
}
In SecondVC.m
-(void)dismissModal
{
[self.navigationController popViewControllerAnimated:NO];
}
Also you can pop to view 1 from view 3 on like this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
[self.navigationController dismissViewControllerAnimated:NO completion:nil];
[self.navigationController dismissViewControllerAnimated:YES completion:nil];
}
You can try this one :
if you are pushing one viewController to another and want to move back then use this one
[self.navigationController popViewControllerAnimated:YES];
if you are presenting viewController use modelly and want to move back then use this one
[picker dismissViewControllerAnimated:YES completion:nil];
also you can try this because first you need to dismiss third viewController then second one.
UIViewController *viewController = [self parentViewController];
[self dismissModalViewControllerAnimated:NO];
[viewController dismissModalViewControllerAnimated:YES];

Can't call the back button on a UITabBarController

I have a tab bar controller with a view inside a navigation controller. One of the buttons on this pops up a modal view. I set my starting view as the delegate for the modal, and call the following:
- (void)dischargeSaveComplete:(dischargeView *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
[self.navigationController popViewControllerAnimated:YES];
}
It correctly dismisses the modal view, but it doesn't call the back button. Do I need to do something else since it's inside a tab bar controller?
I tried set both to animation no as seen below, and it doesn't work either.
- (void)dischargeSaveComplete:(ehrxEncounterDischargeView *)controller
{
[self dismissViewControllerAnimated:NO completion:nil];
[self.navigationController popViewControllerAnimated:NO];
}
Found the solution based on one of the answers, because I was in a tab bar controller, I had to call the popviewcontroller from the first view as seen below:
- (void)dischargeSaveComplete:(ehrxEncounterDischargeView *)controller
{
[self dismissViewControllerAnimated:YES completion:^(void)
{
demoView *e = [self.parentViewController.tabBarController.viewControllers objectAtIndex:0];
[e.navigationController popViewControllerAnimated:YES];
}];
}
You want 2 animations to follow one another, which is not allowed as you did it. You either have to cancel one of the animation or place popViewController inside the completion block for your first animation.
[self dismissViewControllerAnimated:YES completion:^(void) {
[self.navigationController popViewControllerAnimated:YES];
}
];
u can try delay in performing second action
[self.navigationController performSelector:#selector(popViewControllerAnimated:) withObject:#"YES" afterDelay:1];
hope it works.. happy coding :)

Resources