title: iOS Paper fold (origami / accordion) effect animation, with manual control
body: <p>I'm looking for tips on how to implement the popular 'paper folding / origami' effect in my iOS project. </p>
  
  <p>I'm aware of projects such as: 
  <a href="https://github.com/xyfeng/XYOrigami" rel="noreferrer">https://github.com/xyfeng/XYOrigami</a>
  but they only offer the 'animated' effect, with no manual control over the opening animation. </p>
  
  <p>I've struggled to dissect that project and come up with what I'm after.</p>
  
  <p>To be more exact, I'm looking on how to implement the effect shown here: <a href="http://vimeo.com/41495357" rel="noreferrer">http://vimeo.com/41495357</a> where the folding animation is not simply animated open, but the user controls the opening folds.</p>
  
  <p>Any help would be much appreciated, thanks in advance! </p>
  
  <p>EDIT:</p>
  
  <p>Okay, here's some example code to better illustrate what I'm struggling with:</p>
  
  <p>This method triggers the origami effect animation:</p>
  
  <pre><code>- (void)showOrigamiTransitionWith:(UIView *)view 
                  NumberOfFolds:(NSInteger)folds 
                       Duration:(CGFloat)duration
                      Direction:(XYOrigamiDirection)direction
                     completion:(void (^)(BOOL finished))completion
  {
  
  if (XY_Origami_Current_State != XYOrigamiTransitionStateIdle) {
      return;
  }
  XY_Origami_Current_State = XYOrigamiTransitionStateUpdate;
  
  //add view as parent subview
  if (![view superview]) {
      [[self superview] insertSubview:view belowSubview:self];
  }
  
  
  //set frame
  CGRect selfFrame = self.frame;
  CGPoint anchorPoint;
  if (direction == XYOrigamiDirectionFromRight) {
  
      selfFrame.origin.x = self.frame.origin.x - view.bounds.size.width;
  
      view.frame = CGRectMake(self.frame.origin.x+self.frame.size.width-view.frame.size.width, self.frame.origin.y, view.frame.size.width, view.frame.size.height);
  
      anchorPoint = CGPointMake(1, 0.5);
  }
  else {
      selfFrame.origin.x = self.frame.origin.x + view.bounds.size.width;
      view.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, view.frame.size.width, view.frame.size.height);
  
      anchorPoint = CGPointMake(0, 0.5);
  }
  
  UIGraphicsBeginImageContext(view.frame.size);
  [view.layer renderInContext:UIGraphicsGetCurrentContext()];
  UIImage *viewSnapShot = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  
  //set 3D depth
  CATransform3D transform = CATransform3DIdentity;
  transform.m34 = -1.0/800.0;
  CALayer *origamiLayer = [CALayer layer];
  origamiLayer.frame = view.bounds;
  origamiLayer.backgroundColor = [UIColor colorWithWhite:0.2 alpha:1].CGColor;
  origamiLayer.sublayerTransform = transform;
  [view.layer addSublayer:origamiLayer];
  
  
  
  
  
  
  //setup rotation angle
  double startAngle;
  CGFloat frameWidth = view.bounds.size.width;
  CGFloat frameHeight = view.bounds.size.height;
  CGFloat foldWidth = frameWidth/(folds*2);
  CALayer *prevLayer = origamiLayer;
  for (int b=0; b &lt; folds*2; b++) {
      CGRect imageFrame;
      if (direction == XYOrigamiDirectionFromRight) {
          if(b == 0)
              startAngle = -M_PI_2;
          else {
              if (b%2)
                  startAngle = M_PI;
              else
                  startAngle = -M_PI;
          }
          imageFrame = CGRectMake(frameWidth-(b+1)*foldWidth, 0, foldWidth, frameHeight);
      }
      else {
          if(b == 0)
              startAngle = M_PI_2;
          else {
              if (b%2)
                  startAngle = -M_PI;
              else
                  startAngle = M_PI;
          }
          imageFrame = CGRectMake(b*foldWidth, 0, foldWidth, frameHeight);
      }
      CATransformLayer *transLayer = [self transformLayerFromImage:viewSnapShot Frame:imageFrame Duration:duration AnchorPiont:anchorPoint StartAngle:startAngle EndAngle:0];
      [prevLayer addSublayer:transLayer];
      prevLayer = transLayer;
  }
  
  [CATransaction begin];
  [CATransaction setCompletionBlock:^{
      self.frame = selfFrame;
      [origamiLayer removeFromSuperlayer];
      XY_Origami_Current_State = XYOrigamiTransitionStateShow;
  
      if (completion)
          completion(YES);
  }];
  
  [CATransaction setValue:[NSNumber numberWithFloat:duration] forKey:kCATransactionAnimationDuration];
  CAAnimation *openAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position.x" function:openFunction fromValue:self.frame.origin.x+self.frame.size.width/2 toValue:selfFrame.origin.x+self.frame.size.width/2];
  openAnimation.fillMode = kCAFillModeForwards;
  [openAnimation setRemovedOnCompletion:NO];
  [self.layer addAnimation:openAnimation forKey:@"position"];
  [CATransaction commit];
  }
  </code></pre>
  
  <p>The method grabs a CATransform Layer from this method:</p>
  
  <pre><code>- (CATransformLayer *)transformLayerFromImage:(UIImage *)image Frame:(CGRect)frame Duration:(CGFloat)duration AnchorPiont:(CGPoint)anchorPoint StartAngle:(double)start EndAngle:(double)end;
  {
  CATransformLayer *jointLayer = [CATransformLayer layer];
  jointLayer.anchorPoint = anchorPoint;
  CGFloat layerWidth;
  if (anchorPoint.x == 0) //from left to right
  {
      layerWidth = image.size.width - frame.origin.x;
      jointLayer.frame = CGRectMake(0, 0, layerWidth, frame.size.height);
      if (frame.origin.x) {
          jointLayer.position = CGPointMake(frame.size.width, frame.size.height/2);
      }
      else {
          jointLayer.position = CGPointMake(0, frame.size.height/2);
      }
  }
  else 
  { //from right to left
      layerWidth = frame.origin.x + frame.size.width;
      jointLayer.frame = CGRectMake(0, 0, layerWidth, frame.size.height);
      jointLayer.position = CGPointMake(layerWidth, frame.size.height/2);
  }
  
  //map image onto transform layer
  CALayer *imageLayer = [CALayer layer];
  imageLayer.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);
  imageLayer.anchorPoint = anchorPoint;
  imageLayer.position = CGPointMake(layerWidth*anchorPoint.x, frame.size.height/2);
  [jointLayer addSublayer:imageLayer];
  CGImageRef imageCrop = CGImageCreateWithImageInRect(image.CGImage, frame);
  imageLayer.contents = (__bridge id)imageCrop;
  imageLayer.backgroundColor = [UIColor clearColor].CGColor;
  
  //add shadow
  NSInteger index = frame.origin.x/frame.size.width;
  double shadowAniOpacity;
  CAGradientLayer *shadowLayer = [CAGradientLayer layer];
  shadowLayer.frame = imageLayer.bounds;
  shadowLayer.backgroundColor = [UIColor darkGrayColor].CGColor;
  shadowLayer.opacity = 0.0;
  shadowLayer.colors = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor, (id)[UIColor clearColor].CGColor, nil];
  if (index%2) {
      shadowLayer.startPoint = CGPointMake(0, 0.5);
      shadowLayer.endPoint = CGPointMake(1, 0.5);
      shadowAniOpacity = (anchorPoint.x)?0.24:0.32;
  }
  else {
      shadowLayer.startPoint = CGPointMake(1, 0.5);
      shadowLayer.endPoint = CGPointMake(0, 0.5);
      shadowAniOpacity = (anchorPoint.x)?0.32:0.24;
  }
  [imageLayer addSublayer:shadowLayer];
  
  
  //animate open/close animation
  CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
  [animation setDuration:duration];
  [animation setFromValue:[NSNumber numberWithDouble:start]];
  [animation setToValue:[NSNumber numberWithDouble:end]];
  [animation setRemovedOnCompletion:NO];
  [jointLayer addAnimation:animation forKey:@"jointAnimation"];
  
  
  
  //animate shadow opacity
  animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
  [animation setDuration:duration];
  [animation setFromValue:[NSNumber numberWithDouble:(start)?shadowAniOpacity:0]];
  [animation setToValue:[NSNumber numberWithDouble:(start)?0:shadowAniOpacity]];
  [animation setRemovedOnCompletion:NO];
  [shadowLayer addAnimation:animation forKey:nil];
  
  return jointLayer;
  }
  </code></pre>
  
  <p>Basically, I need to remove the automatic animation, and control the progress of the effect using some manually set value (e.g.: uislider value, or content offset).</p>
  
  <p>Once again, any help provided is much appreciated!</p>