2012-02-21 34 views
13

Tôi có cảm giác tôi đang xem một số thứ cơ bản, nhưng cách nào tốt hơn để tìm thấy nó hơn là sai trên internet?Thuộc tính CAGradientLayer không hoạt ảnh trong khối hoạt ảnh UIView

Tôi có giao diện người dùng khá cơ bản. Chế độ xem cho số UIViewController của tôi là một phân lớp có +layerClassCAGradientLayer. Tùy thuộc vào hành động của người dùng, tôi cần di chuyển một số phần tử giao diện người dùng xung quanh và thay đổi giá trị của độ dốc của nền. Mã này trông giống như sau:

[UIView animateWithDuration:0.3 animations:^{ 
    self.subview1.frame = CGRectMake(...); 
    self.subview2.frame = CGRectMake(...); 
    self.subview2.alpha = 0; 

    NSArray* newColors = [NSArray arrayWithObjects: 
         (id)firstColor.CGColor, 
         (id)secondColor.CGColor, 
         nil]; 
    [(CAGradientLayer *)self.layer setColors:newColors]; 
}]; 

Vấn đề là những thay đổi tôi thực hiện trong khối này đến subviews động tốt (thứ di chuyển và fades), nhưng sự thay đổi màu sắc của độ dốc thì không. Nó chỉ hoán đổi.

Hiện tại, the documentation does say mã hoạt ảnh chính trong khối hoạt ảnh sẽ không kế thừa thuộc tính của khối (thời lượng, giãn, v.v ...). Nhưng đó có phải là trường hợp không xác định được giao dịch hoạt hình không? (Ý nghĩa của các tài liệu có vẻ là bạn sẽ nhận được một hình ảnh động mặc định, nơi tôi không nhận được.)

Tôi có để sử dụng rõ ràng CAAnimation để thực hiện tác vụ này không? (Và nếu có, tại sao?)

Trả lời

13

Có vẻ như có hai điều đang xảy ra ở đây. Việc đầu tiên (như Travis chính xác chỉ ra, và các trạng thái tài liệu) là các hoạt ảnh UIKit dường như không giữ bất kỳ ảnh hưởng nào trên hoạt ảnh ngầm được áp dụng cho các thay đổi về tài sản CALayer. Tôi nghĩ rằng điều này là lạ (UIKit phải được sử dụng Core Animation), nhưng nó là những gì nó được.

Dưới đây là một cách giải quyết (có thể rất ngớ ngẩn?) Cho vấn đề đó:

NSTimeInterval duration = 2.0; // slow things down for ease of debugging 
    [UIView animateWithDuration:duration animations:^{ 
    [CATransaction begin]; 
    [CATransaction setAnimationDuration:duration]; 

    // ... do stuff to things here ... 

    [CATransaction commit]; 
    }]; 

Mấu chốt khác là layer gradient này là lớp nhìn tôi. Điều đó có nghĩa rằng quan điểm của tôi là ủy nhiệm của lớp (trong đó, nếu lớp gradient chỉ là một lớp con, nó sẽ không có một đại biểu). Và việc triển khai UIView-actionForLayer:forKey: trả lại NSNull cho sự kiện "colors". (Có lẽ mọi sự kiện không có trong danh sách cụ thể về hoạt ảnh UIView.)

Thêm đoạn mã sau vào quan điểm của tôi sẽ gây ra sự thay đổi màu sắc để được hoạt hình như mong đợi:

- (id<CAAction>)actionForLayer:(CALayer *)layer forKey:(NSString *)event 
{ 
    id<CAAction> action = [super actionForLayer:layer forKey:event]; 
    if([@"colors" isEqualToString:event] 
     && (nil == action || (id)[NSNull null] == action)) { 
    action = [CABasicAnimation animationWithKeyPath:event]; 
    } 
    return action; 
} 
+0

Tìm kiếm tuyệt vời! Sẽ không bao giờ đoán được điều này. –

1

Bạn phải sử dụng CAAnimations rõ ràng, vì bạn đang thay đổi giá trị của một CALayer. UIViewAnimations làm việc trên đặc tính UIView, nhưng không trực tiếp về tài sản CALayer của họ ...

Trên thực tế, bạn nên sử dụng một CABasicAnimation để bạn có thể truy cập vào fromValuetoValue của tài sản.

Các mã sau đây nên làm việc cho bạn:

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 
    [UIView animateWithDuration:2.0f 
          delay:0.0f 
         options:UIViewAnimationCurveEaseInOut 
        animations:^{ 
         CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"colors"]; 
         animation.duration = 2.0f; 
         animation.delegate = self; 
         animation.fromValue = ((CAGradientLayer *)self.layer).colors; 
         animation.toValue = [NSArray arrayWithObjects:(id)[UIColor blackColor].CGColor,(id)[UIColor whiteColor].CGColor,nil]; 
         [self.layer addAnimation:animation forKey:@"animateColors"]; 
        } 
        completion:nil]; 
} 

-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag { 
    NSString *keyPath = ((CAPropertyAnimation *)anim).keyPath; 
    if ([keyPath isEqualToString:@"colors"]) { 
     ((CAGradientLayer *)self.layer).colors = ((CABasicAnimation *)anim).toValue; 
    } 
} 

Có một trick với CAAnimations ở chỗ bạn phải thiết lập một cách rõ ràng giá trị của bất động sản sau khi bạn hoàn thành các hình ảnh động.

Bạn làm điều này bằng cách đặt đại biểu, trong trường hợp này tôi đặt đối tượng đó gọi hoạt ảnh và sau đó ghi đè phương thức animationDidStop:finished: để bao gồm cài đặt màu của CAGradientLayer thành giá trị cuối cùng của chúng.

Bạn cũng sẽ phải thực hiện một số thao tác truyền theo phương pháp animationDidStop: để truy cập các thuộc tính của hoạt ảnh.

+0

Có điều là, tính CALayer có hình ảnh động ngầm. Dường như các thuộc tính hoạt ảnh được xác định bởi một hoạt ảnh UIView không được áp dụng cho các thay đổi của CALayer, nhưng điều đó không giải thích tại sao tôi không nhìn thấy * bất kỳ hoạt ảnh * nào của thay đổi màu sắc. –

+0

Cả UIView và CALayer đều có hoạt ảnh ngầm. Khi bạn kích hoạt một UIViewAnimation, và bạn muốn kích hoạt một hình ảnh động trên CALayer của nó, bạn phải điều này một cách rõ ràng cùng một lúc. Đó là những gì mà CABasicAnimation thực hiện trong các hoạt ảnh của UIView:^{} block ... –

+0

Nhưng điều quan trọng là hoạt ảnh ngầm không xảy ra vì lớp được đề cập là lớp * xem *. Đó là một mối quan tâm hoàn toàn riêng biệt từ việc thay đổi được thực hiện bên trong một khối hoạt hình UIView. (Toàn bộ điểm hoạt hình ngầm là chúng không yêu cầu các khối hoạt hình rõ ràng.) –

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