9

I'm trying to make an exact "translation" of this UIView block-based animation code:

[UIView animateWithDuration:0.5
                      delay:0.0
                    options:UIViewAnimationOptionCurveEaseInOut
                 animations:^{
                               someView.frame = CGRect(0, 100, 200, 200);
                             }
                 completion:nil];

using CABasicAnimation instead.

I'm totally aware that the frame property is actually a combination of position, bounds and anchorPoint of the underlying layer, as it is described here: http://developer.apple.com/library/mac/#qa/qa1620/_index.html
... and I already made a solution like that, using two CABasicAnimations one setting the position, one for bounds and it works for that one view.

The problem is however that I have subviews inside my view. someView has a subview of type UIScrollView in which I place still another subview of type UIImageView. UIScrollView subview has autoresizingMask set to UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight. That all works perfectly if I use the UIView block-based version, however when I try using CABasicAnimations the subviews start behaving unexpectedly(i.e. get resized to incorrect widths). So it seems autoresizingMask is not working correctly when using CABasicAnimations. I noticed also that subviews don't receive a call to setFrame:, although the frame property of the parent view does change after changes to layer position and bounds are made.

That's why I would like to know what would be the correct code to replicate with CABasicAnimation that what is happening when one uses UIView's animateWithDuration method.

Ivan Kovacevic
  • 1,322
  • 12
  • 30

1 Answers1

12

I'm totally aware that the frame property is actually a combination of position, bounds and anchorPoint of the underlying layer

Good, but it's important also to be aware that frame is not an animatable property for layers. If you want to animate with CABasicAnimation you must use properties that are animatable for layers. The CALayer documentation marks every such property as explicitly "animatable". The idea of using bounds and position is correct.

Thus, this code does essentially what you were doing before:

[CATransaction setDisableActions:YES];
// set final bounds and position
v.layer.bounds = CGRectMake(0,0,200,200);
v.layer.position = CGPointMake(100,200);

// cause those changes to be animated
CABasicAnimation* a1 = [CABasicAnimation animationWithKeyPath:@"bounds"];
a1.duration = 0.5;
CABasicAnimation* a2 = [CABasicAnimation animationWithKeyPath:@"position"];
a2.duration = 0.5;
[v.layer addAnimation:a1 forKey:nil];
[v.layer addAnimation:a2 forKey:nil];

However, that code has no effect on the size of any sublayers of v.layer. (A subview of v is drawn by a sublayer of v.layer.) That, unfortunately, is the problem you are trying to solve. I believe that by dropping down to the level of layers and direct explicit core animation, you have given up autoresizing, which happens at the view level. Thus you will need to animate the sublayers as well. That is what view animation was doing for you.

This is an unfortunate feature of iOS. Mac OS X has layer constraints (CAConstraint) that do at the layer level what autoresizing does at the view level (and more). But iOS is missing that feature.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Yes, I've created two CABasicAnimations one for @"position" KeyPath, the other one for @"bounds". I've set their individual fromValue and toValue. Then I've created one CAAnimationGroup and added these two animations to it, after which I add the group animation to the layer. At the end I set someView.layer.bounds and someView.layer.position to the new desired values(used in toValue) so that the change actually persists in the model layer tree. – Ivan Kovacevic Mar 23 '13 at 02:06
  • The animation works for someView, which is a main(parent) view containing other subviews. The problem is that subviews don't get resized, they keep their width as if there is a problem with autoresizingMask(which is set on them). It all works when using UIView animateWithDuration: however I would like to use CABasicAnimations since I need additional configuration options... – Ivan Kovacevic Mar 23 '13 at 02:17
  • 2
    I'm with you. I believe that because you are animating the layer directly, you will have to manage animation of the sublayers manually as well. – matt Mar 23 '13 at 02:23
  • That's what I was afraid of :(... Well hopes are slim, but I'll wait a bit more, maybe someone has some idea... Thank you anyhow! – Ivan Kovacevic Mar 23 '13 at 02:32
  • I'm now revisiting how I could still use UIView animateWithDuration. I described my problem in a different question, maybe you care to take a look: http://stackoverflow.com/questions/15582988/get-a-reference-of-an-animation-created-by-uiviews-animatewithduration-or-ident – Ivan Kovacevic Mar 23 '13 at 03:26