I'm struggle at this for 2 days and believe that this is the moment I should call for help. After I search SOF for a while, none of any answer could solve my problem. Here are my application ...
In the application,
Device is iPad, iOS 6
RootViewController is NavigationController
TopViewController is TabBarController
In this TabBarController, I present a popoverController from right bar button of navigation bar
In presenting popover there is a button to allow user to pick image from by taking new one or pick from existing.
To pick new one, I presentViewController UIImagePickerController to allow user to take photo with divice camera. presentModalViewController:animated: if iOS < 6, and presentViewController:animated:completion: for iOS > 6
I also hide Status Bar before presentation
To select from existing photo, I do presentPopoverFromBarButtonItem:permitArrowDirections:animated:
PopoverViewController also referencing by A TabBarController
Here is the issue
Present UIImagePickerController will always failed if user try to pick new one first with exception "Application tried to present modally an active controller <[name of view controller that try to present]>"
BUT, if user try to pick image from camera roll for once and then try to take new one again, it won't fail.
Here are what I tried
present from RootViewController
present from TopViewController (TabBarController)
present from popoverViewController itself
present from a tab of TabBarController
hide popoverViewController before presentation
resignFirstResponder from a textField in popoverViewController
Here is the current code I'm using
// PopoverViewController, presented by a tab in TabBarController
- (IBAction)takePhoto:(id)sender {
[self.delegate takePhotoWithDeviceCamera];
}
// A Tab in TabBarController, delegate of popoverViewController
- (void)takePhotoWithCamera {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
if ([UIDevice OSVersion] < 6.0) {
[self presentModalViewController:cameraPicker animated:YES];
} else {
[self presentViewController:cameraPicker animated:YES completion:nil];
}
}
Any idea what would cause this error? Any suggestion are welcome. Thank you.
Got the same trouble than you and finally got the solution based on #CainaSouza's answer. I've been working with Xamarin.iOS so I'll make my answer in C#, but it can be easily translated to Objective-C.
I'm using the same code as #CainaSouza to call the controller:
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController (customController, true, null);
And then I add the following code to my custom RootViewController:
public override void PresentViewController (UIViewController viewControllerToPresent, bool animated, Action completionHandler)
{
if (PresentedViewController != viewControllerToPresent) {
base.PresentViewController (viewControllerToPresent, animated, completionHandler);
}
}
The trick is to check if you haven't presented that UIViewController before.
I know it's an old question, but hope it will help someone. :)
Present the imagePicker controller in a popoverController(in case of iPad). This will not give you that error.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:picker];
[popover presentPopoverFromRect:self.selectedImageView.bounds inView:self.selectedImageView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
self.popOver = popover;
}
else {
[self presentModalViewController:picker animated:YES];
}
Best Regards.
Have you tried to present it like this?
[self.view.window.rootViewController presentModalViewController:cameraPicker animated:YES];
My guess is that the cameraPicker instance is not correctly allocated/released. Try creating the cameraPicker inside your - (void)takePhotoWithCamera method rather than relying on a previously created instance. You'll get a handle to the picker instance in the callback methods...
I had the same problem - I wanted users to take photos using a full screen view (i.e. call presentViewController and pass UIImagePickerController controller instance) and select existing photos from a popover (I associated it with a popover using initWithContentViewController). I reused the same instance of UIImagePickerController for both camera and popover and it threw the same exception if I tried to run a camera before opening a popover.
I turned out to cause a problem and my solution was simply to have two instances of UIImagePickerController - one for camera (which I presented from a main view) and another one for popover. It works so far. :-)
Not sure if it is still actual for the original poster, but hopefully it will help anyone else who encounter this discussion.
Related
I have looked around but haven't found a satisfying answer. My problem is that whenever I call popToRootViewControllerAnimated:(BOOL) it is not doing anything. When I NSLog it, it logs (null).
Let me back up a bit here. I have a table view controller that has a list of things, at the navigation bar up top there is an option to add and that takes me to a new view controller with a segue "Present as PopOver" which gets rid of the principal or main navigation bar. So I made one manually and added 2 bar button items "Cancel" and "Add". When "Cancel" is tapped, it should take the user back to the table view controller and discard changes, when "Add" button is tapped, it should also take user back to the previous table view controller with the changes. But it's not doing anything.
Here is my code.
- (IBAction)cancelButton:(UIBarButtonItem *)sender {
UINavigationController * navigationController = self.navigationController;
NSLog(#"%#", navigationController);
NSLog(#"cancel tapped though");
ListingTableViewController *rootController = [[ListingTableViewController alloc] init];
[navigationController popToRootViewControllerAnimated:NO];
[navigationController pushViewController:rootController animated:YES];
}
As far as the segue, this view controller is not connected to anything, or should I connect it? This is a noobish question indeed. Here is my xcode screenshot.
Check this link for the screenshot of the storyboard
http://i.stack.imgur.com/lqnCF.png
You must call
- (IBAction)cancelButton:(UIBarButtonItem *)sender {
NSLog(#"cancel tapped though");
[self dismissViewControllerAnimated:YES completion:nil];
}
instead of popToRootViewControllerAnimated because your VC presented and not pushed!
When presenting a view, you are not pushing it in your navigation controller, but having it presented. To dismiss it, try using [self.presentingViewController dismissViewControllerAnimated:NO completion:nil].
I'm attempting to convert our application to storyboards and have hit what I believe is a bug in the handling of unwind segues when dealing with custom container controllers. We have a view controller which displays another and uses the view controller containment api to do this, I wire up the segue in IB then select a custom class for the implementation. The perform method looks something like this:
-(void) perform {
UIViewController *container = [self sourceViewController];
UIViewController *child = [self destinationViewController];
[container addChildViewController:child];
[container.view addSubview:child.view];
child.view.center = container.view.center;
[UIView transitionWithView:container.view
duration:0.35
options:UIViewAnimationOptionCurveEaseInOut
animations:^{
child.view.alpha = 1;
} completion:^(BOOL finished) {
[child didMoveToParentViewController:container];
}];
}
That works perfectly, however I can't make it perform the unwind segue back to the container controller. I override viewControllerForUnwindSegueAction: fromViewController: withSender: and ensure that it's returning the correct value:
-(UIViewController *) viewControllerForUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
id default = [super viewControllerForUnwindSegueAction:action fromViewController:fromViewController withSender:sender];
NSAssert1(default == self, #"Expected the default view controller to be self but was %#", default);
return default;
}
I can also confirm that canPerformUnwindSegueAction:fromViewController:withSender is being called and doing the right thing, but to be sure I overrode it to return YES
-(BOOL) canPerformUnwindSegueAction:(SEL)action fromViewController:(UIViewController *)fromViewController withSender:(id)sender {
return YES;
}
The next step I would expect to happen is for segueForUnwindingToViewController:fromViewController:identifier: to be called, however it never is. Instead the application crashes with an NSInternalInconsistencyException.
2012-10-01 10:56:33.627 UnwindSegues[12770:c07] *** Assertion failure in -[UIStoryboardUnwindSegueTemplate _perform:], /SourceCache/UIKit_Sim/UIKit-2372/UIStoryboardUnwindSegueTemplate.m:78
2012-10-01 10:56:33.628 UnwindSegues[12770:c07] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Could not find a view controller to execute unwinding for <USCustomContainerViewController: 0x75949a0>'
*** First throw call stack:
(0x1c8e012 0x10cbe7e 0x1c8de78 0xb61f35 0x581711 0x45ab54 0x10df705 0x16920 0x168b8 0xd7671 0xd7bcf 0xd6d38 0x4633f 0x46552 0x243aa 0x15cf8 0x1be9df9 0x1be9ad0 0x1c03bf5 0x1c03962 0x1c34bb6 0x1c33f44 0x1c33e1b 0x1be87e3 0x1be8668 0x1365c 0x1e7d 0x1da5)
libc++abi.dylib: terminate called throwing an exception
Has anyone successfully used unwind segues combined with the view controller containment APIs? Any idea what step I'm missing? I've uploaded a demo project to github which shows the issue in the simplest demonstration project I could come up with.
The problem in your example is that there's no there there. It's too simple. First, you create your container view controller in a rather odd way (you don't use the new IB "container view" which is there to help you do this). Second, you've got nothing to unwind: nothing was pushed or presented on top of anything.
I have a working example showing that canPerformUnwindSegueAction really is consulted up the parent chain, and that viewControllerForUnwindSegueAction and segueForUnwindingToViewController are called and effective, if present in the right place. See:
https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch19p640presentedViewControllerStoryboard2
I have now also created a fork of your original example on github, correcting it so that it illustrates these features:
https://github.com/mattneub/UnwindSegues
It isn't really a situation where "unwind" is needed, but it does show how "unwind" can be used when a custom container view controller is involved.
This seems to be a bug – I would also expect unwind segues to work as you implemented.
The workaround that I used is explicitly dismissing the presented view controller in the IBAction method:
- (UIStoryboardSegue *)segueForUnwindingToViewController:(UIViewController *)toViewController
fromViewController:(UIViewController *)fromViewController
identifier:(NSString *)identifier
{
return [[UIStoryboardSegue alloc] initWithIdentifier:identifier
source:fromViewController
destination:toViewController];
}
- (IBAction)unwind:(UIStoryboardSegue*)segue
{
UIViewController *vc = segue.sourceViewController;
[vc willMoveToParentViewController:nil];
if ([vc respondsToSelector:#selector(beginAppearanceTransition:animated:)]) {
[vc beginAppearanceTransition:NO animated:YES]; // iOS 6
}
UIView *modal = vc.view;
UIView *target = [[segue destinationViewController] view];
[UIView animateWithDuration:duration animations:^{
modal.frame = CGRectMake(0, target.bounds.size.height, modal.frame.size.width, modal.frame.size.height);
} completion:^(BOOL finished) {
[modal removeFromSuperview];
[vc removeFromParentViewController];
if ([vc respondsToSelector:#selector(endAppearanceTransition)]) {
[vc endAppearanceTransition];
}
}];
}
Brief history before the answer: I just ran into the same exact error message when trying to use multiple Container Views on one iPad screen in iOS 6 and calling unwind segues from code. At first I thought this may be a problem because my segue was created using Storyboards by CTRL-dragging from File Owner to Exit instead of from some UI control to Exit, but I got same results when I put test Close buttons on each VC and had them trigger the unwind segues. I realized that I'm trying to unwind an embed segue, not a modal/push/popup segue, so it makes sense that it fails to do it. After all, if the unwind segue succeeds and the view controller is unloaded from a Container View, iOS 6 thinks there'll just be an empty space on the screen in that spot. (In my case, I have another container view taking up screen real estate that's shown behind the container view which I'm trying to unload, but iOS doesn't know that since the two aren't connected in any way.)
Answer: this led me to realize that you can only unwind modal, push, or popover segues, be it within the main window or as part of a Navigation/Tab Controller. This is b/c iOS then knows that there was a previous VC responsible for the whole screen and it's safe to go back to it. So, in your case, I'd look into a way to tell iOS that your child container view is connected to your parent container view in a way that makes it safe to dismiss the child container view. For example, perform a modal/push/popover segue when displaying the child container view, or wrap both into a custom UINavigationController class (I assume you don't want the navigation bar, that's why custom class).
Sorry I can't give exact code, but this is the best I got to so far and I hope it's helpful.
Looks like this bug is fixed in iOS9.
I've used this code to force an orientation change back to portrait when the user is finished watching the video (it allows viewing in landscape mode), before popping the video view controller off the navigation controller:
//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:NO];
//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[[UIViewController alloc] init] autorelease];
[self presentModalViewController:mVC animated:NO];
[self dismissModalViewControllerAnimated:NO];
[[UIApplication sharedApplication] setStatusBarHidden:NO withAnimation:UIStatusBarAnimationNone];
This worked perfectly until iOS 5.1.1. I've even tried to use the new present/dismiss methods after reading in another post that those should be used now:
[self presentViewController:mVC animated:NO completion:NULL];
[self dismissViewControllerAnimated:NO completion:NULL];
The problem is it doesn't work at all. After I rotated the video viewer to landscape and then pop it, my settings view (table view controller) comes back, but also in landscape mode.
I've even tried the tip from Here
"The setStatusBarOrientation:animated: method is not deprecated outright. However it now works only if the supportedInterfaceOrientations method of the topmost full screen view controller returns 0. This puts the responsibility of ensuring that the status bar orientation is consistent into the hands of the caller."
So I've experimented with setting a flag to force supportedInterfaceOrientations to return 0 (before calling the first code block above) but it doesn't work either.
Does anybody have a solution for this?
Thanks for your time and effort.
setStatusBarOrientation method has changed behaviour a bit. According to Apple documentation:
The setStatusBarOrientation:animated: method is not deprecated
outright. It now works only if the supportedInterfaceOrientations
method of the top-most full-screen view controller returns 0
Your root view controller should answer false to the method shouldAutorotate in order that your app responds to setStatusBarOrientation:animated
From Apple Documentation: "if your application has rotatable window content, however, you should not arbitrarily set status-bar orientation using this method"
To understand that, put a breakpoint in the shouldAutorotate method and you will see that it is called juste after setting the status bar orientation.
Here is how I fixed.
https://stackoverflow.com/a/14530123/1901733
The current question is linked with the question from the url above.
The statusBarOrientation is a real problem in ios6.
I need a little help on a problem with navigation controllers.
I have a navigationController with 4 ViewControllers pushed. The last vc I push presents a further ViewController modally. The modal ViewController presents an ActionSheet. Depending on the user's answer I either dismiss the modal ViewController only or I want to go back to the root ViewController.
In the ViewController presented modally I have:
- (void) dismissGameReport
{
[[self delegate] GameReportModalWillBeDismissed:modalToPopToRoot];
}
In the last ViewController pushed onto the navigationController stack I have:
- (void)GameReportModalWillBeDismissed: (BOOL)popToRoot;
{
if (popToRoot)
{
[self.navigationController popToRootViewControllerAnimated:NO];
}
else
{
[self dismissModalViewControllerAnimated:YES];
}
}
Dismissing the modal view controller works fine.
However,
[self.navigationController popToRootViewControllerAnimated:NO];
does not cause the root ViewController to display its views. Adding some log info I see that after the message to self.navigationController the stack is correctly popped but execution continues sequentially. The screen still shows the view of the modal ViewController.
As a workaround I tried always dismissing the modal view controller and in the ViewWillAppear method have the popToRootAnimated message. No difference. Still the stack of controllers is popped but the screen continues showing my modal view controller's view and execution continues sequentially.
Could someone help me please?
I like these deceptive questions. It seems very simple, until you try to do it.
What I found was that basically you do need to dismiss that modal view controller, but if you try to pop from the navigation controller on the next line things get mixed up. You must ensure the dismiss is complete before attempting the pop. In iOS 5 you can use dismissViewControllerAnimated:completion: like so.
-(void)GameReportModalWillBeDismissed:(BOOL)popToRoot{
if (popToRoot){
[self dismissViewControllerAnimated:YES completion:^{
[self.navigationController popToRootViewControllerAnimated:YES];
}];
}
else{
[self dismissModalViewControllerAnimated:YES];
}
}
But I see you have 4.0 in your question tags. The solution I found for <iOS 5 is far less pretty but should still work, and it sounds like you were already on the trail. You want viewDidAppear: not viewWillAppear:. My solution here involves an ivar, lets say:
BOOL shouldPopToRootOnAppear;
And then your GameReportModalWillBeDismissed: would look something like this:
-(void)GameReportModalWillBeDismissed:(BOOL)popToRoot{
shouldPopToRootOnAppear = popToRoot;
[self dismissModalViewControllerAnimated:YES];
}
And your viewDidAppear: would look like this...
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
if (shouldPopToRootOnAppear){
[self.navigationController popToRootViewControllerAnimated:YES];
return;
}
// Normal viewDidAppear: stuff here
}
I just came across a crash showing a NSInvalidArgumentException with this message on an app which wasn't doing this before.
Application tried to present modally an active controller
UITabBarController: 0x83d7f00.
I have a UITabBarController which I create in the AppDelegate and give it the array of UIViewControllers.
One of them I want to present modally when tapped on it. I did that by implementing the delegate method
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
If that view controller is of the class of the one I want to present modally, I return NO and do
[tabBarController presentModalViewController:viewController animated:YES];
And now I'm getting that error, which seems to mean that you can't present modally a view controller that is active somewhere else (in the tabbar...)
I should say I'm on XCode 4.2 Developer Preview 7, so this is iOS 5 (I know about the NDA, but I think I'm not giving any forbidden details). I currently don't have an XCode installation to test if this crashes compiling against the iOS4 SDK, but I'm almost entirely sure it doesn't.
I only wanted to ask if anyone has experienced this issue or has any suggestion
Assume you have three view controllers instantiated like so:
UIViewController* vc1 = [[UIViewController alloc] init];
UIViewController* vc2 = [[UIViewController alloc] init];
UIViewController* vc3 = [[UIViewController alloc] init];
You have added them to a tab bar like this:
UITabBarController* tabBarController = [[UITabBarController alloc] init];
[tabBarController setViewControllers:[NSArray arrayWithObjects:vc1, vc2, vc3, nil]];
Now you are trying to do something like this:
[tabBarController presentModalViewController:vc3];
This will give you an error because that Tab Bar Controller has a death grip on the view controller that you gave it. You can either not add it to the array of view controllers on the tab bar, or you can not present it modally.
Apple expects you to treat their UI elements in a certain way. This is probably buried in the Human Interface Guidelines somewhere as a "don't do this because we aren't expecting you to ever want to do this".
I have the same problem. I try to present view controller just after dismissing.
[self dismissModalViewControllerAnimated:YES];
When I try to do it without animation it works perfectly so the problem is that controller is still alive. I think that the best solution is to use dismissViewControllerAnimated:completion: for iOS5
In my case i was trying to present the viewController (i have the reference of the viewController in the TabBarViewController) from different view controllers and it was crashing with the above message.
In that case to avoid presenting you can use
viewController.isBeingPresented
!viewController.isBeingPresented {
// Present your ViewController only if its not present to the user currently.
}
Might help someone.
The same problem error happened to me when I tried to present a child view controller instead of its UINavigationViewController parent
I had same problem.I solve it. You can try This code:
[tabBarController setSelectedIndex:1];
[self dismissModalViewControllerAnimated:YES];
For React Native Developer - Problem might not be in AppDelegate Or main.m if app has been successfully build and is running and will crash after splash or perhaps the error screen
Issue might be due to use of fonts/resources that is not available with xcode and not properly configured.. You can find out the error by commenting certain portion starting from App.js and drilling inside the navigation/screens and commenting the components till you find the component that is generating the error....
In my case the resource of fontFamily was making an issue which was used right after splash in walkthrough screen
<Text style={{fontFamily: Fonts.roboto}}>ABC</Text>
Here font roboto wasnot configured properly. Wasted entire days just debugging the error hope its helps you
In my case, I was presenting the rootViewController of an UINavigationController when I was supposed to present the UINavigationController itself.
Just remove
[tabBarController presentModalViewController:viewController animated:YES];
and keep
[self dismissModalViewControllerAnimated:YES];
Instead of using:
self.present(viewControllerToPresent: UIViewController, animated: Bool, completion: (() -> Void)?)
you can use:
self.navigationController?.pushViewController(viewController: UIViewController, animated: Bool)
This is my way which supporting multiple Windows(from a single APP) on the iPad and nested modal present.
import UIKit
///✅Use this public method
public func SheetViewController(ViewController:UIViewController) {
for i in returnAvailableViewControllers().shuffled() {
if i.presentedViewController == nil && !ViewController.isViewLoaded {i.present(ViewController, animated: true, completion: {})}
}
}
///Returns all possible ViewControllers
private func returnAvailableViewControllers() -> [UIViewController] {
let 场景 = UIApplication.shared.connectedScenes
var 存储VC : [UIViewController] = []
for i in 场景 {
if i.activationState == .foregroundActive {
//⭐️Set up “foregroundActive” to give the user more control
var 视图控制器 = (i.delegate as? UIWindowSceneDelegate)?.window??.rootViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
}
var 结束没 = true
while 结束没 {
//🌟Enumerate all child ViewController
视图控制器 = 视图控制器?.presentedViewController
if 视图控制器 != nil {
存储VC.append(视图控制器!)
} else {
结束没.toggle()
}
}
}
}
return 存储VC
}