2014-06-25 22 views
5

Ben yerine bir çubuk grafik animasyon uygulamak CALayer, CAReplicatorLayer ve CABasicAnimation kullanıyorum. Çubuk grafik, CALayer ile küçük dikdörtgenler halinde oluşturduğum "segmentler" den oluşur. Sonra onları bir döngü içinde CAReplicatorLayer'a bir alt katman olarak eklerim. Son olarak, dikdörtgeni tablodaki yerine bırakan her küçük dikdörtgen için bir animasyon ekliyorum. İşte kCAMediaTimingFunctionEaseIn'ın bu kullanımıyla ilgili bir sorun var mı?

animasyonunun son halidir:

Correct final state of the animation

Ve işte animasyon ortasında neye benzediği: Burada

Animating state

kılacak yöntem var gerçekleşmesi:

#define BLOCK_HEIGHT 6.0f 
#define BLOCK_WIDTH 8.0f 

@interface TCViewController() 

@property (nonatomic, strong) NSMutableArray * blockLayers;   // blocks that fall from the sky 
@property (nonatomic, strong) NSMutableArray * replicatorLayers; // replicator layers that will replicate the blocks into position 

@end 

@implementation TCViewController 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.view.backgroundColor = [UIColor lightGrayColor]; 

    self.blockLayers = [NSMutableArray array]; 
    self.replicatorLayers = [NSMutableArray array]; 

    NSArray * dataBlocksData = @[@1,@2,@3,@1,@5,@2,@11,@14,@5,@12,@10,@3,@7,@6,@3,@4,@1]; 

    __block CGFloat currentXPosition = 0.0f; 

    [dataBlocksData enumerateObjectsUsingBlock:^(NSNumber * obj, NSUInteger idx, BOOL *stop) { 

     currentXPosition += BLOCK_WIDTH + 2.0f; 

     if (obj.integerValue == 0) return; 

     // replicator layer for data blocks in a column 
     CAReplicatorLayer * replicatorLayer = [[CAReplicatorLayer alloc] init]; 
     replicatorLayer.frame = CGRectMake(currentXPosition, 0.0f, BLOCK_WIDTH, 200.0f); 

     // single data block layer (must be new for each replicator layer. can't reuse existing layer) 
     CALayer * blockLayer = [[CALayer alloc] init]; 
     blockLayer.frame = CGRectMake(0, 200-BLOCK_HEIGHT, BLOCK_WIDTH, BLOCK_HEIGHT); 
     blockLayer.backgroundColor = [UIColor whiteColor].CGColor; 
     [replicatorLayer addSublayer:blockLayer]; 

     replicatorLayer.instanceCount = obj.integerValue; 
     replicatorLayer.instanceDelay = 0.1f; 
     replicatorLayer.instanceTransform = CATransform3DMakeTranslation(0, -BLOCK_HEIGHT, 0); // negative height moves back up the column 

     [self.blockLayers addObject:blockLayer];     
     [self.replicatorLayers addObject:replicatorLayer];  
    }]; 
} 

- (void)viewDidLayoutSubviews { 
    // add replicator layers as sublayers 
    [self.replicatorLayers enumerateObjectsUsingBlock:^(CAReplicatorLayer * obj, NSUInteger idx, BOOL *stop) { 
     [self.view.layer addSublayer:obj]; 
    }]; 
} 

- (void)viewDidAppear:(BOOL)animated { 

    // add animations to each block layer 
    [self.blockLayers enumerateObjectsUsingBlock:^(CALayer * obj, NSUInteger idx, BOOL *stop) { 
     // position animation 
     CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.y"]; 
     animation.fromValue = @(-300); 
     animation.toValue = @(0); 
     animation.duration = 1.0f; 
     animation.fillMode = kCAFillModeBoth; 
     animation.removedOnCompletion = NO; 
     animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]; 
     [obj addAnimation:animation forKey:@"falling-block-animation"]; 
    }]; 
} 

Son olarak, işte yakalayıcı. Hiçbir timingFunction, bir Linear işlev veya bir EaseIn belirtirseniz animasyonlar tamamen tamamlanmaz çalışır. Bloklar tamamen yerine düşmez. Bitişe yakın gibi görünüyorlar ama tam olarak değil. Sadece bir kare veya iki kapalı. -viewDidAppear: zamanlama fonksiyonu atama aktarır ve bu garabeti alırsınız:

enter image description here

Devam et, bir deneyin. Hatta tam bir EaseIn işlevi gibidir kontrol noktaları ile bir işlev (link) belirterek çalıştı:

EaseOut ve Default zamanlama fonksiyonları iyi çalışır ancak stil işlevinde bir kolaylık daha uzun süren görünmektedir Nedense
// use control points for an EaseIn timing: 
animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.42 :0.00 :1.0 :1.0]; 

// or simply specify the ease in timing function: 
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; 

, görsel olarak, belki de enterpolasyon eğrisinin uzunluğu nedeniyle?

  1. bu yapıların üzerinde döngü ve sadece 1 katman ve aynı animasyon ile 1 çoğaltıcı katmanını kullanmama:

    Sorunu daraltmak için birçok şeyi denedim. Bununla demek istediğim, sadece bir sütun hareketlendirmek. Şanssız. Döngü sorun değil.

  2. oluşturulmasını koyun, alt katman-ekleme ve -viewDidAppear tüm kodu animasyon ekleyerek. Animasyonlar görünümün yaşam döngüsünde daha önce veya daha sonra eklendiyse, bir fark yaratmıyor gibi görünmüyordu.
  3. Yapılarım için özellikleri kullanma. Fark yok.
  4. benim yapıların özelliklerini kullanmama. Yine, fark yok. Doğru "nihai" halde sütun bırakacak şekilde animasyon tamamlanması için
  5. Eklenen geri aramalar animasyon kaldırın. Bu iyi değildi ama denemeye değer.
  6. sorunu hem iOS 6 ve gerçekten bir şey için en iyi şekilde görünmesini animasyon yerine oturuyor çünkü bir EaseIn zamanlama işlevi kullanmak isteyen

7. mevcuttur. Ben de CAReplicatorLayer kullanmak istiyorum çünkü yapmak istediğim şeyi yapıyor.

EaseIn zamanlama işlevlerini kullanırken kullandığım gibi mi? Bu bir hata mı?

cevap

1

Bunun, CAReplicatorLayer ve kCAMediaTimingFunctionEaseIn zamanlama işlevi kombinasyonunda bir hata olduğu anlaşılıyor.

Geçici bir çözüm olarak, üç anahtar kareden oluşan bir ana kare animasyonu kullanmanızı öneriyorum. İlk iki anahtar karesi, istediğiniz animasyonu kolaylık zamanlamasıyla tanımlar. Son anahtar kare ikinci anahtar kareye özdeş olarak tanımlanır, ancak son anahtar karede oturması için animasyona ek süre verir, bu da çoğaltıcı katmanın son çoğaltılmış katmanı son haline getirmesini sağlar.

Benim önerim bu nedenle:

CAKeyframeAnimation * animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"]; 

// extra keyframe duplicating final state 
animation.values = @[@-300.0f, @0.0f, @0.0f]; 

// double length of original animation, with last half containing no actual animation 
animation.keyTimes = @[@0.0, @0.5, @1.0]; 
animation.duration = 2.0f; 

animation.fillMode = kCAFillModeBoth; 
animation.removedOnCompletion = NO; 

// ease-in for first half of animation; default after 
animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn], [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]]; 
[obj addAnimation:animation forKey:@"falling-block-animation"]; 
+2

'removedOnCompletion = NO' lütfen kullanmayın. Animasyonlar yapmanın kötü bir yolu. İnsanlara, SO cevaplarında görmediklerinde bile kötü bir fikir olduğuna ikna edebilecek yeterince zamanım var. OP'nin bu soruyu kullandığını biliyorum, ama hepimiz iyi bir izci olabiliriz ve kamp alanını oraya vardığımız zamandan daha iyi bir durumda bırakabiliriz. –

+1

Tamamen katılıyorum 'removedOnCompletion = NO' kötü bir fikir, ancak bir CAReplicatorLayer'daki bir animasyon için tamamen gerekli. Aksi halde, son çoğaltılan katman tamamen oynatılmadan önce animasyon kaldırılabilir. Buradaki doğru çözümün, time + (instanceCount - 1) * instanceDelay'de bir zamanlayıcıyı ateşlemek ve o anda el ile animasyonu kaldırmak olduğunu düşünüyorum. – GBegen

İlgili konular