13

I'm doing a simple animation of UIView height so that it reveals.

By default it seems to be revealing from top to bottom, and I want it to reveal bottom to top.

I have the UIView anchored to the bottom of the screen.

I'm sure it something simple i'm missing..... any tips?

Thanks

bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
  • Did you try anchoring it at the bottom and then changing the y coord to the correct place in your view? – J Max Nov 18 '11 at 21:03
  • Yup, its anchored at the bottom. I think maybe I need to flip the coordinate system or something like that.... because in an animation block i'm telling the frame to go from 100 to 0, and iOS coordinates go from top to bottom.... – bandejapaisa Nov 18 '11 at 21:13
  • Changing the y coordinate would just move the view off screen... I want it to collapse – bandejapaisa Nov 18 '11 at 21:16
  • Thats not going to work. Its on an iPad anyway. Imagine a square view in the middle of the screen that I want to collapse. I don't have the luxury of the edges of the screen. I want to collapse the box from top to bottom, and then reveal it again from bottom to top... – bandejapaisa Nov 18 '11 at 21:24
  • i just animate the height of a CALayer anchored at (0,1) and it animated towards the top while anchored at the bottom. haven't tried with UIView. – aleph_null Nov 20 '11 at 05:14
  • Well every UIView is backed by a CALayer, so changing the view height is changing the underlying layers height. – bandejapaisa Nov 21 '11 at 10:48
  • @aleph_null I've tried that and it doesn't animate in the way you suggest.. how exactly do you animate it? – bandejapaisa Nov 21 '11 at 20:09
  • i just posted my code... are you looking for something like that? – aleph_null Nov 21 '11 at 23:11

6 Answers6

11

I really think the simplest way to accomplish this would be to animate BOTH the height and the y properties of the view. If they happen along the same curve, it should look completely seamless to the user. As you are animating the height to 0, also animate the y component to the original y + the original height.

UIView *view = ...;
float originalY = view.frame.origin.y;
float originalH = view.bounds.size.height;

[UIView animateWithDuration:1.2f delay:1.0f options:UIViewAnimationCurveEaseInOut animations:^{
    view.frame = CGRectMake(view.frame.origin.x, (originalY + originalH), view.bounds.size.width, 0);

}completion:^(BOOL finished) {
    NSLog(@"Animation is complete");
}];

I believe this would give the look and feel of a collapsing view. I haven't tried this out in code, but I see no reason why it wouldn't be possible like this.

atreat
  • 4,243
  • 1
  • 30
  • 34
9

hide under bottom

[self animateViewHeight:myView withAnimationType:kCATransitionFromBottom];

for reverse animation

[self animateViewHeight:myView withAnimationType:kCATransitionFromTop];

...

- (void)animateViewHeight:(UIView*)animateView withAnimationType:(NSString*)animType {  
    CATransition *animation = [CATransition animation];
    [animation setType:kCATransitionPush];
    [animation setSubtype:animType];

    [animation setDuration:0.5];
    [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    [[animateView layer] addAnimation:animation forKey:kCATransition];
    animateView.hidden = !animateView.hidden;
}
Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
5

Like a dog with a bone I figured this out....

Instead of animating the frame height, I applied a transform to the view and set the anchor point of the layer.

//set the anchor point to the bottom of the view
[self setAnchorPoint:CGPointMake(0.5, 1.0) forView:hostView];
//Scale the height to close to zero
hostView.transform = CGAffineTransformMakeScale(1, 0.00001);

If I put 0 as the y scale, the view behaves weird.... at the end of the animation i just set it to hidden.

On the way back up I just use the Identity Transform (reset it)

hostView.transform = CGAffineTransformIdentity;

Note that changing my anchor point shifted the position of my view. See this post for the setAnchorPoint method which normalises the view after setting the anchorPoint

Changing my CALayer's anchorPoint moves the view

neowinston
  • 7,584
  • 10
  • 52
  • 83
bandejapaisa
  • 26,576
  • 13
  • 94
  • 112
3

Instead you could try putting it in a view with clipsToBounds = YES and then animate it from the bottom to the middle of the view, like so:

viewToAnimate.frame = CGRectMake(viewToAnimate.frame.origin.x,
                                 viewToAnimate.superview.frame.size.height,
                                 viewToAnimate.frame.size.width,
                                 viewToAnimate.frame.size.height);

[UIView animateWithDuration:0.5 animations:^{
     viewToAnimate.center = viewToAnimate.superview.center;
}];

This way, you don't have to set the height to 0, and it solves any problems with autoresizing within the view.

aopsfan
  • 2,451
  • 19
  • 30
  • sounds like that would work - but its a bit of a hack, and I'm a purist... there must be a correct way of doing this, and now its bugging me and I have to figure it out... thanks for the idea though – bandejapaisa Nov 18 '11 at 21:21
  • 2
    Not to be rude, but I don't see how this is a hack in any way. Besides, from what you described, it would _look_ to a user like its sliding up from the bottom, not growing, am I correct? Also, I don't think there is a non-hacky way to "grow" it from the bottom, since the change in the origin.y will start the animation from the top. – aopsfan Nov 18 '11 at 21:25
  • I see it as a hack because I believe there is a 'correct' way to do it.... I'd be adding superfluous UIViews to my heirarchy to mask the moving of other views to perform what I need. I should be able to apply a transform directly to the layer I want to animate. – bandejapaisa Nov 18 '11 at 21:34
  • ThanQ it perfectly helped me with some own modifications anyway thnQ so much for the Start.... – ashokdy Dec 04 '13 at 11:22
2

As requested, this is the code that I'm using... I'm using a CAKeyFrameAnimation, which may be a bit more than what you're looking for. It would probably work the same with a CABasicAnimation, I'm just showing you this code because I already have it written.

-(id)initWithFrame:(CGRect)frame {
  self = [super initWithFrame:frame];
  if (self) {   
    springLayer = [[CALayer alloc] init];
    springLayer.backgroundColor = [UIColor redColor].CGColor;
    springLayer.anchorPoint = CGPointMake(0, 1);
    springLayer.frame = CGRectMake(125, 285, 100, 115);
    [springLayer setNeedsDisplay];
    [self.layer addSublayer:springLayer];
    [self test];
  }
  return self;
}

-(void)test {
    CAKeyframeAnimation *heightAnim = [[CAKeyframeAnimation alloc] init];
    heightAnim.duration = 3;
    heightAnim.removedOnCompletion = NO;
    heightAnim.fillMode = kCAFillModeForwards;

    heightAnim.beginTime = CACurrentMediaTime() + 0.25;

    NSMutableArray *v = [[NSMutableArray alloc] init];
    NSMutableArray *t = [[NSMutableArray alloc] init];

    float dest = 250;
    float difference = 135;


    while (difference > 1.0) {
        [v addObject:[NSNumber numberWithFloat:dest-difference]];
        [t addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];

        difference *= 0.7;

        [v addObject:[NSNumber numberWithFloat:dest+difference]];
        [t addObject:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];

        difference *= 0.7;
    }

    heightAnim.values = v;
    heightAnim.timingFunctions = t;

    [springLayer addAnimation:heightAnim forKey:@"bounds.size.height"];
}
aleph_null
  • 5,766
  • 2
  • 24
  • 39
0

one way I've done it with an AdWhirlView, hide it below the screen, then animate it up;

AdWhirlView *adWhirlView = [AdWhirlView requestAdWhirlViewWithDelegate:self];
adWhirlView.delegate = self;
adWhirlView.frame = CGRectMake(0, 430+kAdWhirlViewHeight, kAdWhirlViewWidth, kAdWhirlViewHeight);
[self.parentViewController.view insertSubview:adWhirlView belowSubview:self.view];

[UIView beginAnimations:@"AdWhirlIn" context:nil];
[UIView setAnimationDuration:.5];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
adWhirlView.frame = CGRectMake(0, 430, kAdWhirlViewWidth, kAdWhirlViewHeight);
[UIView commitAnimations];
timbroder
  • 858
  • 1
  • 13
  • 25