UIView 애니메이션을 취소 하시겠습니까?


244

UIView진행중인 애니메이션 을 취소 할 수 있습니까? 아니면 CA 수준으로 떨어 뜨려야합니까?

즉, 다음과 같은 작업을 수행했습니다 (아마도 애니메이션 동작을 설정했을 수도 있음).

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

그러나 애니메이션이 완료되고 애니메이션 종료 이벤트를 받기 전에 취소하고 싶습니다 (짧게 잘라 내고 싶습니다). 이게 가능해? 인터넷 검색을 통해 몇 사람이 대답없이 동일한 질문을하고 한 명 또는 두 명이 할 수 없다고 추측합니다.


중간에 애니메이션을 일시 중지하고 그대로 두는 것을 의미합니까? 아니면 처음에 있던 곳으로 돌아가는가?
Chris Lundie

2
정확한 위치에 애니메이션을 배치하거나 (실제로는 다른 애니메이션을 곧바로 시작할 것입니다) 끝점으로 바로 점프합니다. 첫 번째가 더 자연 스럽지만이 경우 나에게 효과적이라고 생각합니다.
philsquared

3
@Chris Hanson : 편집 해 주셔서 감사하지만 iPhone 태그를 제거하는 이유가 무엇인지 궁금합니다. 그것은 cocoa-touch에 의해 암시 될 수 있지만, 나는 iPhone에 태그 필터가 있고 그 경우 이것을 놓칠 것입니다.
philsquared

@Boot To The Head (다시) : 당신의 질문과 그것에 대한 나의 반응은 조금 더 테스트 해 보라는 메시지를 표시했으며 첫 번째 애니메이션이 끝나기 전에 새로운 애니메이션을 시작하면 끝점으로 점프합니다. 새로운 것을 시작하기 전에.
philsquared

-이것은 내 즉각적인 요구를 충족시키고 실제 코드에 다른 버그가 있음을 제안하지만 다른 사람들이 동일한 것을 요구한다는 것을 알기 때문에이 질문을 열어 두겠습니다.
philsquared

답변:


114

내가하는 방법은 끝점에 새 애니메이션을 만드는 것입니다. 매우 짧은 지속 시간을 설정 +setAnimationBeginsFromCurrentState:하고 현재 상태에서 시작 하는 방법을 사용하십시오 . YES로 설정하면 현재 애니메이션이 짧아집니다. 다음과 같이 보입니다.

[UIView beginAnimations:nil context:NULL];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:0.1];
[UIView setAnimationCurve: UIViewAnimationCurveLinear];
// other animation properties

// set view properties

[UIView commitAnimations];

고마워 Stephen. 당신이 내 질문에서 의견을 읽고 사실 경우이 내가 결국 짓을 정확히 -하지만 당신은 여기에 대한 명확한 좋은 계정을 준 이후로는로 표시하고있어 가능
philsquared

1
@Vojto 어떻게 그렇게? 문서에 따르면 setAnimationBeginsFromCurrentState:iOS 4 에서는 사용을 권장하지 않지만 여전히 존재하며 여전히 작동해야합니다.
Stephen Darlington

55
: 지연 : 옵션 : 애니메이션 : 완료 iOS4 +에서 UIViewAnimationOptionBeginFromCurrentState의 animateWithDuration에 옵션 사용
아담 Eberbach의

UIViewAnimationOptionBeginFromCurrentState가 진행중인 애니메이션이나 동일한 속성을 대상으로하는 애니메이션 만 취소합니까? 애니메이션 인 경우 진행중인 동일한 애니메이션을 중단하지 않고 같은 시점에 새로운 애니메이션을 계속 만들 수있는 방법은 무엇입니까?
zakdances

1
@yourfriendzak은 간단한 테스트를 기반으로 동일한 속성을 대상으로하는 애니메이션 만 취소하는 것으로 보입니다. contentOffset에 애니메이션이 적용되고 contentOffset 애니메이션이 계속되는 scrollView의 알파를 변경하려고했습니다.
arlomedia

360

사용하다:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

22
컴파일러 경고를 제거 하기 위해 #import <QuartzCore / QuartzCore.h> 를 추가해야한다는 것을 알았습니다 .)
deanWombourne

4
매우 간단하지만 찾기가 어렵습니다. 이것에 대해 감사합니다, 나는 이것을 알아 내려고 한 시간을 보냈습니다.
MikeyWard

4
이 솔루션의 가능한 문제는 (적어도 iOS 5에서는) 적용되기까지 지연이 있다는 것입니다. "다음 애니메이션으로 넘어 가기 전에 지금 애니메이션을 중지하십시오. 암호".
matt

14
+ (void) animateWithDuration : (NSTimeInterval) duration animations : (void (^) (void)) animations complete : (void (^) (BOOL finished)를 사용하면 호출하면 완료가 발생합니다. ^ (BOOL finished) 블록 ) 완료
JWGS

1
@ matt 애니메이션을 즉시 중단시키는 솔루션을 알고 있습니까? 애니메이션이 실제로 멈추기 전에 약간의 지연이 관찰되고 있습니다.
tiguero

62

특정 뷰에서 모든 애니메이션을 즉시 중지 하는 가장 간단한 방법 은 다음과 같습니다.

프로젝트를 QuartzCore.framework에 연결하십시오. 코드 시작시 :

#import <QuartzCore/QuartzCore.h>

이제 트랙에서 죽은 뷰의 모든 애니메이션을 중지하려면 다음과 같이 말합니다.

[CATransaction begin];
[theView.layer removeAllAnimations];
[CATransaction commit];

중간 라인은 모두 작동하지만 runloop가 완료 될 때까지 지연됩니다 ( "redraw moment"). 지연을 방지하려면 표시된대로 명시 적 트랜잭션 블록에 명령을 래핑하십시오. 현재 runloop에서이 계층에 대해 다른 변경이 수행되지 않은 경우 작동합니다.


2
코드가 잘 작동 한 후 [CATransaction flush]를 사용해 왔으며 때로는 즉시 쓰지 않습니다. 그러나 0.1 초의 성능 문제가 있습니다. 예를 들어 특정 시간 동안 지연됩니다. 해결 방법이 있습니까?
Sidwyn Koh

5
removeAllAnimations애니메이션을 현재 위치에서 멈추지 않고 최종 프레임으로 가져 오는 부작용이 있습니다.
Ilya

3
일반적으로 애니메이션을 중지하려면 첫 프레임이나 마지막 프레임이 아니라 현재 프레임에서 중지 될 것으로 예상합니다. 첫 번째 또는 마지막 프레임으로 점프하면 흔들리는 동작처럼 보입니다.
Ilya

1
그런 다음 기본 동작과 변경 방법을 지정하는 대답을 자세히 설명하겠습니다. 분명히 애니메이션을하는 누군가가 그의 메소드 호출에 따라 화면에서 일어나는 일에 적극적으로 관심을 갖고 있습니다. :)
Ilya

1
다른 곳에서 자세히 설명했습니다. 그것은 요청 된 것이 아니기 때문에 내가 여기서 대답 한 것이 아닙니다. 다른 답변이 있다면 반드시 답변으로 제공하십시오!
matt

48

iOS 4 이상에서는 두 번째 애니메이션 UIViewAnimationOptionBeginFromCurrentState옵션사용 하여 첫 번째 애니메이션을 짧게 자릅니다.

예를 들어, 활동 표시기가있는보기가 있다고 가정하십시오. 시간이 많이 걸리는 활동이 시작되는 동안 활동 표시기를 페이드하고 활동이 완료되면 페이드 아웃합니다. 아래 코드에서 활동 표시기가있는보기를 호출 activityView합니다.

- (void)showActivityIndicator {
    activityView.alpha = 0.0;
    activityView.hidden = NO;
    [UIView animateWithDuration:0.5
                 animations:^(void) {
                     activityView.alpha = 1.0;
                 }];

- (void)hideActivityIndicator {
    [UIView animateWithDuration:0.5
                 delay:0 options:UIViewAnimationOptionBeginFromCurrentState
                 animations:^(void) {
                     activityView.alpha = 0.0;
                 }
                 completion:^(BOOL completed) {
                     if (completed) {
                         activityView.hidden = YES;
                     }
                 }];
}

41

애니메이션을 취소하려면 UIView 애니메이션 외부에서 현재 애니메이션중인 속성을 설정하기 만하면됩니다. 그러면 애니메이션이 어디에 있든 중지되고 UIView가 방금 정의한 설정으로 이동합니다.


9
이것은 부분적으로 만 사실입니다. 애니메이션을 중지하지만 대리자 메소드, 특히 animationComplete 핸들러를 실행하는 것을 중지하지는 않습니다. 따라서 로직이 있으면 문제가 발생합니다.
M. Ryan

33
그것이 바로 '완료된'논쟁의 -animationDidStop:finished:context:대상입니다. 인 경우 [finished boolValue] == NO애니메이션이 어떻게 든 취소되었음을 알 수 있습니다.
Nick Forge

이것은 7 년 전의 훌륭한 팁입니다! 이상한 동작이 있습니다. 알파를 앞뒤로 움직여서 "0으로 가십시오"라고 말하십시오. 애니메이션을 중지하려면 알파를 0으로 설정할 수 없습니다. 당신은 그것을 1이라고 설정했습니다.
Fattie

26

이 답변을 부활시켜 죄송하지만 iOS 10에서는 약간 변경되었으며 이제 취소가 가능하며 정상적으로 취소 할 수도 있습니다!

iOS 10 이후에는 UIViewPropertyAnimator로 애니메이션을 취소 할 수 있습니다 !

UIViewPropertyAnimator(duration: 2, dampingRatio: 0.4, animations: {
    view.backgroundColor = .blue
})
animator.stopAnimation(true)

true를 전달하면 애니메이션이 취소되고 취소 한 위치에서 바로 중지됩니다. 완료 메소드는 호출되지 않습니다. 그러나 false를 전달하면 애니메이션을 완료해야합니다.

animator.finishAnimation(.start)

애니메이션을 끝내고 현재 상태 (.current)를 유지하거나 초기 상태 (.start) 또는 종료 상태 (.end)로 이동할 수 있습니다.

그건 그렇고, 나중에 일시 중지했다가 다시 시작할 수도 있습니다 ...

animator.pauseAnimation()
animator.startAnimation()

참고 : 갑작스러운 취소를 원하지 않으면 애니메이션을 일시 중지 한 후 애니메이션을 반전 시키거나 변경할 수도 있습니다!


2
이것은 현대 애니메이션과 가장 관련이 있습니다.
Doug

10

view 속성 대신 상수를 변경하여 제약 조건에 애니메이션을 적용하는 경우 다른 방법은 iOS 8에서 작동하지 않습니다.

애니메이션 예 :

self.constraint.constant = 0;
[self.view updateConstraintsIfNeeded];
[self.view layoutIfNeeded];
[UIView animateWithDuration:1.0f
                      delay:0.0f
                    options:UIViewAnimationOptionCurveLinear
                 animations:^{
                     self.constraint.constant = 1.0f;
                     [self.view layoutIfNeeded];
                 } completion:^(BOOL finished) {

                 }];

해결책:

구속 조건 변경 및 해당 하위 레이어의 영향을받는 뷰의 레이어에서 애니메이션을 제거해야합니다.

[self.constraintView.layer removeAllAnimations];
for (CALayer *l in self.constraintView.layer.sublayers)
{
    [l removeAllAnimations];
}

1
또한 self.constraintView.layer.removeAllAnimations()작동합니다 (Swift)
Lachtan

다른 솔루션은 효과가 없었습니다. 감사합니다.
swapnilagarwal


5

위의 어느 것도 나를 위해 해결하지 못했지만 이것은 도움이되었습니다. UIView애니메이션은 속성을 즉시 설정 한 다음 애니메이션을 적용합니다. 프리젠 테이션 레이어가 모델 (set 속성)과 일치하면 애니메이션이 중지됩니다.

나는 "당신이 보이는 것처럼 애니메이션을 만들고 싶습니다"라는 문제를 해결했습니다. 당신이 그것을 원한다면,

  1. QuartzCore를 추가하십시오.
  2. CALayer * pLayer = theView.layer.presentationLayer;

위치를 프리젠 테이션 레이어로 설정

나는 몇 가지 옵션을 사용한다. UIViewAnimationOptionOverrideInheritedDuration

그러나 Apple의 문서가 모호하기 때문에 사용시 다른 애니메이션을 실제로 재정의하는지 타이머를 재설정하는지 알 수 없습니다.

[UIView animateWithDuration:blah... 
                    options: UIViewAnimationOptionBeginFromCurrentState ... 
                    animations: ^ {
                                   theView.center = CGPointMake( pLayer.position.x + YOUR_ANIMATION_OFFSET, pLayer.position.y + ANOTHER_ANIMATION_OFFSET);
                   //this only works for translating, but you get the idea if you wanna flip and scale it. 
                   } completion: ^(BOOL complete) {}];

그리고 그것은 현재로서는 괜찮은 해결책이되어야합니다.


5
[UIView setAnimationsEnabled:NO];
// your code here
[UIView setAnimationsEnabled:YES];

2

Stephen Darlington 솔루션의 신속한 버전

UIView.beginAnimations(nil, context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(0.1)
// other animation properties

// set view properties
UIView.commitAnimations()

1

나도 같은 문제가있어; API에는 특정 애니메이션을 취소 할 것이 없습니다. 그만큼

+ (void)setAnimationsEnabled:(BOOL)enabled

모든 애니메이션을 비활성화하므로 작동하지 않습니다. 두 가지 해결책이 있습니다.

1) 애니메이션 객체를 하위 뷰로 만듭니다. 그런 다음 해당보기의 애니메이션을 취소하려면보기를 제거하거나 숨기십시오. 매우 간단하지만 뷰를 유지해야하는 경우 애니메이션없이 서브 뷰를 다시 작성해야합니다.

2) 애니메이션을 하나만 반복하고 필요한 경우 다음과 같이 델리게이트 선택기를 사용하여 애니메이션을 다시 시작하십시오.

-(void) startAnimation {
NSLog(@"startAnim alpha:%f", self.alpha);
[self setAlpha:1.0];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0];
[UIView setAnimationRepeatCount:1];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(pulseAnimationDidStop:finished:context:)];
[self setAlpha:0.1];
[UIView commitAnimations];
}

- (void)pulseAnimationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
if(hasFocus) {
    [self startAnimation];
} else {
    self.alpha = 1.0;
}
}

-(void) setHasFocus:(BOOL)_hasFocus {
hasFocus = _hasFocus;
if(hasFocus) {
    [self startAnimation];
}
}

2)의 문제는 현재 애니메이션 사이클이 끝날 때 애니메이션 정지를 지연시키는 것이 항상 지연된다는 것입니다.

도움이 되었기를 바랍니다.


1

위의 방법으로 애니메이션을 취소하더라도 didStopSelector여전히 애니메이션 이 실행됩니다. 따라서 애니메이션으로 구동되는 응용 프로그램에 논리 상태가 있으면 문제가 발생합니다. 이러한 이유로 위에서 설명한 방법으로 UIView애니메이션 의 컨텍스트 변수를 사용합니다 . 컨텍스트 매개 변수를 통해 프로그램의 현재 상태를 애니메이션에 전달하는 경우 애니메이션이 중지되면 didStopSelector함수가 현재 상태와 컨텍스트로 전달 된 상태 값을 기반으로 무언가를 수행해야하는지 아니면 반환해야하는지 결정할 수 있습니다.


NickForge는 TomTaylor의 정답 아래에있는 주석에서이를 완벽하게 설명합니다 ...
Fattie

이 쪽지 감사합니다! 실제로 animationDidStop이 호출 될 때 다음 애니메이션이 시작되는 상황이 있습니다. 단순히 애니메이션을 제거하고 동일한 키에 대해 새 애니메이션을 즉시 다시 추가하면 작동하지 않습니다. 예를 들어 animationDidStop이 다른 애니메이션 또는 다른 항목을 트리거 할 때까지 기다려야합니다. 방금 제거 된 애니메이션은 더 이상 animationDidStop을 시작하지 않습니다.
RickJansen '

0
CALayer * pLayer = self.layer.presentationLayer;
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView animateWithDuration:0.001 animations:^{
    self.frame = pLayer.frame;
}];

0

상태를 원래 또는 최종으로 되 돌리지 않고 애니메이션을 일시 중지하려면

CFTimeInterval paused_time = [myView.layer convertTime:CACurrentMediaTime() fromLayer:nil];
myView.layer.speed = 0.0;
myView.layer.timeOffset = paused_time;

0

UIStackView애니메이션으로 작업 할 때 예측할 수없는 상태로 설정할 수 있으므로 removeAllAnimations()일부 값을 초기 값으로 removeAllAnimations()설정해야합니다. 나는이 stackView함께 view1하고 view2내부 및 하나 개의보기가 표시되어야 하나가 숨겨진 :

public func configureStackView(hideView1: Bool, hideView2: Bool) {
    let oldHideView1 = view1.isHidden
    let oldHideView2 = view2.isHidden
    view1.layer.removeAllAnimations()
    view2.layer.removeAllAnimations()
    view.layer.removeAllAnimations()
    stackView.layer.removeAllAnimations()
    // after stopping animation the values are unpredictable, so set values to old
    view1.isHidden = oldHideView1 //    <- Solution is here
    view2.isHidden = oldHideView2 //    <- Solution is here

    UIView.animate(withDuration: 0.3,
                   delay: 0.0,
                   usingSpringWithDamping: 0.9,
                   initialSpringVelocity: 1,
                   options: [],
                   animations: {
                    view1.isHidden = hideView1
                    view2.isHidden = hideView2
                    stackView.layoutIfNeeded()
    },
                   completion: nil)
}

0

대답 된 해결책 중 어느 것도 나를 위해 일하지 않았습니다. 이 방법으로 문제를 해결했습니다 (올바른 방법인지 모르겠습니까?). 너무 빨리 호출 할 때 문제가 발생했기 때문에 (이전 애니메이션이 아직 완료되지 않은 경우). customAnim 블록으로 원하는 애니메이션을 전달 합니다.

extension UIView
{

    func niceCustomTranstion(
        duration: CGFloat = 0.3,
        options: UIView.AnimationOptions = .transitionCrossDissolve,
        customAnim: @escaping () -> Void
        )
    {
        UIView.transition(
            with: self,
            duration: TimeInterval(duration),
            options: options,
            animations: {
                customAnim()
        },
            completion: { (finished) in
                if !finished
                {
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    // NOTE: This fixes possible flickering ON FAST TAPPINGS
                    self.layer.removeAllAnimations()
                    customAnim()
                }
        })

    }

}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.