2012-04-24 15 views
7

W podklasy CALayer pracuję nad mam właściwość niestandardową, że chcę, aby animować automatycznie, czyli przy założeniu, że nieruchomość jest o nazwie „myProperty”, chcę następujący kod:Niestandardowa animacja właściwości z actionForKey: jak uzyskać nową wartość właściwości?

[myLayer setMyProperty:newValue]; 

powodować płynna animacja od bieżącej wartości do "nowej wartości".

Korzystanie z podejścia nadpisującego działanieForKey: and needsDisplayForKey: (patrz poniższy kod) Udało mi się go bardzo dobrze uruchomić, po prostu interpolując między starą i nową wartością.

Moim problemem jest to, że chcę użyć nieco innego czasu trwania animacji lub ścieżki (lub czegoś podobnego) w zależności od aktualnej wartości i nowej wartości nieruchomości, a ja nie byłem w stanie wymyślić, jak uzyskać nowe wartość od wewnątrz actionForKey:

góry dzięki

@interface ERAnimatablePropertyLayer : CALayer { 
    float myProperty; 
} 
@property (nonatomic, assign) float myProperty; 
@end 
@implementation ERAnimatablePropertyLayer 
@dynamic myProperty; 

- (void)drawInContext:(CGContextRef)ctx { 
    ... some custom drawing code based on "myProperty" 
} 

- (id <CAAction>)actionForKey:(NSString *)key { 
    if ([key isEqualToString:@"myProperty"]) { 
     CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:key]; 
     theAnimation.fromValue = [[self presentationLayer] valueForKey:key]; 

     ... I want to do something special here, depending on both from and to values... 


     return theAnimation; 
     } 
    return [super actionForKey:key]; 
    } 

+ (BOOL)needsDisplayForKey:(NSString *)key { 
    if ([key isEqualToString:@"myProperty"]) 
     return YES; 
    return [super needsDisplayForKey:key]; 
    } 
    @end 

Odpowiedz

1

można przechowywać stare i nowe wartości w CATransaction.

-(void)setMyProperty:(float)value 
{ 
    NSNumber *fromValue = [NSNumber numberWithFloat:myProperty]; 
    [CATransaction setValue:fromValue forKey:@"myPropertyFromValue"]; 

    myProperty = value; 

    NSNumber *toValue = [NSNumber numberWithFloat:myProperty]; 
    [CATransaction setValue:toValue forKey:@"myPropertyToValue"]; 
} 

- (id <CAAction>)actionForKey:(NSString *)key { 
    if ([key isEqualToString:@"myProperty"]) { 
     CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:key]; 
     theAnimation.fromValue = [[self presentationLayer] valueForKey:key]; 
     theAnimation.toValue = [CATransaction objectForKey:@"myPropertyToValue"]; 

     // here you do something special. 
    } 

    return [super actionForKey:key]; 
} 
+0

Implementacja akcesora dla właściwości @dynamic (myProperty) sprawia, że ​​cała magia znika - CA po prostu przestaje inicjować niejawne transakcje dla usługi. – zrxq

+0

Warto wiedzieć, ale biorąc pod uwagę fakt, że OP chce wyraźnej animacji, powiedziałbym, że to nie problem. – Simon

+0

OP chce ukrytego. "Mam niestandardową właściwość, którą chcę animować automatycznie". Ponadto, myProperty jest @dynamic w oryginalnym fragmencie. – zrxq

2

Należy unikać niestandardowych programów pobierających i ustawiających dla właściwości, które mają być animowane.

Zastępuje metodę didChangeValueForKey:. Użyj go, aby ustawić wartość modelu właściwości, którą chcesz animować.

Nie ustawiaj wartości ToValue w animacji akcji. wzywa

@implementation MyLayer 

@dynamic state; 

- (id<CAAction>)actionForKey: (NSString *)key { 

    if([key isEqualToString: @"state"]) 
    { 
     CABasicAnimation * bgAnimation = [CABasicAnimation animationWithKeyPath: @"backgroundColor"]; 
     bgAnimation.fromValue = [self.presentationLayer backgroundColor]; 
     bgAnimation.duration = 0.4; 

     return bgAnimation; 
    } 

    return [super actionForKey: key]; 
} 

- (void)didChangeValueForKey: (NSString *)key { 

    if([key isEqualToString: @"state"]) 
    { 
     const NSUInteger state = [self valueForKey: key]; 
     UIColor * newBackgroundColor; 

     switch (state) 
     { 
      case 0: 
       newBackgroundColor = [UIColor redColor]; 
       break; 

      case 1: 
       newBackgroundColor = [UIColor blueColor]; 
       break; 

      case 2: 
       newBackgroundColor = [UIColor greenColor]; 
       break; 

      default: 
       newBackgroundColor = [UIColor purpleColor]; 
     } 

     self.backgroundColor = newBackgroundColor.CGColor; 
    } 

    [super didChangeValueForKey: key]; 
} 

@end 
+0

W niestandardowych akcesorach musisz wywołać [self willChangeValueForKey: key], ustaw wartość, a następnie wywołaj [self didChangeValueForKey: key]. – SG1

2

Rdzeń Animacja actionForKey: przed aktualizacją wartości nieruchomości -

@interface MyLayer: CALayer 

    @property (nonatomic) NSUInteger state; 

@end 

. Uruchamia akcję po zaktualizowaniu wartości właściwości, wysyłając ją pod numer runActionForKey:object:arguments:. Implementacja CAAnimation implementacji runActionForKey:object:arguments: po prostu wywołuje [object addAnimation:self forKey:key].

Zamiast zwracać animację z actionForKey:, można zwrócić CAAction, który po uruchomieniu tworzy i instaluje animację. Coś takiego:

@interface MyAction: NSObject <CAAction> 
@property (nonatomic, strong) id priorValue; 
@end 

@implementation MyAction 

- (void)runActionForKey:(NSString *)key object:(id)anObject arguments:(NSDictionary *)dict { 
    ERAnimatablePropertyLayer *layer = anObject; 
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key]; 
    id newValue = [layer valueForKey:key]; 
    // Set up animation using self.priorValue and newValue to determine 
    // fromValue and toValue. You could use a CAKeyframeAnimation instead of 
    // a CABasicAnimation. 
    [layer addAnimation:animation forKey:key]; 
} 

@end 

@implementation ERAnimatablePropertyLayer 

- (id <CAAction>)actionForKey:(NSString *)key { 
    if ([key isEqualToString:@"myProperty"]) { 
     MyAction *action = [[MyAction alloc] init]; 
     action.priorValue = [self valueForKey:key]; 
     return action; 
    } 
    return [super actionForKey:key]; 
} 

Można znaleźć pracy przykład podobnego techniki (w Swift, animowanie cornerRadius) in this answer.

+0

Doskonała odpowiedź! – Johannes

Powiązane problemy