2012-12-15 7 views
12

버튼을 누르고 있으면 블록이 애니메이션으로 나타나고 해제하면 애니메이션이 원래 위치로 되돌아갑니다.하지만 애니메이션을 얻을 수 없습니다. 무엇이든 상관없이 애니메이션 블록의 현재 위치.애니메이션 도중 CALayer의 현재 위치를 가져올 수 없습니다.

-(IBAction)moveDown:(id)sender{ 

    CGRect position = [[container.layer presentationLayer] frame]; 

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)]; 

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)]; 

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    moveAnim.path = movePath.CGPath; 
    moveAnim.removedOnCompletion = NO; 
    moveAnim.fillMode = kCAFillModeForwards; 


    CAAnimationGroup *animGroup = [CAAnimationGroup animation]; 
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil]; 
    animGroup.duration = 2.0; 
    animGroup.removedOnCompletion = NO; 
    animGroup.fillMode = kCAFillModeForwards; 

    [container.layer addAnimation:animGroup forKey:nil]; 
} 
-(IBAction)moveUp:(id)sender{ 
    CGRect position = [[container.layer presentationLayer] frame]; 

    UIBezierPath *movePath = [UIBezierPath bezierPath]; 

    [movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)]; 

    [movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)]; 

    CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    moveAnim.path = movePath.CGPath; 
    moveAnim.removedOnCompletion = NO; 
    moveAnim.fillMode = kCAFillModeForwards; 

    CAAnimationGroup *animGroup = [CAAnimationGroup animation]; 
    animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil]; 
    animGroup.duration = 2.0; 
    animGroup.removedOnCompletion = NO; 
    animGroup.fillMode = kCAFillModeForwards; 

    [container.layer addAnimation:animGroup forKey:nil]; 

} 

그러나 라인

CGRect position = [[container.layer presentationLayer] frame]; 

는 대상 위치가 아닌 현재의 위치를 ​​반환 : 여기 내 코드입니다. 기본적으로 버튼을 놓으면 움직이는 컨테이너의 현재 위치를 기본적으로 제공해야하므로 다음 애니메이션을 수행 할 수 있습니다. 내가 지금 가지고있는 것은 효과가 없습니다.

답변

43

코드를 100 % 확신 할만큼 충분히 분석하지 못했지만 왜 [[container.layer presentationLayer] frame]이 예상 한 결과를 반환하지 않을 수 있습니다. 하지만 몇 가지 문제가 있습니다.

하나의 명백한 문제는 moveDown:movePath을 선언하지 않는다는 것입니다. movePath이 인스턴스 변수 인 경우 moveDown:이 호출 될 때마다 인스턴스를 지우거나 새 인스턴스를 만들고 싶지만 그렇게하는 것은 아닙니다.

덜 분명한 문제는 (presentationLayer을 사용 했음에도 불구하고 removedOnCompletionfillMode을 사용했음을 감안할 때) 분명히 코어 애니메이션의 작동 방식을 이해할 수 없다는 것입니다. 이것은 놀랍게도 공통적 인 것으로 밝혀 지므로 내가 틀렸다면 저를 용서하십시오. 어쨌든, Core Animation이 어떻게 작동하는지 그리고 문제를 해결하는 방법을 설명 할 것이기 때문에 계속 읽어보십시오.

코어 애니메이션에서 평소 사용하는 레이어 개체는 모델 레이어입니다. 애니메이션을 레이어에 첨부하면 Core Animation은 표현 레이어이라는 모델 레이어의 복사본을 만들고 애니메이션은 시간이 지남에 따라 프리젠 테이션 레이어의 속성을 변경합니다. 애니메이션은 모델 레이어의 속성을 변경하지 않습니다.

애니메이션이 끝나고 (기본적으로) 제거되면 프리젠 테이션 레이어가 삭제되고 모델 레이어의 속성 값이 다시 적용됩니다. 따라서 화면의 레이어는 원래 위치/색상/무엇이든 "뒤로 물러"것입니다. 이 문제를 해결하는

일반적인하지만 잘못된 방법은 애니메이션의 removedOnCompletionNOfillModekCAFillModeForwards로 설정하는 것입니다. 이렇게하면 프리젠 테이션 레이어가 멈추므로 화면에 "뒤로 물러서"는 없습니다. 문제는 이제 프레젠테이션 레이어가 모델 레이어와 다른 값으로 매달려 있다는 것입니다. 애니메이션 속성의 값에 대해 모델 계층 (또는 모델을 소유 한 뷰)에 요청하면 화면에 표시된 값과 다른 값을 얻게됩니다. 그리고 속성을 다시 애니메이션하려고하면 애니메이션이 잘못된 위치에서 시작될 것입니다.

레이어 속성에 애니메이션을 적용하고 "고정"시키려면 모델 레이어의 속성 값을 변경 한 다음 애니메이션을 적용해야합니다. 그런 식으로 애니메이션이 제거되고 프리젠 테이션 레이어가 사라지면 모델 레이어는 애니메이션이 끝났을 때의 프리젠 테이션 레이어와 동일한 속성 값을 가지기 때문에 화면의 레이어는 완전히 똑같을 것입니다.

지금 저는 왜 직선 운동을 애니메이트하기 위해 키 프레임을 사용하는지 또는 왜 애니메이션 그룹을 사용하는지 알지 못합니다. 어느 쪽도 여기에 필요하지 않습니다.그리고 당신의 두 가지 방법이 거의 동일하다, 그래서 공통 코드를 고려하자 : 나는 레이어에 추가 할 때 우리는 애니메이션에게 키를 제공하고

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    animation.fromValue = [NSValue valueWithCGPoint:fromValue]; 
    animation.toValue = [NSValue valueWithCGPoint:toValue]; 
    animation.duration = 2; 
    [layer addAnimation:animation forKey:animation.keyPath]; 
} 

공지 사항. 매번 동일한 키를 사용하기 때문에 이전 애니메이션이 아직 완성되지 않은 경우 각각의 새 애니메이션은 이전 애니메이션을 대체 (제거)합니다. 물론

, 바로이 함께 연주, 당신은 moveDown:이 절반 만 완료되면 여전히 이초의 기간을 가지고 있기 때문에 당신이 moveUp:moveUp: 애니메이션 절반 속도로 표시됩니다 경우 찾을 수 있습니다 그러나 단지 절반까지만 여행 할 수 있습니다. 우리는 정말 거리에 따라 기간을 계산해야 할 것은 여행한다 : 당신이 정말가 애니메이션 그룹의 키 패스 애니메이션으로 필요한 경우 당신이 그 일을해야하는 이유

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; 
    animation.fromValue = [NSValue valueWithCGPoint:fromValue]; 
    animation.toValue = [NSValue valueWithCGPoint:toValue]; 
    animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY); 
    [layer addAnimation:animation forKey:animation.keyPath]; 
} 

, 당신의 질문은 우리에게 를 표시해야합니다 . 어쨌든, 너무 그런 것들과 함께 작동 : 당신은 내 테스트 프로그램 in this gist의 전체 코드를 찾을 수 있습니다

- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY { 
    CGPoint fromValue = [layer.presentationLayer position]; 
    CGPoint toValue = CGPointMake(fromValue.x, y); 

    layer.position = toValue; 

    UIBezierPath *path = [UIBezierPath bezierPath]; 
    [path moveToPoint:fromValue]; 
    [path addLineToPoint:toValue]; 

    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
    animation.path = path.CGPath; 
    animation.duration = 2.0 * (toValue.y - fromValue.y)/(y - baseY); 

    CAAnimationGroup *group = [CAAnimationGroup animation]; 
    group.duration = animation.duration; 
    group.animations = @[animation]; 
    [layer addAnimation:group forKey:animation.keyPath]; 
} 

. 새로운 Single View Application 프로젝트를 만들고 ViewController.m의 내용을 요점의 내용으로 바꿉니다.

+0

대단히 감사합니다. 나는 애니메이션을 처음 사용하기 때문에 톤을 도왔다. 나는 그것을 발사 할 것이다! – Matt

+4

WWDC 2011의 [Session 421 - Core Animation Essentials] (https://developer.apple.com/videos/wwdc/2011/?id=421) 비디오를 시청하십시오. 단지 1 시간이며 ** 매우 **합니다 유익한. –

+0

굉장한 답변 +1! – Till