1

我有一个动画,可以为 2 个视图的位置pageLeftpageRightalpha 设置动画,总共有 4 个同步 CABasicAnimations:

  1. 将下一页移到视图中
  2. 将当前页面移出视图
  3. 淡入下一页
  4. 淡出当前页面

一切都适用于动画和设置。这个问题是关于页面动画之后的鼠标事件。我发现动画运行后,nextView 没有收到mouseDown:事件。如果动画没有运行,因为shouldNotAnimate块在下面运行,nextView 接收mouseDown:事件。

这意味着关于我设置我CABasicAnimation的 s 的方式并CATransaction导致了这种情况。我很困惑它可能是什么。有任何想法吗?


动画代码:

BOOL forward = currentIndex < index;

if (shouldNotAnimate) {
    // handles swapping pages faster than the animation duration
    [self.sourceViewContainer setSubviews:[NSArray array]];

    [nextView.layer removeAllAnimations];
    [nextView setFrame:self.sourceViewContainer.bounds];
    [nextView.layer setPosition:self.sourceViewContainer.bounds.origin];
    [nextView.layer setOpacity:1.0];
    [self.sourceViewContainer addSubview:nextView];

    [currentView removeFromSuperview];      
    [currentView.layer removeAllAnimations];
    [currentView setFrame:self.sourceViewContainer.bounds];
    [currentView.layer setPosition:self.sourceViewContainer.bounds.origin];
    [currentView.layer setOpacity:1.0];

    self.animationsRunning = NO;

} else {

    self.animationsRunning = YES;

    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        self.animationsRunning = NO;
    }];

    // Setup incoming push animation
    [nextView setFrame:CGRectMake(self.sourceViewContainer.bounds.size.width * (forward ? 1 : -1), 0, self.sourceViewContainer.bounds.size.width, self.sourceViewContainer.bounds.size.width)];
    [self.sourceViewContainer addSubview:nextView];
    CABasicAnimation *incomingAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    incomingAnimation.fromValue = [NSValue valueWithPoint:nextView.frame.origin];
    incomingAnimation.toValue = [NSValue valueWithPoint:self.sourceViewContainer.bounds.origin];
    incomingAnimation.duration = PANEL_PAGE_SWIPE_ANIMATION_DURATION;
    incomingAnimation.removedOnCompletion = NO;
    incomingAnimation.fillMode = kCAFillModeForwards;
    incomingAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    incomingAnimation.completion = ^(BOOL finished) {
        [nextView.layer setPosition:self.sourceViewContainer.bounds.origin];
        [nextView.layer removeAnimationForKey:@"incoming"];
    };

    [nextView.layer addAnimation:incomingAnimation forKey:@"incoming"];


    // Setup outgoing push animation
    CGRect offscreen = CGRectMake(self.sourceViewContainer.bounds.size.width * (forward ? -1 : 1), 0, self.sourceViewContainer.bounds.size.width, self.sourceViewContainer.bounds.size.height);
    CABasicAnimation *outgoingAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    outgoingAnimation.fromValue = [NSValue valueWithPoint:self.sourceViewContainer.bounds.origin];
    outgoingAnimation.toValue = [NSValue valueWithPoint:offscreen.origin];
    outgoingAnimation.duration = PANEL_PAGE_SWIPE_ANIMATION_DURATION;
    outgoingAnimation.removedOnCompletion = NO;
    outgoingAnimation.fillMode = kCAFillModeForwards;
    outgoingAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    outgoingAnimation.completion = ^(BOOL finished) {
        [currentView removeFromSuperview];
        [currentView.layer setPosition:self.sourceViewContainer.bounds.origin];
        [currentView.layer removeAnimationForKey:@"outgoing"];

    };
    [currentView.layer addAnimation:outgoingAnimation forKey:@"outgoing"];


    // Setup incoming alpha animation
    CABasicAnimation *fadeInAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeInAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    fadeInAnimation.toValue = [NSNumber numberWithFloat:1.0f];
    fadeInAnimation.duration = PANEL_PAGE_SWIPE_ANIMATION_DURATION;
    fadeInAnimation.removedOnCompletion = NO;
    fadeInAnimation.fillMode = kCAFillModeForwards;
    fadeInAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    fadeInAnimation.completion = ^(BOOL finished) {
        [nextView.layer setOpacity:1.0f];
        [nextView.layer removeAnimationForKey:@"fadeIn"];
    };
    [nextView.layer addAnimation:fadeInAnimation forKey:@"fadeIn"];


    // Setup outgoing alpha animation
    CABasicAnimation *fadeOutAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    fadeOutAnimation.fromValue = [NSNumber numberWithFloat:1.0f];
    fadeOutAnimation.toValue = [NSNumber numberWithFloat:0.0f];
    fadeOutAnimation.duration = PANEL_PAGE_SWIPE_ANIMATION_DURATION;
    fadeOutAnimation.removedOnCompletion = NO;
    fadeOutAnimation.fillMode = kCAFillModeForwards;
    fadeOutAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    fadeOutAnimation.completion = ^(BOOL finished) {
        [currentView removeFromSuperview];
        [currentView.layer setOpacity:1.0f];
        [currentView.layer removeAnimationForKey:@"fadeOut"];

    };
    [currentView.layer addAnimation:fadeOutAnimation forKey:@"fadeOut"];

    [CATransaction commit];

} // Run animations

解决方案

我必须在完成块中显式设置视图框架,即使我在动画调用之前就已经这样做了。

    [CATransaction begin];
    [CATransaction setCompletionBlock:^{
        self.animationsRunning = NO;
        [nextView setFrame:self.sourceViewContainer.bounds];
    }];
4

2 回答 2

1

我想我知道发生了什么。

CAAnimation 对象实际上并没有改变它们动画的底层属性。相反,它将属性应用于表示层,而不是常规层。当您使用 removedOnCompletion=false 创建动画时,表示层会在动画完成后保持活动状态,但底层属性仍不会更改。另外,移动视图的层并不会移动视图本身,因此它不会响应用户在新位置的交互。

我建议使用 UIView 块动画(UIView animateWithDuration:completion: and it's relatives)而不是使用 CAAnimation 对象来移动您的视图。这些方法实际上将视图移动到它的新位置,以便在动画完成后响应最终位置的用户交互。

您可以在一个动画块中完成所有移动和 alpha 更改。

于 2012-12-01T01:43:19.153 回答
0

我曾经遇到过 iOS 的问题,其中 UIMapView 因为调用地图视图而在动画后无响应。动画完成后,不知何故 userInteractionEnable 被设置为 FALSE。

如果是这种情况,您可以做的测试是断言 userInteractionEnable 的值为 TRUE。

assert(view.userInteractionEnable != FALSE); // ensure user interaction is enabled

您可能会发现,当您运行应用程序时,它会在此断言上中断。如果是这样,您只需将其设置为 TRUE,您的应用程序就会按预期运行。

至于我的 UIMapView 问题,在 iOS 版本更新后问题就消失了,因此很可能是由边缘情况引起的错误。我提交了一个关于该问题的错误,这可能有助于提高对意外行为的认识。

于 2012-11-30T19:51:53.690 回答