"Application tried to present modally an active controller"? - objective-c

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
}

Related

ios 8 : ABPeoplePickerNavigationController dismiss on implementing people picker delegate methods

This is strange behavior noticed while accessing contact details from address book in ios 8.
My scenario is simple
Show contacts table
select a row that will invoke didSelectPerson method
in didSelectPerson method
push SecondViewController
- (void)peoplePickerNavigationController:(ABPeoplePickerNavigationController*)peoplePicker didSelectPerson:(ABRecordRef)person;
{
SecondViewController *detailVC = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
[detailVC.view setBackgroundColor: [UIColor redColor]];
// [peoplePicker.navigationController pushViewController:detailVC animated:YES];
[peoplePicker pushViewController:detailVC animated:YES];
}
but what happens is ABPeoplePickerNavigationController dismiss.
Please enlighten me on this.
I don't know the philosophy thing what happens under the hood of the "didSelectPerson" method, me was facing the same problem today. I found a simple solution to overcome this issue, i override the
"-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion" method of the "ABPeoplePickerNavigationController". Then implement it like somewhat following
bool dismissedEnabled;
-(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
if (dismissedEnabled) {
[super dismissViewControllerAnimated:flag completion:completion];
}
}
then inside the "didSelectPerson" i have done the following
viewController = [[ViewController alloc] initWithNibName:#"ViewController" bundle:[NSBundle mainBundle]];
dismissedEnabled = false;
[self presentViewController:viewController animated:YES completion:nil];
this works for me, hope you guys overcome it too :)
It automatically dismisses if you select a contact with a single email address for example.
If a contact has more than one email, you must specify a predicate that will force the ABPeoplePickerNavigationController to push a ABPersonViewController on the stack.
if ([picker respondsToSelector:#selector(setPredicateForSelectionOfPerson:)])
{
// The people picker will select a person that has exactly one email address and call peoplePickerNavigationController:didSelectPerson:,
// otherwise the people picker will present an ABPersonViewController for the user to pick one of the email addresses.
picker.predicateForSelectionOfPerson = [NSPredicate predicateWithFormat:#"emailAddresses.#count = 1"];
}
I believe the default behavior in iOS 8 is that the ABPeoplePickerNavigationController is automatically dismissed when didSelectPerson is called.
The reason that the SecondViewController is not displayed (I'm inferring that this is the problem symptom) is because you are trying to push the SecondViewController while the ABPeoplePickerNavigationController is being dismissed. This overlapping animation is a problem that the iOS core view management/animation system tries to avoid.
You may get a warning in the console when this happens.
#Ratul's solution works because it circumvents this default behavior.
In my scenario, my code sleeps a second before presenting a UIAlertController from within didSelectPerson. This is a hack that depends on the ABPeoplePickerNavigationController dismissal animation taking less than a second. For me, if this alert is not displayed, nobody would even notice this was a problem.
If you want something more robust, you may want to override viewDidAppear to handle this special case (using a flag in your presenting view controller). But this gets a bit clumsy as well.

Check orientation in AwakeFromNib – Objective-c

I want to check the orientation of the iphone in the AwakeFromNib method. This is my code:
- (void)awakeFromNib
{
orientation = [[UIApplication sharedApplication]statusBarOrientation];
if (orientation == UIInterfaceOrientationPortrait)
{
NSLog(#"orientation portrait");
}
else if (orientation == (UIInterfaceOrientationLandscapeLeft | UIInterfaceOrientationLandscapeRight))
{
NSLog(#"Orientation landscape");
}
}
The logs aren't messaged when I'm turning my iPhone, what's the problem?
Your comments on Michael Dautermann's (now-deleted) answer are contradictory. You said you're creating your view programmatically, but you also talked about “when the view is loaded”.
Either you're creating the view in code, or you're loading it from a nib/storyboard. You can't be doing both with the same view instance.
The fact that your awakeFromNib method isn't being called matches your claim that you are creating the view programmatically. But another possibility is that you are loading the view from a nib but have not correctly set the “Custom Class” of the view in the nib, so it's not actually creating an instance of your view subclass.
If you are truly creating the view programmatically, then somewhere you should have code like this:
view = [[MyView alloc] initWithFrame:someRect];
or maybe this:
view = [[MyView alloc] init];
The second case, under the covers, just calls [self initWithFrame:CGRectZero]. So either way, your view's initWithFrame: method is getting called. So you should override that method and check the interface orientation there.
If you're not creating the view in one of those ways, then edit your question to explain exactly how you are creating it.

Application tried to present modally an active controller : UIImagePickerController

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.

UIView Login screen to tabbar logic

Folks,
I'm having trouble with some navigation logic. Currently I have a simple two tabbed tabbar application. But I want to show a loginscreen in front. So that would be an UIView.
Currently the code is as follows:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
UIViewController *viewController1 = [[roosterViewController alloc] initWithNibName:#"roosterViewController" bundle:nil];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = #[viewController1, viewController2];
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
So this pushes a simple tabcontroller. Well, now I want to have a login screen. So that would be a simple UIView which pushes the tabbar controller. But I can't seem to see the logic on how to do this.
I've been trying to present a modal view controller, but the thing is: the tabbar will be loaded on the background. Since I need the username/password information to work on the tabbarview, this won't work.
My Logic would be:
delegate > load loginViewController > load tabbar controller
But, then I need to be able to "logout". So I need to destroy the tabbar controller and present the login screen.
Any thoughts on this?
You could do something like this:
You could create a protocol which your AppDelegate conforms to.
#protocol Authenticator <NSObject>
#required
- (void)authenticateWithUsername:(NSString *)username andPassword:(NSString *)password;
- (bool)authenticated;
#optional
- (void)authenticationSuccess;
- (void)authenticationFailure;
...
#interface AppDelegate : UIResponder <UIApplication, Authenticator>
#property (readonly, nonatomic, assign) bool loggedIn;
...
#implementation AppDelegate
#synthesize loggedIn = _loggedIn;
- (void)authenticateWithUsername:(NSString *)username andPassword:(NSString *)password
{
//if success
_loggedIn = YES;
//check if app responds to the optional authenticateSuccess method
//call it if it does
//else fail
//do stuff
}
- (bool)authenticated
{
if (_loggedIn != NULL) {
return _loggedIn;
}
//do other stuff
}
...
I am a bit fuzzy on proper objective-c conventions and syntax so forgive me if I have a few errors, but anyways that is some logic and pseudo-code to work off of. Tweak that to your needs.
I hope this helps.
EDIT:
I guess my answer was a bit unfinished. It seemed to to me the answer to your question was strongly connected to some kind of authentication structure. If your app is so closely connected to authentication, then why not control its flow through the authentication structure. I guess that was my point, and since all this would be conveniently and readily available in your apps delegate, you could call these methods anywhere you wanted, therefore letting your authentication logic decide which view controller to show for example.
As mentioned have the login as the rootView and if the login is successful enable the other tabbarButtons else don't enable them like this..initially set the bool to false on view load then if successful enable a tabbar button else don't.
login = TRUE;
UITabBarItem *reportit = [[[[self tabBarController]tabBar]items] objectAtIndex:2];
[reportit setEnabled:TRUE];
else {
UITabBarItem *reportit = [[[[self tabBarController]tabBar]items] objectAtIndex:2];
[reportit setEnabled:FALSE];
}
I recommend you set the login screen as root. When the login is successful, you simply change the root of the window to the tabbar controller, with a nice animation.
I would recommend that you set up the tab bar just like you have it right now, but immediately following the makeKeyAndVisible you instantiate and present the login view controller, without animation.
This way once the app has launched the user does not see the tabBarController, but only the modally presented login screen. Once login is done you simply dismiss the login view controller and beneath it appears .... drumroll the tabbarcontroller!
Expanding on Levi's answer, this is how you switch the root view controller continuously (with an animation). Just add this extension:
extension UIViewController
{
func transitionToRootViewController(viewController:UIViewController)
{
UIView.transitionWithView(self.view.window,
duration: 0.3,
options: UIViewAnimationOptions.TransitionCrossDissolve,
animations: {
window.rootViewController = viewController
},
completion: nil
)
}
}
...to the UIViewController class (that will make the method transitionToRootViewController () available to all your view controllers), and call it on the exiting view controller, passing the entering view controller (that you perhaps instantiated form a separate storybord, who knows...) as a parameter.
(The basic idea was taken from here. I simplified the code a bit)
Beware though: I tried this code to insert a tab bar controller, whose selected index (tab) contained a navigation controller. During the transition animation, the navigation controller's navigation bar "underlaps" the status bar (carrier, clock, battery level), and only after the transition animation completes, it "jumps" into its place immediately, creating a horribly distracting (and unpolished) effect.
I fixed it by adding the following code to the child view controller embedded in the navigation controller:
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
self.navigationController?.navigationBar.layer.removeAllAnimations()
// ^ THIS
}
(This fix was taken from this forum.)
I haven't checked, but the same issue might occur whenever you transition into a navigation controller (or any plain-vanilla UIViewController that happens to have a navigation bar attached), whether it is embedded in a tab bar controller or not.

Unable to create unwind segues when using custom view controller containment

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.