i'm working on an app that display a viewController when is rotated to the left and then when it's rotated to the right, it displays another view controller. my segue's style is modal and i'm using the cross dissolve transition. the problem is when i rotate left then quickly rotate to the right, the console sends me this warning:
Warning: Attempt to present <SSGraphRightViewController: 0x81159b0> on <UINavigationController: 0x82439a0> while a presentation is in progress!
is there a way to avoid this warning? maybe accelerating the time of transition timing?
You cannot present a view controller from a view controller that is already being presented, the console is right about that so either use a different style of segue or handle it rightly.
Hope this helps : What is the difference between Modal and Push segue in Storyboards?
I had the same problem when I was trying to present a UIViewController when another UIViewController was being presented, so all I had to do was to dismiss the existing UIViewController and then present the new one. In your case it is because of the fast transition that happens during presenting a UIViewController so first dismiss the existing one and then present a new one. It should goes something like this
- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
In the (void) completion block put your code to present the new VC, like this:
[yourVC dismissViewControllerAnimated:YES completion:^{
//present your new VC here.
}];
Hope it helps!!.
Related
I have a a VC structure like this
UIPageViewController -> detailViewController -> popoverviewcontroller
The popoverviewcontroller is dismissed using an unwind segue, bringing us back to the detailviewcontroller
Now, after the popover is done being dismissed, I would like to refresh the pages on the pagecontroller, since the action the user takes has changed the data.
I would also like to display an alert notifying the user about whether they were successful.
So I tried putting this code in the pageViewcontroller
- (IBAction) unwindFromPopup:(UIStoryboardSegue*)unwindSegue{
[self refreshPages];
UIAlertController * alertController = [UIAlertController alertControllerWithTitle:#"alert" message:#"this should appear" preferredStyle:UIAlertControllerStyleAlert];
[self presentViewController:alertController animated:YES completion:nil];
}
I tried moving the above code to the detail view controller instead, but I am getting no result from this. No error or anything, just a complete lack of alert. I put a breakpoint in the segue, and the code gets called. But no alert.
I thought of putting the code in one of the respective viewDidAppear methods, but for some reason viewDidAppear does not get called for either the pageviewcontroller or the detailview controller after I dismiss the popup.
So at this point I have no idea how to make this alert appear.
Do I need to post my full code, or is my problem apparent with the details I've included?
Thanks - based on your comment ... long ago in a distant version of iOS I performed all the possible segues and noted what gets called when and have a table of that that I based my answer on. I must admit, nowadays I get most done using the presentation controller delegate.
Anyhow, to reply to your question, when you pop or modally present a controller, the controller that is being presented will message beingPresented and beingDismissed when it is done and you might be able to use this for what you are trying to do.
When you push a controller it will message isMovingToParentViewController when shown and isMovingFromParentViewController when dismissed, again in the controller being presented.
Back to a pop ... it will message prepareForSegue in the presenting VC and viewWillAppear and viewDidAppear in the presented VC and, when dismissing, will message only viewWillDisappear and viewDidDisappear in the presented VC, thus your problem. At least it will also message beingDismissed as mentioned and if you can use that I am really glad for you.
I have one view controller MainViewController which (through buttons) allows the user to present/dismiss another InventoryViewController modally.
A third view controller CallViewcontroller is then presented programmatically from MainViewController at specific timed intervals. Before this happens, InventoryViewController is dismissed.
If InventoryViewController is never presented, CallViewController is presented without any problems.
If InventoryViewController is visible/present when CallViewController is launched, there also don't seem to be any issues.
But if InventoryViewController is presented then manually dismissed by the user, at any point, when CallViewController is presented the app partially freezes and the console throws an error -
Warning: Attempt to present <CallViewController: 0x17e31bb0> on <MainViewController: 0x18d3fc00> whose view is not in the window hierarchy!
Code to launch InventoryViewController from MainViewController:
[self performSegueWithIdentifier:#"segueToInventoryView" sender:self];
Code to dismiss InventoryViewController from a button from within MainViewController:
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
Code to dismiss InventoryViewController programmatically from MainViewController just before CallViewController is presented:
[self dismissViewControllerAnimated:NO completion:nil];
(I also use a timer to delay the method that presents CallViewController to make sure InventoryViewController has been properly dismissed)
I'm guessing that the error is saying that I can't present a modal from MainViewController because it isn't at the top of the stack, suggesting that InventoryViewController hasn't been properly dismissed.
Originally my code was written for iOS7, now the BaseSDK is iOS9 ( I am returning to this project and iOS programming generally after a year). As far as I am aware, this problem didn't occur before. So is this being caused by changes brought on by iOS8/9, or is there something else happening here?
Any help appreciated.
Found the solution, I had a flag in MainViewController for checking if InventoryViewController had been dismissed, which wasn't being set correctly. This was causing [self dismissViewControllerAnimated:NO completion:nil] to be called twice, and the second time round it was actually dismissing MainViewController, by sending the call to it's parent!
So the fact that [self dismissViewControllerAnimated:NO completion:nil] can be called on both the presenting and presented view controller is perhaps something to watch out for.
The code outlined above all seems to work fine in iOS8/9
I've been struggling to find an answer to this question. I build a stack of modals via:
[[[NavA viewControllers] objectAtIndex:0] presentViewController:NavB animated:YES completion:NULL];
[[[NavB viewControllers] objectAtIndex:0] presentViewController:NavC animated:YES completion:NULL];
When I want to dismiss the NavA and NavB modals simultaneously I call
[[[NavA viewControllers] objectAtIndex:0] dismissViewControllerAnimated:YES completion:NULL];
This works fine except there's a brief flash where you can see NavB as the full stack is dismissed.
I stepped through the debugger and it looks like before the animation begins NavC disappears instantly and NavB dismisses with animation.
Is there any way to avoid this visual artifact and have the whole stack dismiss smoothly with NavC visible for the full duration of the animation?
Edit: To clarify, I'm presenting UINavigationController rather than UIViewController because this flow is for user login and has multiple possible branches that can lead back either to the current stage e.g. NavC (LoginPage), NavB (LandingPage with login and signup buttons) or all the way back to the root, NavA (main page of the application). In the iOS documentation they present a similar design pattern with the camera where each stage presents a UINavigationController with multiple possible view controllers https://developer.apple.com/library/ios/featuredarticles/ViewControllerPGforiPhoneOS/ModalViewControllers/ModalViewControllers.html
Actually there's no way to do it by just using dismissViewControllerAnimated:completion: method no matter where you put it or how you call it (at least I couldn't, if someone knows a way - we all want to know).
HOWEVER, there's a hack you can use to achieve your desired outcome (this code should be called from "B" ViewController):
// Snapshot of "C" ViewController
UIGraphicsBeginImageContextWithOptions([UIScreen mainScreen].bounds.size, YES, 0);
UIView *snapshot = [self.presentedViewController.view snapshotViewAfterScreenUpdates:NO];
UIGraphicsEndImageContext();
// Cover the entire view of "B" (and hide navigation bar)
[self.view addSubview:snapshot];
self.navigationController.navigationBarHidden = YES;
// Dismiss "C" without animation
[self.presentedViewController dismissViewControllerAnimated:NO completion:^{
// Dismiss "B" with animation
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
}];
If you are using a Storyboard then this should be achievable using Unwind Segues. Mike Woelmer has a good set of articles about this. Basically you provide information to the Storyboard about how a view can unwind through several different views to get a to a view that's already on the stack.
However, I'm a bit confused in the same way that Jeffery Thomas is in the comments: why are you presenting a navigation controller with another navigation controller? I can understand that you might want the navigation bar to look different on different views, but you can customise that when the view is due to appear. You should think a bit about the content of the views in NavB and NavC and ask yourself whether they are supposed to be presented as modal views or whether they would be better off as part of a single navigation stack. By presenting each Navigation Controller modally you're ending up with multiple navigation stacks, not a single stack with multiple view controllers. Even if just NavB and NavC were part of the same stack it would probably remove the visual glitch you're seeing.
If you did use a single navigation controller then you can get back to a previous view controller in the navigation stack by using the method -popToViewController:animated: on UINavigationController.
If you decide that presenting NavB and NavC modally as you are currently doing is the right thing to do then you are likely to get into trouble because when you ask NavA to dismiss its view controller it will try to dismiss NavB, which to it means setting up a transition between the NavB's view and NavA's view. That's why you're seeing that transition, and not the one you want (which is between NavC's view and NavA's). One way which might work (and sounds a bit weird) is to try to present NavA from NavC, then override the transition to make it look like you're popping NavC off the stack. Once you're there you can clean things up by removing any strong references to NavB and NavC. This article from Ash Furrow will get you most of the way.
You can fake the animation to look exactly as you wish:
pop/dismiss B and C without animation
push/present C without animation
pop/dismiss C using whatever animation you wish
Yes, I struggled a bit with that title...
Here's what my setup is meant to do:
I push a viewController (resultViewController) onto the navigation stack. Immediately (literally in the next line of code) I present another viewController to the rootViewController (filterViewController).
So far, so good, and things work as expected.
The modally presented viewController contains a tableView. Selecting a cell is supposed to present another viewController in the current (filterView) controller.
[self presentViewController:filterDetailsController animated:YES completion:^(void){
}];
This is where things go awry: The above snippets is called at the correct time, but the viewController does not appear until I perform an additional tap or gesture somewhere in the filterView. This suggest that the stacking of modalView inside modalView somehow messes things up. If I change the animated parameter to NO, the viewController is presented immediately.
An obvious work-around would be to set animated to NO and perform a manual animation in the completion block. I would however love to see if anyone have come across anything similar and knows how to fix the problem.
I am creating an app using iOS 5 SDK. I managed to push views using the Storyboard's Segues, but I cannot find the proper way to pop the current view and go back to the previous one.
I am not using any navigationController (the app doesn't have any top or bottom bars).
I don't think using modal or push segue the other way would be the solution as it instantiates a new controller.
Do I have to use a custom Segue with the opposite animation and deletion of the view at the end ? Or is there a better way ?
Storyboards in iOS 5 don't provide a "no-code" way to return from a segue -- that's something you'll need to implement yourself.
If you use "push" segues (which require a navigation controller), use the navigation controller's popViewControllerAnimated: method to undo the last push segue. (Or other methods to undo more; see the UINavigationController documentation.)
If you use "modal" segues, call dismissViewControllerAnimated:completion: on the view controller which presented the current view controller (which you can get from its presentingViewController property).
Update: In iOS 6 and later there's unwind segues for going "back" in a storyboard. It's still not a no-code solution -- and it shouldn't be, because you need to be able to do things like differentiating between "Done" and "Cancel" exits from a modal view controller. But it does let you put more of the semantic flow of your app into the storyboard. Apple has a tech note that describes them in detail, and they're also covered in the video from WWDC 2012 Session 407.
You could try calling [self dismissViewControllerAnimated:YES completion:nil]; from the controller you want to dismiss (whether the controller has been pushed, or shown modally).
Here is the related documentation : UIViewController Class Reference
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.
Just to clarify.
In the class that was pushed. Simply wire up the following and the controller and view will be popped off.
[self.navigationController popViewControllerAnimated:YES];
Create Segue type "Custom" on your stroyboard. This can be from a button.
Create a new UIStoryboardSegue class named "popSegue"
In the popSegue.m file add the following;
-(void)perform{
UIViewController *sourceViewContreoller = [self sourceViewController];
[sourceViewContreoller.navigationController popViewControllerAnimated:YES];
}
-In the storyboard editor.
-Select the segue and change the Segue Class to "popSegue"
-Set the Identifier to "popSegue"
Done!
You can use the same "popSegue" class throughout your project.
Hope this helps
I'm using Xcode 5 also and here's how it's done. First, in the view code file that pushed the other, create an IBAction method in the .h file such as this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender;
Then in the .m file add this:
- (IBAction)exitToHere:(UIStoryboardPopoverSegue *)segue sender:(id)sender {
}
You can add any cleanup code you want executed in this method. Next go to your storyboard and select the pushed view. I assume you've got some kind of button on the view that the user taps to signal he's finished. Click on that button, hold down the key and drag to the the green box below the view which is the Exit. Release the mouse button but continue to hold the key. A popup will appear and your method will show in the list. Select that method. Now when the user clicks on the button, the view will pop and you'll be returned to the starting method.