Popping to a controller and calling the reenteringViewController method show below after view will appear ignores the preferredStatusBarUpdateAnimation set in the view controller and instead uses a Slide Animation (which was used previously in the old controller though is set to fade before popping to the current controller).
-(void)reenteringViewController{
[UIView animateWithDuration:0.7 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^
{
if ([self respondsToSelector:#selector(setNeedsStatusBarAppearanceUpdate)]) {
[self prefersStatusBarHidden];
[self performSelector:#selector(setNeedsStatusBarAppearanceUpdate) withObject:nil];
}
}
completion:^(BOOL finished){
}];
}
- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation{
return UIStatusBarAnimationFade;
}
-(BOOL)prefersStatusBarHidden{
return NO;
}
Has anyone experienced this before and if so if they found a solution? I've spent hours pouring over the code but it's very simple and all seems to be calling things correctly but just doesn't use the correct animation. Seems like it could be an iOS7 bug.
I appreciate your help in advance.
Related
Now, this question have partially been asking alot, but none actually considering how (or when) the messages -viewWillDisappear & -viewDidDisappear are being sent. Almost every example use the following design:
[UIView animateWithDuration:0.5
delay:1.0
options: UIViewAnimationCurveEaseOut
animations:^{
yourView.alpha = 0;
}completion:^(BOOL finished){
[yourView removeFromSuperview]; // Called on complete
}];
The problem with this is that these messages will both be sent when de animation ends!
Now, -addSubview can be animated (if put inside the animations-block) which will send the corresponding messages (-viewWillAppear & -viewDidAppear) with correct timedifference. So naturally one would place -removeFromSuperview inside the animations-block. This WILL send the messages correctly, but the view is actually removed instantly making the animation... Well, it won't animate because nothing is left to animate!
Is this intentional from apple and if so, why? How do you do it correctly?
Thanks!
Edit.
Just to clearify what I'm doing:
I got a custom segue, vertically animating a Child-ViewController down from top which works as expected with the following code:
-(void)perform{
UIViewController *srcVC = (UIViewController *) self.sourceViewController;
UIViewController *destVC = (UIViewController *) self.destinationViewController;
destVC.view.transform = CGAffineTransformMakeTranslation(0.0f, -destVC.view.frame.size.height);
[srcVC addChildViewController:destVC];
[UIView animateWithDuration:0.5f
animations:^{
destVC.view.transform = CGAffineTransformMakeTranslation(0.0f, 0.0f);
[srcVC.view addSubview:destVC.view];
}
completion:^(BOOL finished){
[destVC didMoveToParentViewController:srcVC];
}];
}
Here it will happen in the following order (thanks to -addSubview being inside the animations-block):
Add childView (will automatically invoke -willMoveToParentViewController)
-addSubview will invoke -viewWillAppear
When the animation finishes, -addSubview will invoke -viewDidAppear
Manually invoke -didMoveToParentViewController inside the completion-block
Above is the exact expected behavior (just like the built-in transitions behave).
With the following code to do the above segue but backwards (with an unwindSegue):
-(void)perform{
UIViewController *srcVC = (UIViewController *) self.sourceViewController;
srcVC.view.transform = CGAffineTransformMakeTranslation(0.0f, 0.0f);
[srcVC willMoveToParentViewController:nil];
[UIView animateWithDuration:5.5f
animations:^{
srcVC.view.transform = CGAffineTransformMakeTranslation(0.0f, -srcVC.view.frame.size.height);
}
completion:^(BOOL finished){
[srcVC.view removeFromSuperview]; // This can be done inside the animations-block, but will actually remove the view at the same time ´-viewWillDisappear´ is invoked, making no sense!
[srcVC removeFromParentViewController];
}];
}
the flow will be like this:
Manually invoke -willMoveToParentView:nil to notify that it will be removed
When the animation finishes, both -viewWillDisappear & -viewDidDisappear will be invoked simultaneously (wrong!) and -removeFromParentViewController will automatically invoke -didMoveToParentViewController:nil.
And if I now move -removeFromSuperview in to the animations-block, the events will be sent correctly but the view is removed when the animation starts instead of when the animation finishes (this is the part that makes no sense, following how -addSubview behaves).
Your question is about removing view controller, because, viewWillDisappear and viewDidDisappear are method of view controller.
viewWillDisappear: will be called from completion block, not earlier, because this is the place where you said that you want to remove subview from main view.
If you want to remove some property before that point, then in child controller override willMoveToParentViewController: method. This method will be called before animation block.
Here's code example:
//Prepare view for removeing.
[self.childViewController willMoveToParentViewController:nil];
[UIView animateWithDuration:0.5
delay:1.0
options: UIViewAnimationOptionCurveEaseOut
animations:^{
self.childViewController.view.alpha = 0;
}completion:^(BOOL finished){
[self.childViewController.view removeFromSuperview];
[self.childViewController didMoveToParentViewController:self];
}];
So, the flow will be:
First willMoveToParentViewController: with nil parameter will be called
Animation block will start and view will set it's alpha property to 0
When animation finish, completion block will start to execute...
[self.childViewController.view removeFromSuperview]; will be called first
Then viewWillDissapear: in childViewController will be called
Then [self.childViewController didMoveToParentViewController:self];
And at the end viewDidDissapear: in childViewController will execute.
Pre request for this flow is that you embed childViewController with code like this:
[self addChildViewController:self.childViewController];
[self.view addSubview:self.childViewController.view];
[self.childViewController didMoveToParentViewController:self];
I have a custom UIViewController that's being displayed modally. I'm calling it mainMenu.
It has its own cute little transition animation it does to make its views slide off screen. When I want to dismiss it, I want to call the custom animation and then actually dismiss it once that's done. It seems like this should work:
- (void) dismissCustomViewController {
[mainMenu slideMenuPanelsAway];
[self dismissViewControllerAnimated:YES completion:nil];
}
However, this makes the view controller vanish instantly, before I get to see the custom slidey stuff.
What's the right way to make the view controller wait until the menus are gone before vanishing?
I've tried a bunch of things. I only found one way to make it work:
- (void) dismissCustomViewController {
[mainMenu slideMenuPanelsAway];
[self performSelector:#selector(dismissController) withObject:nil
afterDelay: 2.0f];
}
(I wrote a custom method called dismissController just to make the selector easier to use, it basically just calls [self dismissViewControllerAnimated:YES completion:nil];.)
It just seems awful kludgey to use a manual delay setting instead of actually basing it on the completion of the animation. There's got to be a better way, doesn't there?
Use animateWithDuration:animations:completion:, and do the "slidey stuff" in the animation block and do the dismissal in the completion block.
[UIView animateWithDuration:.5 animations:^{
//Your custom animation stuff here
} completion:^(BOOL finished) {
[self dismissViewControllerAnimated:YES completion:nil];
}];
I can't find this anywhere. So, if you possess the info about it, please give me a link.
I have one view controller, I made a menu for a simple game. There are some buttons on it.
I have another view controller and there some buttons too.
The question is:
"How I can do an animation of this buttons (hiding off the screen) after I choose one button that triggers a custom segue (without any animation) to another View Controller, which will run it's button animation(coming to the screen from a border of the screen)?"
I made this like this:
1) Theory: I make a IBAction for a menu button, then in this IBAction I call an animation method, which call a performSegueMethod:. After this in new VC in viewWillAppear method call a animation method (that almost equal method from source VC). All this works, but this don't look smooth. The problem with this animation occurs when destination VC replace source VC. There is some split second, when all looks static, and only after this animation starts.
I don't know how to remove this destination VC lag. May be I must load a destination view before a segue? I tried to do this, but may be a made something wrong, or it's just don't help me.
2) Practice:
firstViewController.m:
- (IBAction)newGameSegueButton {
[self menuSlideInDirection:#"CenterToLeft" performingSegueWithIdentifier:#"mode"];
}
-(void)menuSlideInDirection:(NSString *)direction performingSegueWithIdentifier:(NSString *)segueIdentifier
{
[UIView animateWithDuration:0.4 animations:^{
CGPoint newGameButtonCenter;
newGameButtonCenter.x = directionIndex * 160;
newGameButtonCenter.y = self.gameButtonSlide.center.y;
self.gameButtonSlide.center = newGameButtonCenter;
[UIView animateWithDuration:0.3 delay:0.1 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//some animation too
} completion:nil];
[UIView animateWithDuration:0.2 delay:0.2 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//animation
} completion:nil];
[UIView animateWithDuration:0.1 delay:0.3 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
//some animation too
} completion:^(BOOL finished){
if(segueIdentifier){
[self performSegueWithIdentifier:segueIdentifier sender:self];
}
}];
}];
}
Okay, then custom segue code is pretty simple:
-(void)perform
{
UIViewController *src = self.sourceViewController;
UIViewController *dst = self.destinationViewController;
[src.navigationController pushViewController:dst animated:NO];
}
And my secondViewController.m:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self menuSlideInDirection:#"RightToCenter" performingSegueWithIdentifier:nil];
}
menuSlideInDirection:#"RightToCenter" performingSegueWithIdentifier:nil in secondViewController identical to method with same name in firstView.
I'm looking for smooth animation of particular objects of destination view controller right after a segue.
May be a doing all wrong and there a another way to do this? (I only think of adding all view and all controls of destination VC to source VC and remove "destination VC" at all).
Hope, somebody can help me with this.
Try doing all first view controller's animations in perform method.
As for the second view controller's animations, I don't think there is any other 'good' way than doing it in viewWillAppear (although I would prefer viewDidAppear) since the outlets of the destination view controller are not set while performing the segue(they will be nil). In other words, you do not have a way to access your buttons, let alone animate them.
A hackish way would be to call [segue.destinationViewController view] before perform so that the destination view controller's view hierarchy is loaded and the outlets are set. Then perhaps, you may animate buttons in the destination view controller in perform before pushing it onto navigation stack.
For my question, I choose the way of putting two views under the same VC. Animation is smooth and it's look much better than using perform / viewWillAppear method.
Below is my code..
TestTemp1ViewController *temp=[[TestTemp1ViewController alloc]init];
[self.view addSubview:temp.view];
[self presentModalViewController:temp animated:FALSE];
This codes works well in iOS 5.0 but crashes in iOS 6.0.
Crash report: [UIViewController loadViewIfRequired]-- bad excess
I cannot understand why this isn't working in iOS 6.0. Guys, ya I know that's not the good way but what I am trying to do is presentviewcontroller with grow and shrink animation. If I do this after I present then I will get white background of view controller.
Below is my code...
-(void)animateGrowAndShrink:(ViewController *)controller1 {
//self.view.userInteractionEnabled=NO;
[self.view addSubview:controller1.view];
[self presentModalViewController:self.controller animated:FALSE];
if (dayTimeLineIsShown) {
//shrink dayTimeLineIsShown=FALSE;
[UIView beginAnimations:#"animationShrink" context:NULL];
[UIView setAnimationDuration:.61f];
controller1.view.transform=CGAffineTransformMakeScale(.01f,.01f);
} else {
//expand dayTimeLineIsShown=TRUE;
controller1.view.transform=CGAffineTransformMakeScale(0.01,0.01);
[UIView beginAnimations:#"animationExpand" context:NULL];
[UIView setAnimationDuration:.60f];
timeLine.view.transform=CGAffineTransformMakeScale(1, 1);
}
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(animationDidStop:finished:context:)];
[UIView commitAnimations];
}
-(void)animationDidStop:(NSString *)animationID finished:(BOOL)finished context:(void *)context{
self.view.userInteractionEnabled=YES;
if ([animationID isEqualToString:#"animationExpand"]) {
[self presentModalViewController:self.controller1 animated:FALSE];
} else {
self.controller.view.hidden=true;
}
}.
Also the controller in which I am doing this is also presented modally if I remove that then it's works in ios 6. Any other idea to achieve zooming and shrinking.
-presentModalViewController and -addSubview are quite different are they're not supposed to be used together. See: When should I use the addSubview method of a view controller?
I think remove the second line or third line will eliminate the error.
I was setting presentation style wrong..It should be like this..
self.modalPresentationStyle = UIModalPresentationCurrentContext;
It should be on current context.So, Now presented view controller view will be drawn on transparent background and not on white background so while shrinking it's view, there will be no white background behind it.
I am struggling with understanding why the first method below works for hiding and removing a subview of a view. In this first method I pass the pointer by reference. In the second method, which is less general, I have a delegate method designed for removing a specific view. I would like to use the first method, because I have several views that I would like to apply this too. I should mention that the first method works without fail as long as it is called within the implementing class. It fails when I call it from the view controller that I wish to dismiss. I get an EXC_BAD_ACCESS on the removeFromSuperview line when it fails in the first method.
-(void)closeView:(UIViewController **)viewController
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionCurveLinear
animations:^
{
[[*viewController view] setAlpha:0.0];
}
completion:^(BOOL finished)
{
[[*viewController view] removeFromSuperview];
[*viewController release], *viewController = nil;
}];
}
-(void)closeButtonClicked
{
[delegate closeView:&self];
}
//
// This method works without fail:
//
-(void)closeView
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionCurveLinear
animations:^
{
// In this context viewController is defined in the class interface
[[viewController view] setAlpha:0.0];
}
completion:^(BOOL finished)
{
[[viewController view] removeFromSuperview];
[viewController release], viewController = nil;
}];
}
-(void)closeButtonClicked
{
[delegate closeView];
}
First of all, it is not according to the style guides, and not a good idea in general, to do a release of the viewController within a method like this. It will get you into trouble quickly. If the caller of this method is responsible for the viewController (it has done the retain), then it should release it as well. This is likely the cause of the first method not working from within the viewcontroller itself.
In the second method you do not pass in the viewController as parameter, which means it needs to be defined in the context.
If you don't release the viewController in this method, then you don't need to set its variable to nil either, and you can simply pass it as normal parameter:
-(void)closeView:(UIViewController *)viewController
{
[UIView transitionWithView:self.view
duration:UINavigationControllerHideShowBarDuration
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^
{
[[viewController view] removeFromSuperview];
}
completion:nil];
}
you would then do this at the call-site:
[self closeView:childViewController];
[childViewController release]; childViewController = nil;
It safe to release the child in this way before the animation is done, because the animations block implicitly retains all objects referenced from the block, including the viewController parameter. Therefore, the child's dealloc is not called until the animations block releases it.
This does not work in your first code example, because you pass a pointer to a variable. That is, the animations block does not know it needs to retain the child.
BTW, I am not sure why you want to set the alpha, in the example above I show that you can also remove the view already in the animations block. See more about that in the UIView Class Reference.
**viewcontroller and &self is not the way to go. In Objective-C, you do [self.view removeFromSuperview] in the subview itself, in the parent viewcontroller you do release or with ARC just replace the subview with another view.