EDIT Problem reinstated from scratch.
I have a ViewController A which has a Navigation Bar Button which presents an UIImagePickerController of sourcetype UIImagePickerControllerSourceTypeCamera, we'll call this ViewController B. In the didFinishPickingMediaWithInfo: method of A, I then present ViewController C.
The problem now starts here. When I finally reach C, we can see that view stack is clearly:
A -modal-> B -modal-> C
From C I then have a "Back" button present on the navigation bar which should take me back to B. However, since B is an UIImagePickerController, I cannot reuse it and it must be dismissed. So, to make this happen, I currently have the following method executing for that "Back" button on C:
- (IBAction)backOnPost:(UIBarButtonItem *)sender
{
[self.view endEditing:YES];
UINavigationController *LogControl = [self.storyboard instantiateViewControllerWithIdentifier:#"LogControl"];
RGLogViewController *logView = (RGLogViewController *)LogControl.topViewController;
[UIView animateWithDuration:0.25 animations:^{
self.presentingViewController.view.alpha = 0;
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:NO completion:nil];
} completion:^(BOOL finished){
[logView setBoolBackPushed];
}];
}
The above code works in that it takes me back to A by dismissing B and C. However, you can still see the transition because of a bit of "black-screening" from the dismissal of the two ViewControllers. You can see in the completion block [logView setBoolBackPushed];, this sets a static BOOL variable to true so that the beginning of A's viewWillAppear: presents a new UIImagePickerController immediately - the method looks like this:
- (void)viewWillAppear:(BOOL)animated
{
NSLog(#"HERES postBackButtonPushed:%hhd", postBackButtonPushed);
if(postBackButtonPushed == true)
{
self.view.hidden = YES;
self.navigationController.navigationBar.hidden = YES;
self.tabBarController.tabBar.hidden = YES;
UIImagePickerController * imagePicker = [[UIImagePickerController alloc] init];
imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
imagePicker.delegate = self;
[self presentViewController:imagePicker animated:NO completion:^{}];
}
This is currently how I am getting the following desired effect: Go from A to the camera (B), to C and then back to a new camera which is our new B. This is achieved w/ almost perfect seamless transitions.
Im still having problems with being able to slightly see the transitions. Another problem is the timing. This collaboration of dismissing and presenting ViewControllers takes more time than I would like it to. It's almost instantaneous, but I would like something better. What should I do and am I doing something wrong?
there are some possible cases that cause your problem :
presenting B after A and then presenting C is a bit strange behaviour for both UX/UI and by code. Either push B from A and then present C over B OR dismiss A, present B, dismiss B, present C.
there are completion methods and you are not using them. your code show that you call dismissViewControllerAnimated twice for C and B for ex. you should handle presenting and dismissing order. I think iOS 8.2+ or 8.3+ start to handle them more efficiently. And if there is an error, it will crash after this version.
imagepicker controller cause this error while dismissing VCs in different ways. For ex. showing alert popup before presenting a VC can cause also a crash.
I believe this will work for you.
Do!
[self.presentingViewController dismissViewControllerAnimated:NO completion:^{
[RefrenceOfPreviousViewController dismissViewControllerAnimated:YES completion:^{
}];
}];
Instead of
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:NO completion:nil];
Related
I have a UiViewController A which opens a second UIViewController B as follows:
BViewController *BController = [Utilities getMainStoryBoardWithIdentifier:#"BViewController"];
[self presentViewController:webViewController animated:YES completion:nil];
Now on UiViewController B, when I want to close it, I would like to close the A also. So I'm using the following:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
But only the B is closing...
Any idea ?
Can you please try following code :
[self dismissViewControllerAnimated:YES completion:nil];
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
Where self is your B controller. I've just tried the code and it works well. Tell me if any other help is needed.
Try using an unwind segue.
If you have X which presents A, which in turn presents B, add
func unwindToX(_ segue: UIStoryboardSegue) { }
to view controller X
Then in your storyboard, drag from the close button in B to it's exit and select unwindToX. Clicking the button will then dismiss both view controllers. This also works with view controllers pushed on to a nav stack, or a combination of both.
Im trying to dismiss a ModaViewController called C, back to A. The ModalViewController is presented in B. Therefore the Navigation flow is A->B - (present ModalView) -> C. I am able to dismiss the ModalViewController back to B, but I am unable to pop back to A in the completion bracket. This is the code I have tried:
[self dismissViewControllerAnimated:YES completion:^{
[self.navigationController popToViewController:[[self.navigationController viewControllers] objectAtIndex:0] animated:YES];
}];
The ModalViewController is dismissed but does not pop back to A. I call this block of code in an IBAction.
Any advice?
On a second note, when I dismiss the ModalViewController all my UIPickers in Controller B are empty/ deallocated. I am using ARC as well.
The problem with your code is that self.navigationController will be nil. If you have a controller (A) embedded in a navigation controller, and that controller pushes to another controller (B) which then presents your last controller (C), then you need to do something like this,
-(IBAction)dismissToBThenPop:(id)sender {
UINavigationController *nav = (UINavigationController *)self.presentingViewController;
[self dismissViewControllerAnimated:YES completion:^{
[nav popViewControllerAnimated:YES];
}];
}
Even though you present C from B, the actual presentingViewController will be the navigation controller. This code will dismiss C then pop B, but you will see B for an instant before ut pops back to A. If you don't want to see this, then you should use an unwind segue to go directly back to A from C.
Your second problem about the pickers being empty and deallocated should not be happening under the scenario that you say you have. You will have to provide more information about what you're doing in B to solve that problem.
Create a protocol in ModalViewController, let's say ModalViewControllerDelegate with a method -(void)dismissTheModal, and make B implement this protocol. Before showing the ModalViewController, do modalViewController.delegate = self. When you're IBAction is called, do [self.delegate dismissTheModal], and in controller B you should do :
-(void)dismissTheModal {
[self dismissViewControllerAnimated:YES completion:^{
[self popViewController];
}];
i have a uivewcontroller (let's call it A) that loads uiviewcontroller (B) setting it up as a delegate which i use to close B and continue code on A. There is also a scenerio where B leads to another uiviewcontroller C (again with a delegate). When C is closed i use it's delegate to return to B but in this scenario i also want B to be immediately dismissed and the code to return to A. Now, B->A works, and C->B works but doing C->B->A fails at B with an error:
"attempt to dismiss modal view controller whose view does not currently appear" it appears to be trying to dismiss view C again.
Both viewcontrollers are being dismissed with this code (though the code sits in different uiviewcontrollers)
[self dismissViewControllerAnimated:YES completion:nil];
Am I using delegates correctly for what i want, or should i be using a different process?
Code for option 1 (A->B, B<-A):
A -> B
scorer_turn *st = (scorer_turn *) segue.destinationViewController;
st.st_delegate=self;
st.league = _match.league;
st.match = _match;
st.leg = _leg;
st.set = _set;
B -> A
-(void)closeView{
[_st_delegate scorer_turn:self didFinish:YES];
}
-(void)scorer_turn:(scorer_turn *)controller didFinish:(BOOL)finish{
[self dismissViewControllerAnimated:YES completion:nil];
}
Code for option 2 (A->B, B->C, C->B->A):
as above plus:
B -> C
matchSummaryViewController *ms = (matchSummaryViewController *) segue.destinationViewController;
ms.match = _match;
ms.oneScreen = NO;
ms.delegate = self;
[[segue destinationViewController] setManagedObjectContext:self.managedObjectContext];
C -> B, B -> A
in C:
[_delegate matchSummaryViewController:self didFinish:YES];
in B:
[self dismissViewControllerAnimated:YES completion:nil];
[_st_delegate scorer_turn:self didFinish:YES];
in A (this is where the error occurs):
-(void)scorer_turn:(scorer_turn *)controller didFinish:(BOOL)finish{
[self dismissViewControllerAnimated:YES completion:nil];
}
If ViewController B is a modalViewController, and ViewController C is pushed onto the same modalViewController, then calling
[self dismissViewControllerAnimated:YES completion:nil];
should dismiss the entire modal view - as in, it'll dismiss C and skip B all-together (B wont even appear), which would cause problems if B tries to also call dismissViewControllerAnimated:completion: since there is no more modal view to dismiss.
Now, if you have some other set-up, such as B being a modal which, when pushing C, it actually dismisses itself and launches a new modal view (this would happen if you always use the presentViewController:animated:completion function), then you have other design problems - you shouldn't be swapping modal views like that, instead use
[self.navigationController pushViewController:C animated:YES]
to show the view and
[self.navigationController popViewControllerAnimated:YES]
to dismiss View C and return to View B.
This is assuming of course that you're using a navigationController to manage your views (which you typically should be doing).
If on the other hand you aren't using modal views at all, then dismissViewControllerAnimated:completion: isn't what you want to use at all.
I have three ViewControllers and its order is (A present B which presents C),
when I stay in the C viewController ,I have to dismiss ViewController to B viewController.
for C viewController ,its presentingViewCOntroller is B viewController
of course ,I can use
[self dismissViewControllerAnimated:YES completion:NULL];//self means C ViewController
But I was wondering I can also use the following method:
[self.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
because the presentingViewController of C is B ViewController, but it did the same effect.
self means C ViewController while self.presentingViewController means B ViewController,but they also did the same work
The second question is I cannot use the following to dismiss two viewController in succession:
[self dismissViewControllerAnimated:YES completion:^{
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
}]; //self means C viewController
Thanks for your help!
of course ,I can use
[self dismissViewControllerAnimated:YES completion:NULL];//self means C ViewController
This only dismisses C, not B as you seem to want.
But I was wondering I can also use the following method:
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
Yes, this works. When in doubt, try it out.
The second question is I cannot use the following to dismiss two viewController in succession:
[self dismissViewControllerAnimated:YES completion:^{
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:NULL];
}]; //self means C viewController
That's not a question. Anyway, the reason it doesn't work is that after you dismiss yourself, your presentingViewController is nil. You need to store it in a temporary variable.
UIViewController *gp = self.presentingViewController.presentingViewController;
[self dismissViewControllerAnimated:YES completion:^{
[gp dismissViewControllerAnimated:YES completion:nil];
[self.presentingViewController.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}];
Of course, the two will have different animations, you'll need to decide which you prefer.
see documentation:
The presenting view controller is responsible for dismissing the view
controller it presented. If you call this method on the presented view
controller itself, it automatically forwards the message to the
presenting view controller.
Please check: self.presentingViewController.presentingViewController is A viewController or not.
if not, I think you need use delegate or handler.
In my case, I used a tabbarcontroller as a rootViewController. When I used self.presentingViewController.presentingViewController, I got rootViewController (tabbarcontroller) which it have no response to presentingViewControler method.
I have three viewController
First, Second and Third
from Second to open Third I use
Third *third = [[Third alloc]initWithNibName:#"Third" bundle:nil];
[self presentModalViewController:third animated:YES];
[third release];
Now I want return from third to first; then I set in viewDidAppear in second this code:
[self dismissModalViewControllerAnimated:NO];
but for 1 second I see Second and I don't want watch it...how can I do?
You need to dismiss third view controller first and then second Viewcontroller. Do the following code when you want to go first view controller.
-(void)goToFirstView{
UIViewController *vc = [self parentViewController];
// UIViewController *vc = [self presentingViewController]; //ios 5 or later
[self dismissModalViewControllerAnimated:NO];
[vc dismissModalViewControllerAnimated:YES];
}
How is the Third modal view being dismissed in the first place? Perhaps by the user touching a 'Done' button? If so, it is in the handler for the button that you want to dismiss both.
You can dismiss both as:
[self dismissModalViewControllerAnimated: YES];
[self.presentingViewController dismissModalViewControllerAnimated: NO];
This happens coz viewDidAppear is called everytime before the view appears so as soon as it appears you dismiss it and it disappears..
I don't think what u are trying to do can be achieved with modalViewControllers...
instead use a navigationController and keep adding your viewcontrollers onto the stack and when you want to goto the First view controller just call
[self.navigationController popToRootViewControllerAnimated:YES];
EDIT:
just thought of it this can be achieved by using delegation.. you make second the delegate of third and as soon you dismiss the thirdviecontroller send the delegate a message.In this message call [self dismissModalViewControllerAnimated:NO];..
and you are done.. (pretty easy if you know delegation.)