viewcontroller won't dismiss - iphone

I have the following code
- (void) viewWillAppear:(BOOL)animated
{
NSLog(#"dismiss view");
[self dismissModalViewControllerAnimated:YES];
}
This prints dismiss view but won't execute the view dismissal code.
Can a view be dismissed in code without a trigger from an IBAction?
Why wouldn't this be executing?

You would probably have better luck putting the code into viewDidAppear rather than viewWillAppear. The latter tends to be called right at the start of any animation such as a modal beginning to slide off the screen; the former tends to be called when that animation has completely finished. Note that even if this strategy works, you may end up with a weird effect whereby two modals are seen to slide off, one after the other; I presume you just want one sliding off effect.
What happens if you dismiss the "lower down" modal dialog (the one first pushed), and don't bother dismissing the one that is topmost?
Also consider paying attention to the animated argument when dismissing your modal view controller. Different combinations of animated or not can have different effects when you have problems like yours.
If you post a more complete code sample we can give a better answer!

try with a little delay on the second dismiss.
- (void) viewWillAppear:(BOOL)animated
{
NSLog(#"dismiss view");
[self performSelector:#selector(delayedDismiss) withObject:nil afterDelay:0.5];
}
-(void)delayedDismiss{
[self dismissModalViewControllerAnimated:YES];
}

Related

Using a push segue in StoryBoard, how to change to previous view

I am creating an app where if something occurs, it switches back to the previous screen.
There is practically no real code in the app, and when it does it has nothing to do with the previous view, but just in case, here is the entire .m: http://pastie.org/4111440
[self.navigationController popViewControllerAnimated:YES];
This^^ gives me EXC_BAD_ACCESS with "nested pop animation can result in corrupted navigation bar"
If you need any more info leave a comment. Thank you!
If you really want to help, I will message you a link to the entire .xcodepproj or whatever the extention is for the entire product.
I think the message says it all: "nested pop animation can result in corrupted navigation bar".
It looks like you are doing a pop with animation while in the middle of doing a pop with animation. I would recommend you figure out a different way to structure the code so you don't have the double pop.
If that's not possible, you may want to do a delayed pop to keep them from overlapping.
- (void)doPop
{
[self.navigationController popViewControllerAnimated:YES];
}
…
// At the previous pop point.
[self performSelector:#selector(doPop) withObject:nil afterDelay:0.5];

presentModalViewController does not work if called too early

If I dismiss the the modal VC and present it (or another one) again in less than a certain amount of time, it does not appear.
Am I missing something?
Yes, you can't do that. I'm assuming what you did is something like this:
[self dismissModalViewControllerAnimated:YES];
[self presentModalViewController:myNewController animated:YES];
This doesn't work. I don't know exactly why, but it is related to the animations I believe. Your options are to either dismiss the first one without animation, or else wait to present the new one in viewDidAppear of the parent, (or possibly viewDidDisappear for the previous modal view, not sure if that works though).
You cant dismiss two modal view controllers right after another, aside from what the other poster suggested, you can wait a small amount of time before dismissing the other modal view, for example
[self performSelector:#selector(method) withObject:nil afterDelay:.5];
where the method called just dismisses your other modal view.
The snippet of code performs the selector after .5 seconds and can be used when having animation timing problems like the one you describe, to seperate the call times in order for them to execute properly.

willAnimateRotationToInterfaceOrientation not called on popViewControllerAnimated

In the MainViewController I have to adjust some images on orientation change. Easy - just add the code to the willAnimateRotationToInterfaceOrientation: callback
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
NSLog(#"orientation: %#", name(orientation));
..
Works as expected. While in Portrait I push another orientation aware UIViewController onto the UINavigationController. I rotate to Landscape and all is good.
Then I go back to the MainViewController by calling
[self.navigationController popViewControllerAnimated:YES];
While the subviews of the MainViewController get adjusted to Landscape according to the autoresize masks, willAnimateRotationToInterfaceOrientation: does not get called!
I would have expected willAnimateRotationToInterfaceOrientation: to be either also called when not on top of navigation controller stack or at least get called when returning to the top of the stack.
I assume I could manually call willAnimateRotationToInterfaceOrientation: before the pop but that feels wrong.
What am I missing?
This is expected behavior as I see it. If the UIVC is not on the top of the stack, then willAnimateRotationToInterfaceOrientation shouldn't be called as no rotation is being animated at that time. The way I've been handling this in the app I am working on now is similar to the above poster. Any UIVC that supports all orientations gets a new method
- (void) updateLayoutForNewOrientation: (UIInterfaceOrientation) orientation;
This method is called from two places:
-(void) viewWillAppear: (BOOL) animated {
[super viewWillAppear: animated];
[self updateLayoutForNewOrientation: self.interfaceOrientation];
}
-(void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation duration: (NSTimeInterval) duration {
[self updateLayoutForNewOrientation: interfaceOrientation];
}
The new method is simple:
- (void) updateLayoutForNewOrientation: (UIInterfaceOrientation) orientation {
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Do some stuff
} else {
// Do some other stuff
}
}
Additionally, if you were worried about the code running when its not actually needed, you could track the orientation the device was in when the new UIVC was pushed on to the stack via an instance variable set in viewDidDisappear and consult it to decide if you want to "Do the stuff"
Isn’t this the correct behaviour? Because the animation callback should only be called before the animated orientation change. When you pop the topmost view, you see the pop animation and then the already rotated view underneath. There’s no rotation animation and therefore no callback. (But you should certainly receive willRotateToInterfaceOrientation:duration:.)
P.S. I have a sample Xcode project for interface orientation experiments on GitHub, you might be interested.
since there is no rotation taking place when you come back to the main view the willAnimateRotationToInterfaceOrientation will not get called. However, if you are looking to do some stuff based on the changed orientation, you could add the following code to your viewWillAppear method of the main viewcontroller
- (void)viewWillAppear:(BOOL)animated {
UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if(UIInterfaceOrientationIsPortrait(statusBarOrientation)){
NSLog(#"Portrait");
....your code....
}
else {
NSLog(#"Landscape");
....your code....
}
}
I am with Scott on this one - I think it is the behaviour which has to be expected.
See also the View Controller Programming Guide where it states that only the frontmost controller will have the methods called. (Search for 'frontmost' to find the two relevant locations in the document.)
I have also experienced this behavior, and I think it's because of performance and architectural reasons; AFAIK this method is called inside a UIView animation context (which allows you to change frames and centers, and have a nice animation while everything rotates) and when your view controller is not visible, either UIKit avoids executing this method, or simply it's impossible to execute it, because the view is not visible (and probably you can't animate what you can see).
Again, this is pure speculation :)

Getting "Using two-stage rotation animation" warning with UIImagePickerController

I wrote simple code to test UIImagePickerController:
#implementation ProfileEditViewController
- (void)viewDidLoad {
[super viewDidLoad];
photoTaker_ = [[UIImagePickerController alloc] init];
photoTaker_.delegate = self;
photoTaker_.sourceType = UIImagePickerControllerSourceTypeCamera;
photoTaker_.showsCameraControls = NO;
}
- (void)viewDidAppear: (BOOL)animated {
[self presentModalViewController: photoTaker_ animated: NO];
}
#end
And I'm getting strange warnings like the following:
2010-05-20 17:53:13.838 TestProj[2814:307] Using two-stage rotation animation. To use the smoother single-stage animation, this application must remove two-stage method implementations.
2010-05-20 17:53:13.849 TestProj[2814:307] Using two-stage rotation animation is not supported when rotating more than one view controller or view controllers not the window delegate
Got any idea what this is about? Thanks a lot in advance!
This message will appear if you are presenting the UIImagePickerController within another UIViewController. Because it isn't pushed like a UINavigationController stack, there is confusion at the UIWindow level. I don't know if the warning is a problem, but to eliminate the warning you can do the following:
// self = a UIViewController
//
- (void) showCamera
{
cameraView = [[UIImagePickerController alloc] init];
[[[UIApplication sharedApplication] keyWindow] setRootViewController:cameraView];
[self presentModalViewController:cameraView animated:NO];
}
- (void) removeCamera
{
[[[UIApplication sharedApplication] keyWindow] setRootViewController:self];
[self dismissModalViewControllerAnimated:NO];
[cameraView release];
}
Perhaps you are adding the root UIViewController's view as a subview of the window instead of assigning the view controller to the window's rootController property?
IT ALL FALLS BACK ON THE UI
This warning can be implemented for several different objects: Pickers, keyboard, etc.
I have found that it is related to the UI taking two steps to complete a transition or other animation. Or for any instance where the UI is trying to finish one thing and is being asked to execute another before it has finished. (therefore it covers a wide range of possible triggers)
I have seen the warning appear on 4.0 & 4.2. In my case I was working with rotating the device and catching whether the keyboard was still up-(i.e. text field was still first responder). If so, the keyboard needed to stay up for between the views, but this presented other complications with other views.
Therefore, I implemented a BOOL tracker to keep track if keybaordIsPresent, and if so I was {textfield resignFirstResponder]; when detecting the orientation change and the reset the textfield to becomeFristResponder after the transition that was wrapped in an Animation Block. My BOOL tracker worked better, I still use the NSNotifications for the Keyboard, but there were overlaps of notifications during rotations because the keyboard was being dismissed without requesting such. The BOOL is set to NO on Load, and when the [textfield resignFirstResponder]; is implemented. *not when "-(void)keyboardWillhide is trigger by the NSNotifications, which gives me both working triggers that never conflict. The BOOL is set to YES, only when the user touches textfield which automatically triggers becomeFirstResponder.
I removed the warning by taking the [textfild resignFirstResponder]; out of the
-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}
}
and placing it back at the top of the code for the:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (keyboardIsPresent == YES) {
[self.entryTextField resignFirstResponder];
}
//Determine Which Orientation is Present:
if((fromInterfaceOrientation == UIInterfaceOrientationPortrait) || (fromInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)){
//LANDSCAPE VIEW:
[self configureLandscapeView];
}else {
//PORTRAIT VIEW:
[self configurePortraitView];
}
}
**Even though I had no code inside the -(void)willAnimatFirstHalfOfRotationToInterface:, the warning was still popping up. I think the warning was still popping up because the compiler still has to attempt the method while it is trying to execute the first animation and therefore gets the double animation call or overlap of resources. It doesn't know that there is no executable code with the method until after it runs through it. And by that time it already set aside resource in preparation for handling possible actions within the method.
**To ellimiate the warning I had to remove or nil out the code for the willAnimateFirstHalfOfRotation, so that the compiler does not have to even check to see if there is a possible 2nd animation or action that may need to be executed at the same time.
/*-(void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
//if (keyboardIsPresent == YES) {[self.entryTextField resignFirstResponder];}}*/
After the transition is completed, within the original animation block I check to see if the "keyboardIsPresent" was YES prior to the rotation, and if so, I resign the First Responder once again. I use setAnimationDuration:0.3which comes out pretty clean and not jumpy.
Well, you are presenting UIImagePickerController modally inside viewDidAppear of ProfileEditViewController.
Think about this. That means when ProfileEditViewController view appears, the UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and UIImagePickerController appears, say later you dismiss UIImagePickerController and it goes back to ProfileEditViewController, then viewDidAppear is called again and.... you get the idea.
That warning is rather cryptic though, not sure if that is what it's trying to tell you. I would suggest making a button somewhere on the ProfileEditViewController that calls presentModalViewController and also make sure you have a way to dismiss the UIImagePickerController (I've never used it not sure if it has one automatically).
You may be trying to present two modal view controllers at the same time, and they are fighting for animation resources.
1) There is rarely any UI reason to do this. You could instead just go directly to the second view controller (the image picker); and, after dismissing it, then present the first view or view controller.
2) If you do want two stacked view controllers or a view controller on top of a view, then set a timer in viewDidAppear to present the second view controller after the first one has finished it's animation. (You could display a dummy png image of a blank picker in the first one to prevent too much display flashing until the second view controller goes live.)
EDIT - Added a random code example:
I might try substituting this as an experiment:
- (void)foo {
[self presentModalViewController: photoTaker_ animated: NO];
}
- (void)viewDidAppear: (BOOL)animated {
NSTimer *bar = [ NSTimer scheduledTimerWithTimeInterval: (2.0f)
target: self
selector: #selector(foo)
userInfo: nil
repeats:NO ];
}
A shorter time delay may work as well.
I just had the same problem. In my case was a silly mistake that I'm putting here just in case anyone else falls into that same issue.
In my tabbed app I remove one of the original ViewControllers and added a new one with Storyboard to create a "Settings" section.
This new VC had to be a table view VC and even I designed, compiled and run it without a problem, when I changed the orientation of the app I kept getting this “Using two-stage rotation animation” error.
My problem was that I forgot to change in the original .h file interface "UIViewController" for "UITableViewController".
Once this was done I changed on the Storyboard identity badge the class from the general value to my SettingsViewController and that was the end of it.
I hope it can help someone else. It took me a while to get to bottom of this.
Cheers,
I think the warning here is about Core Animation performance. As a test, I loaded the image picker without any action sheet or other animations going on and the warnings are still there. I think these are warnings coming from the image picker class itself and not from any misuse of the API.

Presenting a modal view controller only after another one has been dismissed

I can't just do
[myViewController dismissModalViewControllerAnimated:YES];
[myViewController presentModalViewController:nextModalViewController animated:YES];
one after the other, because then the two animation blocks try to affect the same references simultaneously and things break badly.
So what I need to do is make the latter call only after the first animation has finished. But there's no UIViewControllerDelegate method like didDismissModalViewController. What should I do?
It's a bit hacky (ok, maybe a lot hacky), but you could simply present the second one after a fixed delay:
[myViewController performSelector:#selector(showSecondModalView) withObject:nil afterDelay:0.5];
(or whatever the animation duration turns out to be).
What's wrong with just subclassing the view controller (if you haven't already) and doing this:
- (void) viewDidDisappear: (BOOL) animated
{
[super viewDidDisappear: animated];
[myViewController presentModalViewController:nextModalViewController animated:YES];
}
I'm not sure how you are handling your references to the view controllers, but the point I'm trying to make is just catch the viewDidDisappear for the model view controller that is sliding off.