Tôi đang sử dụng CALayer
, CAReplicatorLayer
và CABasicAnimation
để 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:
Và đây là những gì nó trông giống như ở giữa của hình ảnh động:
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:
Đ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 EaseOut
và Default
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 đề:
- 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 đề.
- Đặ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. - Sử dụng các thuộc tính cho cấu trúc của tôi. Không khác nhau.
- 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.
- Đã 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ử.
- 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?
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 đó. –
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