title: CAAnimation - change properties in the last frame of the animation?
body: <p>I'm animating a UIView (or rather its CALayer) and at the end of the animation it is no longer visible (I do a 3D transform so that it rotates about y by 90°, imagine a door opening towards you), though it's technically 'visible' in that its frame is still on-screen.</p>
  
  <p>At the end of this animation, I remove that view from its superview in the animationDidStop:finished: method.</p>
  
  <p>Problem is, there's a brief flicker where you see this view in its original untransformed state before it gets removed.</p>
  
  <p>I'm wondering how to fix this.  I have no idea.  I tried animating the layer's opacity but that didn't work either.  The flicker still happens.</p>
  
  <p>Any ideas?  Can I provide anything or perhaps someone here has had this problem before?</p>
  
  <p>EDIT:  Here's some code (scroll further down and look for the line inside if(isHalfDone) where [subview removeFromSuperview];</p>
  
  <pre><code>- (void) pageOpenView:(UIView *)viewToOpen duration:(NSTimeInterval)duration pageTurnDirection:(PageTurnDirection) p{
      // Remove existing animations before stating new animation
      [viewToOpen.layer removeAllAnimations];
  
      // Make sure view is visible
      viewToOpen.hidden = NO;
  
      // disable the view so it’s not doing anythign while animating
      viewToOpen.userInteractionEnabled = NO;
  
      float dir = p == 0 ? -1.0f : 1.0f;  // for direction calculations
  
      // create an animation to hold the page turning
      CABasicAnimation *transformAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
      transformAnimation.removedOnCompletion = NO;
      transformAnimation.duration = duration;
      transformAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
  
      CATransform3D startTransform = CATransform3DIdentity;
  
      if (p == NEXT_PAGE) {
          startTransform.m34 = 0.001f;
      }else {
          startTransform.m34 = -0.001f;
      }
  
      // start the animation from the current state
      transformAnimation.fromValue = [NSValue valueWithCATransform3D:startTransform];
      // this is the basic rotation by 90 degree along the y-axis
      CATransform3D endTransform = CATransform3DMakeRotation(3.141f/2.0f,
                                                             0.0f,
                                                             dir,
                                                             0.0f);
      // these values control the 3D projection outlook
      if (p == NEXT_PAGE) {
          endTransform.m34 = 0.001f;
          endTransform.m14 = -0.0015f;
      }else {
          endTransform.m34 = -0.001f;  
          endTransform.m14 = 0.0015f;
      }
  
  
      transformAnimation.toValue = [NSValue valueWithCATransform3D:endTransform];
  
  
      // Create an animation group to hold the rotation
      CAAnimationGroup *theGroup = [CAAnimationGroup animation];
  
      // Set self as the delegate to receive notification when the animation finishes
      theGroup.delegate = self;
      theGroup.duration = duration;
      // CAAnimation-objects support arbitrary Key-Value pairs, we add the UIView tag
      // to identify the animation later when it finishes
      [theGroup setValue:[NSNumber numberWithInt:viewToOpen.tag] forKey:@"viewToOpenTag"];  // We set the tag to the page number
      [theGroup setValue:[NSNumber numberWithInt: p] forKey:@"PageTurnDirection"]; 
      [theGroup setValue:[NSNumber numberWithBool:YES] forKey:@"isAnimationMidpoint"];  // i.e. is this the first half of page-turning or not?
  
      // Here you could add other animations to the array
      theGroup.animations = [NSArray arrayWithObjects:transformAnimation,  nil];
      theGroup.removedOnCompletion = NO;
      // Add the animation group to the layer
      [viewToOpen.layer addAnimation:theGroup forKey:@"flipViewOpen"];
  }
  
  - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag {
      // Get the tag from the animation, we use it to find the animated UIView
      NSNumber *tag = [theAnimation valueForKey:@"viewToOpenTag"];  
      PageTurnDirection dir = [[theAnimation valueForKey:@"PageTurnDirection"] intValue];  
      BOOL isHalfDone = [[theAnimation valueForKey:@"isAnimationMidpoint"] boolValue];
  
      UIView *toStack;    // the stack the page has moved from
      UIView *fromStack;  // the stack the new page will go to.
      int indexDirection;  // will be either 1 or -1
  
      int targetLastPageInStack;  // the page number that wants to get added to its stack  (i.e. either high or low)
  
      // depending on which direction we are turning, we search a different stack
      if (dir == NEXT_PAGE) {
          fromStack = rightStack;
          toStack = leftStack;
          indexDirection = 1;
          int lastPg;
  
          UIView *lastPgInStack;
  
          if ([fromStack.subviews count] &gt; 0) {
              lastPgInStack = [fromStack.subviews objectAtIndex:0];
  
              if ([lastPgInStack tag] == pageIndex) {
                  lastPg = pageIndex;  // pagenr of currently last page in stack.
              }
              else {
                  // there was no last page, so the current page is the last page
                  lastPg = [lastPgInStack tag];
              }
          }else {
              lastPg = pageIndex;
          }
  
  
          targetLastPageInStack = lastPg + 2;
      }
      else {
          fromStack = leftStack;
          toStack = rightStack;
          indexDirection = -1;
          int lastPg;
          UIView *lastPgInStack = [fromStack.subviews objectAtIndex:0];
  
          if ([lastPgInStack tag] == pageIndex-1) {
              lastPg = pageIndex-1;  // pagenr of currently last page in stack.
          }
          else {
              // there was no last page, so the current page is the last page
              lastPg = [lastPgInStack tag];
          }
  
          targetLastPageInStack = lastPg - 2;
      }
  
  
      // different handling if the page turning is half done, or fully done.
      if (isHalfDone) {
  
          UIView *subview = [fromStack viewWithTag:[tag intValue]];
  
          if (flag) {
              // Now we just hide the animated view since
              // animation.removedOnCompletion is not working
              // in animation groups. Hiding the view prevents it
              // from returning to the original state and showing.
  
              subview.hidden = YES;
              [subview removeFromSuperview];  // REMOVE THE ANIMATED PAGE FROM THE STACK
  
  
              // now create one on that stack
              if ((dir == NEXT_PAGE &amp;&amp; targetLastPageInStack &lt;= self.endPageIndex) || (dir == PREV_PAGE &amp;&amp; targetLastPageInStack &gt;= self.startPageIndex)) {
  
                  BODBookPage *newPage = [BODBookPage pageInBook:&amp;myDocumentRef pageNum:targetLastPageInStack frame:fromStack.bounds] ;
                  newPage.hidden = NO;
                  [fromStack insertSubview:newPage atIndex:0];
              }
  
          }
  
          // now create the view for the page that'll be turned over and add it to the right stack.  Probably set it to hidden as well.
          BODBookPage *newPage = [BODBookPage pageInBook:&amp;myDocumentRef pageNum:[tag intValue] + 1*indexDirection frame:toStack.bounds] ;
          newPage.hidden = YES;
          [toStack addSubview:newPage];
          [self pageCloseView:newPage duration:turningTime/2.0f pageTurnDirection:dir];
      }
      else {
          // we just animated the page turning from up to over
  
          // now have to remove the most distant view, so the view at subview index 0.  We do this to keep memory usage low.
          if ([toStack.subviews count] &gt; 1) {
              [(UIView*)[toStack.subviews objectAtIndex:0] removeFromSuperview];
          }
  
  
          pageIndex = pageIndex + 2*indexDirection;
      }
  
  }
  </code></pre>