Bir UIView animasyonu iptal edilsin mi?


244

Bir UIViewanimasyonu devam ederken iptal etmek mümkün mü ? Yoksa CA seviyesine düşmek zorunda mıyım?

yani ben böyle bir şey yaptım (belki de bir animasyon sonu eylemi ayarlama):

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

// set view properties

[UIView commitAnimations];

Ama animasyon tamamlanmadan ve animasyon sona erdi olayı almadan önce, iptal etmek istiyorum (kısa kes). Mümkün mü? Etrafta dolaşmak, aynı soruyu cevap vermeyen birkaç kişi ve bunun yapılamayacağını düşünen bir veya iki kişi bulur.


Animasyonu ortada duraklatmak ve orada bırakmak mı demek istiyorsun? Ya da başlangıçta olduğu yere mi dönüyorsunuz?
Chris Lundie

2
Ya tam olarak olduğu yerde bırakarak, animasyonun ortasında (pratikte zaten başka bir animasyona başlayacağım) ya da doğrudan bitiş noktasına atlayın. Birincisinin daha doğal olduğunu hayal edebilirim, ama ya bu durumda benim için çalışıyor.
philsquared

3
@Chris Hanson: Düzenlemelerinizi takdir ediyorum, ancak iPhone etiketini kaldırmak için mantığınızın ne olduğunu merak ediyorum. Kakao dokunuşuyla ima edilebilir, ancak biri için iPhone'da bir etiket filtresi var ve bu durumda bunu özleyeceğim.
philsquared

@Başta (tekrar): sorunuz ve kendi cevabım, biraz daha fazla tecrit etmemi istedi ve ilk animasyon bitmeden yeni bir animasyona başlarsanız, bitiş noktasına atladığını keşfettim yenisine başlamadan önce.
philsquared

- Bu benim acil ihtiyaçlarını karşılar (ve gerçek kodumda başka bir hata var olduğunu gösterir), ama ben başkalarının da aynı şeyi istediğini bildiğim için bu soruyu açık bırakacağım.
philsquared

Yanıtlar:


114

Bunu yapmamın yolu, bitiş noktanıza yeni bir animasyon oluşturmak. Çok kısa bir süre ayarlayın +setAnimationBeginsFromCurrentState:ve geçerli durumdan başlamak için yöntemi kullandığınızdan emin olun . EVET olarak ayarladığınızda, mevcut animasyon kısa kesilir. Şuna benzer bir şey var:

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

// set view properties

[UIView commitAnimations];

Teşekkürler Stephen. Aslında sorumun altındaki yorumları okursanız, sonunda yaptığım tam olarak budur - ama burada iyi, net bir hesap verdiğiniz için bunu kabul edilmiş olarak işaretliyorum
philsquared

1
@Vojto Nasıl? Dokümanlar, kullanımının setAnimationBeginsFromCurrentState:iOS 4'ten vazgeçtiğini söylüyor, ancak hala orada ve hala çalışması gerekiyor.
Stephen Darlington

55
iOS4 + 'da, animateWithDuration: delay: options: animasyonlar: tamamlanma:
Adam Eberbach

UIViewAnimationOptionBeginFromCurrentState, devam etmekte olan herhangi bir animasyonu veya yalnızca aynı özelliği hedefleyen bir animasyonu iptal eder mi? Herhangi bir animasyon varsa, devam eden aynı animasyonu durdurmadan yeni bir animasyona aynı noktada devam etmesini nasıl sağlayabilirsiniz?
zakdances

1
@yourfriendzak, kısa bir teste dayanarak, sadece aynı mülkü hedefleyen bir animasyonu iptal ettiği anlaşılıyor. ContentOffset animasyonlu ve contentOffset animasyonu devam eden scrollView alfa değiştirmeyi denedim.
arlomedia

360

kullanın:

#import <QuartzCore/QuartzCore.h>

.......

[myView.layer removeAllAnimations];

22
Ben eklemek zorunda olduğu tespit #import <QuartzCore / QuartzCore.h> bir derleyici uyarıyı kaldırmak için;)
deanWombourne

4
Çok basit ve bulmak çok zor. Bunun için teşekkürler, bunu anlamaya çalışmak için sadece bir saatimi harcadım.
MikeyWard

4
Bu çözümle ilgili olası sorun, (en azından iOS 5'te) yürürlüğe girmeden önce bir gecikme olmasıdır - demek istediğim, bir sonraki satıra geçmeden önce şu anda animasyonu durdur kod".
matt

14
+ (Void) animateWithDuration: (NSTimeInterval) süresi animasyonları kullanılıyorsa, bu çağrının tamamlanma: ^ (BOOL bitmiş) bloğunu tetiklediğini buldum: (void (^) (void)) animasyonlar tamamlanma: (void (^) (BOOL bitti) ) tamamlanması
JWGS

1
@matt Animasyonu hemen durduran bir çözüm biliyor musunuz? Ben animasyon gerçekten durmadan önce çok küçük bir gecikme gözlemliyorum.
tiguero

62

Durdurmak için yol basit tüm bir görünümüyle ilgili animasyonlar, hemen şudur ki:

Projeyi QuartzCore.framework'e bağlayın. Kodunuzun başında:

#import <QuartzCore/QuartzCore.h>

Şimdi, parçalarında ölü bir görünümdeki tüm animasyonları durdurmak istediğinizde şunu söyleyin:

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

Orta çizgi tek başına çalışır, ancak çalışma döngüsü bitene kadar bir gecikme olur ("yeniden çizim anı"). Bu gecikmeyi önlemek için komutu gösterildiği gibi açık bir işlem bloğuna sarın. Geçerli çalışma döngüsünde bu katmanda başka değişiklik yapılmadığı sürece bu işler.


2
Harika çalışan kodunuzdan sonra [CATransaction flush] kullanıyorum, bazen hemen yazmıyor. Ancak, 0.1 saniyelik bir performans sorunu var, örneğin belirli bir süre için gecikiyor. Herhangi bir geçici çözüm var mı?
Sidwyn Koh

5
removeAllAnimationsşu anda olduğu yerde durdurmak yerine animasyonu son karesine getirmenin yan etkisi olacak.
Ilya

3
Normalde, bir animasyonun durmasını istediğinde, ilk karede veya son karede değil, geçerli karesinde durmasını beklerler. İlk veya son kareye atlamak sarsıntılı bir hareket gibi görünecektir.
Ilya

1
Yanıtın ayrıntılarını ve varsayılan davranışın ne olduğunu ve nasıl değiştirilebileceğini belirtmek için ayrıntıya gireceğim. Animasyon yapan biri, yöntem çağrılarını takiben ekranda neler olduğuna aktif olarak ilgi duyuyor :)
Ilya

1
Başka bir yerde detaylandırdım. Sorulan şey bu değil, bu yüzden burada cevapladığım şey değil. Başka bir cevabınız varsa, kesinlikle bir cevap olarak verin!
matt

48

İOS 4 ve sonraki sürümlerde , ilk animasyonu kısa kesmek için ikinci animasyondaki seçeneği kullanınUIViewAnimationOptionBeginFromCurrentState .

Örnek olarak, aktivite göstergeli bir görünümünüz olduğunu varsayalım. Potansiyel olarak zaman alan bir etkinlik başlarken etkinlik göstergesinde kaybolmak ve etkinlik bittiğinde kaybolmak istersiniz. Aşağıdaki kodda, üzerinde etkinlik göstergesi olan görünüm çağrılır 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

Bir animasyonu iptal etmek için UIView animasyonunun dışında, o anda canlandırılmakta olan özelliği ayarlamanız yeterlidir. Bu, animasyonu nerede olursa olsun durduracak ve UIView az önce tanımladığınız ayara atlayacaktır.


9
Bu sadece kısmen doğrudur. Animasyonu durdurur, ancak delege yöntemlerini, özellikle de animationComplete işleyicisini ateşlemesini durdurmaz, bu yüzden orada mantığınız varsa, sorunları görürsünüz.
M.Ryan

33
'Bitmiş' argümanının -animationDidStop:finished:context:amacı budur. Eğer [finished boolValue] == NOöyleyse, animasyonunuzun bir şekilde iptal edildiğini biliyorsunuzdur.
Nick Forge

Bu 7 yıl önce harika bir ipucu! garip bir davranış olduğunu unutmayın: alfa'yı öne ve arkaya canlandırdığınızı ve bunu "0'a" yaptığınızı varsayalım. animasyonu DURDURMAK için alfa sıfıra ayarlanamaz; 1 olarak ayarladınız.
Fattie

26

Bu cevabı dirilttiğim için üzgünüm, ancak iOS 10'da biraz şey değişti ve şimdi iptal etmek mümkün ve hatta zarif bir şekilde iptal edebilirsiniz!

İOS 10'dan sonra UIViewPropertyAnimator ile animasyonları iptal edebilirsiniz !

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

True değerini geçerseniz, animasyonu iptal eder ve iptal ettiğiniz yerde durur. Tamamlama yöntemi çağrılmaz. Ancak, yanlış iletirseniz animasyonu bitirmekten siz sorumlusunuz:

animator.finishAnimation(.start)

Animasyonunuzu bitirebilir ve geçerli durumda (.current) kalabilir veya başlangıç ​​durumuna (.start) veya bitiş durumuna (.end) gidebilirsiniz.

Bu arada, daha sonra duraklatabilir ve yeniden başlatabilirsiniz ...

animator.pauseAnimation()
animator.startAnimation()

Not: Ani bir iptal istemiyorsanız animasyonunuzu tersine çevirebilir veya hatta duraklattıktan sonra animasyonunuzu değiştirebilirsiniz!


2
Bu, modern animasyonlar için kesinlikle en alakalı olanıdır.
Doug

10

Bir view özelliği yerine sabiti değiştirerek bir kısıtlamayı canlandırıyorsanız, diğer yöntemlerin hiçbiri iOS 8'de çalışmaz.

Örnek animasyon:

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) {

                 }];

Çözüm:

Animasyonları, kısıtlama değişikliğinden ve alt katmanlarından etkilenen görünümlerin katmanlarından kaldırmanız gerekir.

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

1
Ayrıca sadece self.constraintView.layer.removeAllAnimations()çalışır (Swift)
Lachtan

Diğer çözümlerin hiçbiri işe yaramadı.Teşekkürler, benim için çalıştı.
swapnilagarwal


5

Yukarıdakilerin hiçbiri benim için çözmedi, ancak bu yardımcı oldu: UIViewAnimasyon özelliği hemen ayarlıyor, sonra canlandırıyor. Sunu katmanı modelle (set özelliği) eşleştiğinde animasyonu durdurur.

Sorunumu çözdüm, bu da "Göründüğün gibi göründüğün yerden canlandırmak istiyorum" (görünümün anlamı 'sen') idi. Bunu istiyorsanız, o zaman:

  1. QuartzCore ekleyin.
  2. CALayer * pLayer = theView.layer.presentationLayer;

konumu sunum katmanına ayarlama

Dahil olmak üzere birkaç seçenek kullanıyorum UIViewAnimationOptionOverrideInheritedDuration

Ancak Apple'ın belgeleri belirsiz olduğundan, kullanıldığında diğer animasyonları gerçekten geçersiz kılar mı yoksa yalnızca zamanlayıcıları sıfırlar mı bilmiyorum.

[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) {}];

Şimdilik bu iyi bir çözüm olmalı.


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

2

Stephen Darlington'ın çözümünün hızlı versiyonu

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

// set view properties
UIView.commitAnimations()

1

Aynı problemim var; API'ların belirli bir animasyonu iptal edecek hiçbir şeyi yoktur.

+ (void)setAnimationsEnabled:(BOOL)enabled

TÜM animasyonları devre dışı bırakır ve bu nedenle benim için çalışmaz. İki çözüm var:

1) animasyonlu nesnenizi bir alt görünüm yapın. Ardından, o görünümün animasyonlarını iptal etmek istediğinizde, görünümü kaldırın veya gizleyin. Çok basit, ancak görünümde tutmanız gerekiyorsa alt görünümü animasyon olmadan yeniden oluşturmanız gerekir.

2) animasyonu yalnızca bir kez tekrarlayın ve gerekirse animasyonu yeniden başlatmak için bir temsilci seçici yapın, şöyle:

-(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) ile ilgili sorun, animasyonu geçerli animasyon döngüsünü bitirirken her zaman gecikmenin durmasıdır.

Bu yardımcı olur umarım.


1

Animasyonu yukarıdaki yollarla iptal etseniz bile animasyon didStopSelectorhala çalışır. Uygulamanızda animasyonlarla yönlendirilen mantık durumlarınız varsa, sorunlarınız olacaktır. Bu nedenle yukarıda açıklanan yöntemlerle UIViewanimasyonların bağlam değişkenini kullanıyorum . Programınızın geçerli durumunu animasyon parametresiyle animasyona didStopSelectorgeçirirseniz , animasyon durduğunda işleviniz bir şey yapması gerekip gerekmediğine veya yalnızca geçerli duruma ve bağlam olarak iletilen durum değerine göre dönüp dönmeyeceğine karar verebilir.


NickForge, TomTaylor'un yukarıdaki doğru cevabı aşağıdaki yorumda bunu mükemmel bir şekilde açıklıyor ...
Fattie

Bu not için çok teşekkürler! Gerçekten animationDidStop çağrıldığında bir sonraki animasyon ateş nerede böyle bir durum var. Bir animasyonu kaldırırsanız ve hemen aynı anahtar için yeni bir tane eklerseniz, işe yaramayacağını düşünüyorum. Örneğin, animationDidStop başka bir animasyon veya başka bir şey için tetiklenene kadar beklemeniz gerekir. Yeni kaldırılan animasyon artık animationDidStop'u tetiklemez.
RickJansen

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

0

Durumu orijinal veya son haline döndürmeden bir animasyonu duraklatmak için:

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

0

UIStackViewAnimasyonla çalıştığımda , removeAllAnimations()bazı değerleri başlangıç ​​değerine ayarlamam gerekiyor çünkü removeAllAnimations()tahmin edilemeyen bir duruma getirebilirler. Ben stackViewile view1ve view2iç ve bir görünümü görünür olmalıdır ve bir gizli:

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

Cevaplanan çözümlerin hiçbiri benim için çalışmadı. Sorunlarımı bu şekilde çözdüm (doğru bir yol olup olmadığını bilmiyorum?), Çünkü bunu çok hızlı çağırırken sorun yaşadım (önceki animasyon henüz bitmediğinde). İstediğim animasyonu customAnim bloğu ile geçiyorum .

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()
                }
        })

    }

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.