2014-06-25 13 views
5

Tôi đang sử dụng CALayer, CAReplicatorLayerCABasicAnimation để tạo hoạt ảnh cho biểu đồ thanh tại chỗ. Biểu đồ thanh được tạo thành từ "phân đoạn" mà tôi hiển thị dưới dạng hình chữ nhật nhỏ với một số CALayer. Sau đó, tôi thêm chúng dưới dạng một lớp con vào CAReplicatorLayer trong một vòng lặp. Cuối cùng, tôi thêm một hình ảnh động cho mỗi hình chữ nhật nhỏ thả hình chữ nhật vào vị trí của nó trong biểu đồ.Có vấn đề với việc sử dụng kCAMediaTimingFunctionEaseIn này không?

Đây là trạng thái cuối cùng của hoạt hình:

Correct final state of the animation

Và đây là những gì nó trông giống như ở giữa của hình ảnh động:

Animating state

Dưới đây là đoạn code mà làm điều đó xảy ra:

#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"]; 
    }]; 
} 

Cuối cùng, đây là bắt. Nếu bạn chỉ định không timingFunction, chức năng Linear hoặc chức năng EaseIn hoạt ảnh không hoàn thành đầy đủ. Giống như các khối không rơi hoàn toàn vào vị trí. Có vẻ như họ gần hoàn thành nhưng không hoàn toàn. Chỉ một hoặc hai khung hình. Trao đổi trên sự phân công chức năng thời gian trong -viewDidAppear: và bạn sẽ nhận được sự kì quái này:

enter image description here

Đi trước, hãy thử nó ra. Tôi thậm chí đã cố gắng xác định một chức năng với các điểm kiểm soát (link) mà là giống hệt như một chức năng EaseIn:

// 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]; 

Đối với một số lý do EaseOutDefault chức năng thời gian làm việc tốt nhưng một cách dễ dàng trong chức năng phong cách dường như mất nhiều thời gian để hoàn thành, trực quan, có lẽ vì chiều dài của đường cong nội suy?

Tôi đã thử nhiều thứ để thu hẹp vấn đề:

  1. Không Looping trên những cấu trúc và chỉ sử dụng 1 lớp và 1 lớp replicator với các hình ảnh động tương tự. Ý tôi là, chỉ hoạt hình một cột. Không may mắn. Looping không phải là vấn đề.
  2. Đặt mã tạo, thêm lớp con và thêm hoạt ảnh vào tất cả trong -viewDidAppear. Dường như không tạo ra sự khác biệt nếu các hình động đã được thêm vào sớm hơn hoặc muộn hơn trong vòng đời của khung nhìn.
  3. Sử dụng các thuộc tính cho cấu trúc của tôi. Không khác nhau.
  4. Không sử dụng các thuộc tính cho cấu trúc của tôi. Một lần nữa, không có sự khác biệt.
  5. Đã thêm gọi lại để hoàn tất hoạt ảnh để xóa hoạt ảnh để để các cột ở trạng thái "cuối cùng" chính xác. Điều này không tốt nhưng đáng để thử.
  6. Vấn đề là hiện diện trong cả iOS 6 và 7.

Tôi thực sự muốn sử dụng một chức năng EaseIn thời gian vì nó là tìm kiếm hình ảnh động tốt nhất cho một cái gì đó rơi vào vị trí. Tôi cũng muốn sử dụng CAReplicatorLayer vì nó thực hiện chính xác những gì tôi muốn làm.

Vì vậy, có gì sai với chức năng thời gian EaseIn khi tôi đang sử dụng chúng? Đây có phải là một lỗi?

Trả lời

1

Tôi đồng ý rằng đây dường như là lỗi trong sự kết hợp giữa CAReplicatorLayer và hàm thời gian kCAMediaTimingFunctionEaseIn.

Để giải quyết sự cố này, tôi khuyên bạn nên sử dụng hoạt ảnh khung hình chính có ba khung hình chính. Hai khung hình chính đầu tiên xác định hoạt ảnh bạn muốn với thời gian dễ dàng. Khung hình chính cuối cùng được định nghĩa là giống với khung hình chính thứ hai, nhưng cho phép hoạt ảnh thêm thời gian để ngồi trên khung chính cuối cùng, có vẻ như để cho lớp nhân bản hoạt hình lớp được sao chép lần cuối vào trạng thái cuối cùng. do đó

Đề nghị của tôi là:

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

Xin đừng sử dụng 'removedOnCompletion = NO'. Đó là một cách xấu để làm cho hình ảnh động dính. Tôi đang có đủ thời gian để thuyết phục mọi người rằng đó là một ý tưởng tồi ngay cả khi họ không nhìn thấy nó trong các câu trả lời của SO. Tôi biết rằng OP đã sử dụng nó trong câu hỏi, nhưng tất cả chúng ta đều có thể là những trinh sát tốt và rời khỏi khu cắm trại ở trạng thái tốt hơn khi chúng tôi đến đó. –

+1

Tôi hoàn toàn đồng ý rằng 'removeOnCompletion = NO' là một ý tưởng tồi, nhưng đối với một hình động trên một CAReplicatorLayer, nó là hoàn toàn bắt buộc. Nếu không, hoạt ảnh có thể bị xóa trước khi lớp được sao chép cuối cùng đã phát hoàn toàn. Tôi nghĩ rằng giải pháp đúng ở đây là thiết lập một bộ đếm thời gian để bắn vào 'duration + (instanceCount - 1) * instanceDelay' và loại bỏ các hình ảnh động bằng tay tại thời điểm đó. – GBegen

Các vấn đề liên quan