In my storyboard I have a NavViewController which sets the root view to ViewController1. ViewController1 then has a push segue to ViewController2 with the Identifier "socialSeg". ViewController1 has a UIImageView which I use to load the camcorder. After the user records the video and selects 'Use' i want to load the next view controller. In ViewController1.m I have the following:
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary *)info
{
NSLog(#"Performing Segue with ID");
[self performSegueWithIdentifier:#"socialSeg" sender:self];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Prepping");
}
Both NSLogs run, but nothing happens on my screen. I don't see the ViewController2, and no error messages pop up. Am I doing this correct?
Related question: Should I be running performSegueWithIdentifier on self (the current ViewController), or should they be ran on self.navigationController?
Edit: I tried to shorten my question somewhat to keep the post shorter, I realized you might need more context.
Here is my full storyboard. Basically "Video View Controller" would be ViewController1 and "Social View Controller" would be ViewController2. When I click on the "Root View Controller" button (Record Video), that segue works fine, which is why I left it out. Is there any chance the "Root View Controller" could be throwing things off?
There is nothing wrong with your performSegue code. It seems like your picker animation is still being run when you perform this Segue. Try calling performSegue in the completion block of presentViewController by modifying your present call as given below.
[imagePickerController presentViewController:YES completion:^() {
NSLog(#"Performing Segue with ID");
[self performSegueWithIdentifier:#"socialSeg" sender:self];
}];
Please make sure to replace imagePickerController with the name of your popup image picker controller.
Its not ideal solution but you can also delay execute your your performSegue to make sure your image picker animation has stopped as:
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//1.5 is number of seconds its going to wait before executing the block of code
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC), dispatch_get_current_queue(), ^{
NSLog(#"Performing Segue with ID");
[self performSegueWithIdentifier:#"socialSeg" sender:self];
});
}
NOTE: The issue is that image picker popup animation is still in progress (to close the picker after user has picked an image or video) when performSegue tries to start another animation to load next view controller. Since one animation has not finished yet therefore performSegue animation gets ignored and view controller does not load.
Here is a related question and answer: performSegueWithIdentifier not working
Related
I need your help please.
I have two different view controllers:
1. with a button
2. with an image view.
I'm trying to pass the button's image to the image view at the second view controller.
The prepareforsegue is being called but the image does not pass.
I attached my code:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"pushGuessLogo"])
{
GeneralGuess *vc = [segue destinationViewController];
[self setLogoImageView:[[UIImageView alloc] initWithImage:self.logoImageSelected.imageView.image]];
[vc setImageGuessView:self.logoImageView];
}
}
ImageGuessView is the outlet of the image view at view controller number 2.
Thank you
Someone answered this the other day but I'm too lazy to find the link. (wouldn't it be great if SO also had a Knowledge Base which questions get promoted to, rather than endless questions that do not come up in a search).
The gist is that the viewController's view hierarchy is not yet set up at the point of prepareForSegue, so your ImageGuessView is nil at that point. What you can do is define a UIImage property on your GeneralGuess class and set that. Then in viewWillAppear load that image into your ImageGuessView.
The destination is not yet loaded when prepareForSegue is called so the image view is nil and thus nothing happens what you try to set the image.
Instead you should pass the image and store it in a property on the destination view controller and later set it in the image view in the destination view controllers viewDidLoad
I know this is a hot question for iPhone programming but I can't find a good answer yet.
I've an UITabBarController based application with the middle tabBarItem overlayed by a custom button. This custom button is used for posting a new item. Like this (I take Instagram, for example).
Of course this button must be accessible in the whole application.
When you click on this button you'll can see an UIImagePickerController for choosing an image from your library or from the camera.
The problem is how to display another UIViewController after you've chosen the image.
The first method is set a boolean variable from the UIImagePickerController delegate method and override the viewDidAppear method of all view controllers for checking the boolean var and if it's TRUE, call [self.tabBarController presentModalViewController:MyController animated:YES]. But I think this one is pretty ugly.
Have you others (elegant) method?
Create a new UIImagePickerControllerDelegate object, trap user actions, and either present a modal view controller or send a message to a view controller to handle it.
Let's say ViewController A is the class where you click on the camera button starting UIImagePickerController. ViewController Ashould be the delegate of UIImagePickerController.
Then, in ViewController A class, we define the function which is called when you click on the button Use.
// This function is invoked when you click the button USE
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// Go back to the view A
[self dismissModalViewControllerAnimated:NO];
// Get Image
UIImage *image = [info objectForKey:UIImagePickerControllerOriginalImage];
// Go to view B
[self.navigationController pushViewController:showMapViewController animated:NO];
I have a Navigation Controller that is presented modally with 4 views in the stack. The final view has a done button that dismisses the modal view. When I then present the modal view again, it automatically goes to that last view instead of the first one. I added a line to pop to first view after dismissed but it adds a weird animation whether I set it to YES or NO. Maybe I'm doing it wrong?
- (void)dismissModalView
{
[self dismissModalViewControllerAnimated:YES];
[self.navigationController popToViewController:[self.navigationController.viewControllers objectAtIndex:([self.navigationController.viewControllers count] -4)] animated:YES];
}
Update:
This is the method used to present the modal view/navcontroller
- (void)showModalView
{
self.optionsNavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.navigationController presentModalViewController:self.optionsNavController animated:YES];
}
No, it looks right. If you want to retain the state of the Navigation controller then don't present it modally as it will get deallocated when you dismiss the view.
Modal views are usually used to present information that only needs to be shown briefly without maintaining the state of the view (i.e. about page, login page, settings page, etc).
Excuse me if I'm oversimplifying your issue, but if your need is to pop straight back to the first view controller, perhaps you could give the popToRootViewControllerAnimated: method a try instead of popToViewController:.
I'm not sure what the problem with the dismiss code you posted is, but the following should work:
- (void)showModalView
{
self.optionsNavController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self.optionsNavController popToRootViewControllerAnimated:NO];
[self.navigationController presentModalViewController:self.optionsNavController animated:YES];
}
Also, viewDidLoad is intended to only be called once every time the view is loaded, viewWillAppear a
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];
}
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.