getting a view controller to disappear - objective-c

I'm trying out a multiple view application, but I can't seem to get the first view controller to go away when I bring in the new view controller. I'm laying the second (coming) view controller at index 0, and it's just placing it in the background. I thought the [going.view removeFromSuperview] would remove the original viewcontroller, but that's not what is happening...
UIViewController *coming = nil;
UIViewController *going = nil;
UIViewAnimationTransition transition;
if (answer == YES)
{
coming = boyController;
going = getInfoController;
transition = UIViewAnimationTransitionFlipFromLeft;
}
else
{
coming = girlController;
going = getInfoController;
transition = UIViewAnimationTransitionFlipFromLeft;
}
NSLog(child);
[UIView setAnimationTransition:transition forView: self.view cache:YES];
[coming viewWillAppear:YES];
[going viewWillDisappear:YES];
[going.view removeFromSuperview];
[self.view insertSubview:coming.view atIndex:0];
[going viewDidDisappear:YES];
[coming viewDidAppear:YES];
[UIView commitAnimations];

First a little refactoring:
coming = (answer ? boyController : girlController);
You can delete going and transition, as they're only used once. Then, to actually do the animation, you need to put everything in the context of an animation block.
[UIView beginAnimations:#"flipAnimation" context:NULL];
[UIView setAnimationTransition:transition forView:self.view cache:YES];
[getInfoController.view removeFromSuperview];
[self.view addSubview:coming.view];
[UIView commitAnimations];
viewWillAppear: and viewWillDisappear: are delegate methods. These will be called automatically on those views' delegates, if any. They shouldn't ever be called manually.

Related

Simple UIView animation move doesn't work

I have no idea, why it is not working.
All I want to do is a simple UIView animation in the viewDidLoad.
Here's my code:
[UIView animateWithDuration:3.0f animations:^{
[self.headline setCenter:CGPointMake(0.0, 200.0)];
}];
Nothing happens. When I test the general approach of calling a animation method on that particular object like this:
[UIView animateWithDuration:3.0f animations:^{
[self.headline setAlpha:0.0];
}];
it works!!! Why am I not able to move the view across the screen? I am using latest Xcode 4.5.
Thanks for any advice!
UPDATE:
When I add a view manually in code it works. But for the UIViews I create as Outlets in the Interface Builder it doesn't work.
UILabel *testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 100.0)];
testLabel.backgroundColor = [UIColor redColor];
[self.view addSubview:testLabel];
[UIView animateWithDuration:3.0 animations:^{
testLabel.center = CGPointMake(0.0, 200);
}];
So obviously I am doing something wrong in the .xib file
Dont do it in viewDidLoad. The view is not pressent at that time yet.
Try it in viewDidAppear.
Well, I think the reason for the missing animation was the fact that I called a different push navigation animation from the view controller that was pushing the actual view on screen:
- (IBAction)goForSelection:(id)sender
{
SelectionViewController *selectionViewController = [[SelectionViewController alloc] initWithNibName:#"SelectionViewController" bundle:nil];
//[self.navigationController pushViewController:selectionViewController animated:YES];
[UIView transitionWithView:self.navigationController.view duration:1.0 options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
[self.navigationController pushViewController:selectionViewController animated:NO];
} completion:^(BOOL finished) {
[selectionViewController startIntroAnimation];
}];
}
First I checked what happens when I use the default navigation controller segue. And to my surprise the animation did start. Then I inserted the call to the [selectionViewController startIntroAnimation] to the completion block and this works as well.

Remove from superview AND enable user interaction

I'm doing some custom animations to change views in a single method. I already removed "fromView" from superView using [UIView setAnimationDidStopSelector:#selector(removeFromSuperview)], but I also want to enable user interaction after the end of the animation.
Here's my code:
-(void) switchFrom:(UIViewController*) fromViewController To:(UIViewController*) toViewController usingAnimation:(int) animation{
UIView *fromView = fromViewController.view;
UIView *toView = toViewController.view;
/*************** SET ALL DEFAULT TRANSITION SETTINGS ***************/
// Get the current view frame, width and height
CGRect pageFrame = fromView.frame;
CGFloat pageWidth = pageFrame.size.width;
// Create the animation
[UIView beginAnimations:nil context:nil];
// Create the delegate, so the "fromView" is removed after the transition
[UIView setAnimationDelegate: fromView];
[UIView setAnimationDidStopSelector:#selector(removeFromSuperview)];
// Set the transition duration
[UIView setAnimationDuration: 0.4];
/*************** IT DOESN'T WORK AT ALL ***************/
[toView setUserInteractionEnabled:NO];
[UIView setAnimationDelegate: toView];
[UIView setAnimationDidStopSelector:#selector(setUserInteractionEnabled:)];
[toView setUserInteractionEnabled:YES];
// Add the "toView" as subview of "fromView" superview
[fromView.superview addSubview:toView];
switch (animation) {
case AnimationPushFromRigh:{
// Position the "toView" to the right corner of the page
toView.frame = CGRectOffset(pageFrame, pageWidth,0);
// Animate the "fromView" to the left corner of the page
fromView.frame = CGRectOffset(pageFrame, -pageWidth,0);
// Animate the "toView" to the center of the page
toView.frame = pageFrame;
// Animate the "fromView" alpha
fromView.alpha = 0;
// Set and animate the "toView" alpha
toView.alpha = 0;
toView.alpha = 1;
// Commit the animation
[UIView commitAnimations];
}
.
.
.
Any idea how can I call this two methods in setAnimationDidStopSelector and actually make them work together?
EDIT 1
Tried #safecase code like this, replacing this commented block:
/*************** IT DOESN'T WORK AT ALL ***************
[toView setUserInteractionEnabled:NO];
[UIView setAnimationDelegate: toView];
[UIView setAnimationDidStopSelector:#selector(setUserInteractionEnabled:)];
[toView setUserInteractionEnabled:YES];
*************** IT DOESN'T WORK AT ALL ***************/
// Remove the interaction
[toView setUserInteractionEnabled:NO];
[fromView setUserInteractionEnabled:NO];
// Create the animation
[UIView animateWithDuration:0.4 animations:^{
[fromView performSelector:#selector(removeFromSuperview)];
}
completion:^(BOOL finished){
[UIView animateWithDuration:0.4
animations:^{
C6Log(#"finished");
[toView performSelector: #selector(setUserInteractionEnabled:)];
}];
}];
The "toView" is removed instead of the "fromView" is removed :(
The buttons continue to be interactive during the animation
Do you know beginIgnoringInteractionEvents and endIgnoringInteractionEvents?
Normally, if you don't want any userInteraction during an animation, you would use those.
Anyway you still need correct callbacks to trigger them.
You need to define your own method, e.g. remove: and pass that as the selector. In the method, just use the passed UIView to remove it.
-(void)remove:(id)sender {
UIView *v = (UIView*)sender;
[v removeFromSuperview];
}
You can use this:
[UIView animateWithDuration:0.4
animations:^{ [self performSelector:#selector(removeFromSuperview)]; // other code here}
completion:^(BOOL finished){ [UIView animateWithDuration:0.4
animations:^{ [self performSelector:#selector(setUserInteractionEnabled:)]; //other code here}]; }];
Hope helpful.
I think u can use to enable user interaction
self.view.userInteractionEnabled=NO;
I ended up creating a Category extension to UIView… and it finally works!
UIView+Animated.h code
#import <UIKit/UIKit.h>
#interface UIView (Animated)
- (void) finishedAnimation;
#end
UIView+Animated.m code
#import "UIView+Animated.h"
#implementation UIView (Animated)
- (void) finishedAnimation{
C6Log(#"FinishedAnimation!");
[self removeFromSuperview];
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
}
#end
Then, I #imported this extension in my coordinating controller:
C6CoordinatingController.h partial code
#import "UIView+Animated.h"
And called the selector in setAnimationDidStopSelector:
C6CoordinatingController.m partial code
// Create the delegate, so the "fromView" is removed after the transition
[UIView setAnimationDelegate: fromView];
// Ignore interaction events during the animation
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
// Set the transition duration
[UIView setAnimationDuration: 0.4];
// Call UIView+Animated "finishedAnimation" method when animation stops
[UIView setAnimationDidStopSelector:#selector(finishedAnimation)];
So, I ended up using #jaydee3 and #Mundi concepts and it works like a charm!
Thank you guys!

Multiple UIView Animations

I am trying to perform multiple UIView animations one after the other. However, I've heard that it's bad practice to perform multiple UIView animations one after the other, and that I should instead use Core Animation. I tried this code:
//First Animation
[UIView beginAnimations:#"animation1" context:nil];
[UIView setAnimationDuration:2];
nwView.backgroundColor = [UIColor redColor];
nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
CGRectGetMinY(screenSize),
width,
height);
nwView.transform = CGAffineTransformMakeRotation(45.0f);
[UIView commitAnimations];
//Second Animation
[UIView beginAnimations:#"second animation" context:nil];
[UIView setAnimationDuration:2];
nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
nwView.backgroundColor = [UIColor purpleColor];
[UIView commitAnimations];
But it only does the second animation. I know this question is similar to UIView two animations coexisting, but it has a slightly different context.
I don't think there is anything wrong with doing 2 animations in a row using UIView blocks. Just make sure you start your second animation in the completino block of the first animation.
Without blocks (your example) it is not working as you will have to set a delegate to the animation or set a selector for setAnimationDidStopSelector. There you should start the second animation.
But again, nothing wrong in doing animations with blocks (it is the preferred way).
If you are looking to do multiple animations right after another this is not the way to do it. The code you posted with execute at almost the same time, meaning the animations would be performed at about the same time.
Instead what you should do is set the delegate for the first animation, and then do the first animation. Then when the animationDidStop method is called for the first animation, you should do the second animation. This makes sure they are one after another.
This is how you would do it, assuming you call doMyAnimations to start the animation.
-(void)doMyAnimations{
//First Animation
[UIView beginAnimations:#"animation1" context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self];
nwView.backgroundColor = [UIColor redColor];
nwView.frame = CGRectMake(CGRectGetMidX(screenSize),
CGRectGetMinY(screenSize),
width,
height);
nwView.transform = CGAffineTransformMakeRotation(45.0f);
[UIView commitAnimations];
}
- (void)animationWillStart:(NSString *)animationID context:(void *)context{
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context{
if([animationID isEqualToString:#"animation1"]){
//Second Animation
[UIView beginAnimations:#"second animation" context:nil];
[UIView setAnimationDuration:2];
nwView.transform = CGAffineTransformMakeScale(0.5, 0.33);
nwView.backgroundColor = [UIColor purpleColor];
[UIView commitAnimations];
}
}
Keep in mind that the nwView would have to be accessible throughout the entire class. If it isn't you can either make it an instance variable, or find another way to get access to it in the animationDidStop method.
You can use blocks for this purpose and get a very clean result.
NSMutableArray* animationBlocks = [NSMutableArray new];
typedef void(^animationBlock)(BOOL);
// getNextAnimation
// removes the first block in the queue and returns it
animationBlock (^getNextAnimation)() = ^{
animationBlock block = (animationBlock)[animationBlocks firstObject];
if (block){
[animationBlocks removeObjectAtIndex:0];
return block;
}else{
return ^(BOOL finished){};
}
};
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
[UIView animateWithDuration:1.0 animations:^{
//...animation code...
} completion: getNextAnimation()];
}];
//add a block to our queue
[animationBlocks addObject:^(BOOL finished){;
NSLog(#"Multi-step Animation Complete!");
}];
// execute the first block in the queue
getNextAnimation()(YES);
Taken from: http://xibxor.com/objective-c/uiview-animation-without-nested-hell/

presentModalViewController translucent with previous view in the background

Im trying to present a view translucently and that the previous view sticks around and be that its visible in the background.
I've got
[self presentModalViewController:modalView animation:YES];
and I have the transparency set in the modalView's viewDidLoad, but after modalView gets brought up the previous view disappears. What can I do to keep the other view to stay around in the background?
I have also tried adding it with
[self.view addSubview:modalView.view];
It doesn't cover the whole screen, I would like to be able to solve this problem using presentModalViewController method.
It sounds like you just want a view to be displayed on top of your main view. Modal views are a finicky way of presenting subviews, instead you should look at creating a simple view class to add to your view controller. You can then use [UIView animate...]; method to animate it in and out of view.
To get you started:
- (void)displayViewButtonPressed(id)sender
{
if (!self.topView)
{
UIView *overlayView = [[UIView alloc] initWithFrame:CGRectMake(44.0f, 22.0f, 40.0f, 44.0f];
[overlayView setAlpha:0.0f];
[overlayView setBackgroundColor:[UIColor redColor]];
[self setTopView:overlayView];
[overlayView release];
}
[self.view addSubView:self.topView];
[UIView animateWithDuration:0.5
animations:^{
[self.topView setAlpha:1.0f];
}];
}
In the above method we create a custom UIView and animate it into position. We maintain a pointer to it so we can remove it later (like so:)
- (void)dismissViewButtonTapped:(id)sender
{
[UIView animateWithDuration:0.5
animations:^{
[self.topview setAlpha:0.0f];
}
completion:^(BOOL finished) {
[self.topView removeFromSuperView];
}
}
Its a little more work that using modal views, but it gives you much more flexibility in regards to what you use and how you display it.
Hope this helps:)

Extending a view to the left side, animated

I have a view that I want to extend on the left side using an animation. All borders but the left one should remain the same, so the x position and the width of the view are changing.
I use this code:
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:5.0];
self.frame = CGRectMake(self.frame.origin.x-100,
self.frame.origin.y,
self.frame.size.width+100,
self.frame.size.height);
[UIView commitAnimations];
If I run this code, the width of the view is set to the new value immediately and then the view is moved to the new x point, but why? How can I change this behaviour?
Thanks for your ideas!
Your code runs fine for me. How are you running it? Calling -setFrame is the right choice for what you're doing. Here is what my custom view looks like:
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
}
return self;
}
- (void)dealloc {
[super dealloc];
}
- (void)go;
{
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:5.0];
self.frame = CGRectMake(self.frame.origin.x-100,
self.frame.origin.y,
self.frame.size.width+100,
self.frame.size.height);
[UIView commitAnimations];
}
#end
I create an outlet from my view controller of type TestView and connect it and set its type in IB. Then I create a button whose handler calls [customView go]; Seems to work fine.
Okay, I made it woring now. The problem was that my view was a UIButton and I set the title and background with the setBackground:forState: and setTitle:forState:.
It seems that the background of the view is not animatable. I use the work around that I add my own background with a UIImageView.
Thanks for your replies!