核心动画互动翻转过渡就像前后扑克牌一样

时间:2014-07-10 17:02:06

标签: ios objective-c animation core-animation

我正在尝试使用Core Animation重新创建Apple的UIView transitionFromView:ToView:,以便我可以将其用于交互式视图控制器转换。它几乎就在那里,但我无法让“到”视图控制器的视图显示在卡的背面。

CATransform3D inRotation = CATransform3DMakeRotation(M_PI, 0.0, 1.0, 0.0);
inRotation.m34 = -0.02;
CATransform3D outRotation = CATransform3DMakeRotation(-0.01, 0.0, 1.0, 0.0);
outRotation.m34 = -0.02;

[UIView animateKeyframesWithDuration:[self transitionDuration:transitionContext] delay:0.0 options:0 animations:^{
    [UIView addKeyframeWithRelativeStartTime:0.0
                            relativeDuration:0.5
                                  animations:^{
                                      fromViewController.view.layer.transform = inRotation;
                                  }];
    [UIView addKeyframeWithRelativeStartTime:0.5
                            relativeDuration:0.5
                                  animations:^{
                                      toViewController.view.layer.transform = outRotation;
                                  }];
} completion:^(BOOL finished) {
    [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];

我认为我需要在转换的中途以某种方式交换两个视图(fromViewController.viewtoViewController.view),但我似乎无法在任何地方找到解决方案。

3 个答案:

答案 0 :(得分:1)

我不确定这是否是最常用的方法,但我有一个有效的解决方案:

[fromViewController.view.layer setDoubleSided:NO];
[toViewController.view.layer setDoubleSided:NO];

[container insertSubview:toViewController.view belowSubview:fromViewController.view];

CGFloat h = 0.0001;

CATransform3D initialToRotation = CATransform3DMakeRotation(M_PI - h, 0.0, 1.0, 0.0);
initialToRotation.m34 = -0.002;
[toViewController.view.layer setTransform:initialToRotation];

CATransform3D initialFromRotation = CATransform3DIdentity;
initialFromRotation.m34 = -0.002;
[fromViewController.view.layer setTransform:initialFromRotation];

[UIView animateWithDuration:0.5 animations:^{
    [fromViewController.view.layer setTransform:CATransform3DRotate(initialFromRotation, M_PI - h, 0.0, 1.0, 0.0)];
    [toViewController.view.layer setTransform:CATransform3DRotate(initialToRotation, M_PI + h, 0.0, 1.0, 0.0)];
} completion:^(BOOL finished) {
    BOOL wasCanceled = [transitionContext transitionWasCancelled];
    if (wasCanceled) {
        [transitionContext completeTransition:NO];
    } else {
        [transitionContext completeTransition:YES];
    }
}];

重要的是前后两者需要一起旋转,前部需要在过渡之前旋转。您可以在容器视图中设置视图层次结构,但在viewControllers'上调用转换代码。直接观点。此外,CoreAnimation似乎没有任何顺时针或逆时针的概念,因此您需要按 over pi弧度的顺序旋转确保正确的行为。

答案 1 :(得分:0)

我有一个项目可以做到这一点。这是我们用于这个项目的内容。它不是CoreAnimation,但可以完成工作。

-(void)configureView{

    UITapGestureRecognizer *singleFingerTap =
    [[UITapGestureRecognizer alloc] initWithTarget:self
                                            action:@selector(handleSingleTap:)];


    _contentsView = [[UIView alloc] initWithFrame:CGRectInset(self.view.bounds, 0, 0)];
    _contentsView.backgroundColor = RGB(GRAY_BACKGROUND);
    _contentsView.layer.cornerRadius = 0;
    _contentsView.layer.masksToBounds = YES;
    [_contentsView.layer setBorderColor: [RGB(DARK_GRAY) CGColor]];
    [_contentsView.layer setBorderWidth: .5];
    [_contentsView addGestureRecognizer:singleFingerTap];
    [self.view addSubview:_contentsView];

    CardFrontController *front = [CardFrontController new];
    [front.view setFrame:CGRectMake(0, 0, 300, 300)];
    [_contentsView addSubview:front.view];

    _backView = [[UIView alloc] initWithFrame:CGRectInset(self.view.bounds, 0, 0)];

    // load the back of the card
    cardBack = [CardBackController new];
    [_backView addSubview:cardBack.view];

}

- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {

    // animate the card flipping
    [UIView beginAnimations:nil context:NULL];
    [UIView setAnimationDuration:0.5];
    if (!_review) {
        [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];
    } else {
        [UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];
    }
    [UIView commitAnimations];

    // now stuff that we don't need to animate
    if (!_review) {
        [_contentsView removeFromSuperview];
        [self.view addSubview:_backView];
    }
    else {
        [_backView removeFromSuperview];
        [self.view addSubview:_contentsView];
    }

    _review = !_review;
}

答案 2 :(得分:0)

您的inRotation应该与M_PI_2(或-M_PI_2)代替M_PI。以下解决方案更接近您的原始代码,它通过执行两个四分之一轮换避免了刚刚超过pi或的hacky解决方法:

func flip(_ flipToFront: Bool) {
    let fromView: UIView = flipToFront ? toViewController.view! : fromViewController.view!
    let toView: UIView = flipToFront ? fromViewController.view! : toViewController.view!

    fromView.superview!.insertSubview(fromView, belowSubview: toView)

    var initialToRotation: CATransform3D = CATransform3DMakeRotation(.pi, 0, 1, 0)
    initialToRotation.m34 = -0.004
    toView.layer.transform = initialToRotation

    var initialFromRotation: CATransform3D = CATransform3DIdentity
    initialFromRotation.m34 = -0.004
    fromView.layer.transform = initialFromRotation

    UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: .calculationModeLinear, animations: {
        UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5, animations: {
            fromView.layer.transform = CATransform3DRotate(initialFromRotation, .pi / (flipToFront ? 2 : -2), 0, 1, 0)
            toView.layer.transform = CATransform3DRotate(initialToRotation, .pi / (flipToFront ? -2 : 2), 0, 1, 0)
        })
        UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5, animations: {
            fromView.layer.transform = CATransform3DRotate(initialFromRotation, .pi, 0, 1, 0)
            toView.layer.transform = CATransform3DRotate(initialToRotation, .pi, 0, 1, 0)
        })
    }, completion: nil)
}

我之前设定的地方:

override func viewDidLoad() {
    super.viewDidLoad()

    fromViewController.view.layer.isDoubleSided = false
    toViewController.view.layer.isDoubleSided = false
}