2016-04-19 2 views
0

Я работаю над круговой индикацией выполнения, которую хочу оживить. Все рисует, как ожидалось, и работает, но когда я пытаюсь оживить, я получаю немедленное изменение вместо анимации. Любая помощь будет оценена по достоинству.Неявная анимация на CALayer не анимация

Вот мой слой:

@interface CircularProgressLayer : CALayer 

@property (nonatomic, assign) CGFloat progressDegrees; 
@property (nonatomic, assign) CGFloat trackWidth; 

@end 

@implementation CircularProgressLayer : CALayer 

@dynamic progressDegrees; 
@dynamic trackWidth; 

- (id)initWithLayer:(id)layer 
{ 
    if ((self = [super initWithLayer:layer])) 
    { 
     if ([layer isKindOfClass:[CircularProgressLayer class]]) 
     { 
      self.progressDegrees = ((CircularProgressLayer*)layer).progressDegrees; 
     } 
    } 

    return self; 
} 

// Instruct to Core Animation that a change in the custom property should automatically trigger a redraw of the layer. 
+ (BOOL)needsDisplayForKey:(NSString*)key 
{ 
    if ([key isEqualToString:@"progressDegrees"]) 
    { 
     return YES; 
    } 
    if ([key isEqualToString:@"trackWidth"]) 
    { 
     return YES; 
    } 

    return [super needsDisplayForKey:key]; 
} 

// Needed to support implicit animation of this property. 
// Return the basic animation the implicit animation will leverage. 
- (id<CAAction>)actionForKey:(NSString *)key 
{ 
    if ([key isEqualToString:@"progressDegrees"]) 
    { 
     CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:key]; 
     animation.fromValue = [[self presentationLayer] valueForKey:key]; 
     animation.duration = 5.0; 
     animation.fillMode = kCAFillModeForwards; 
     animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]; 

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

Как я понимаю, возвращая анимацию в actionForKey: должно быть все, что нужно, чтобы сделать анимацию работы, но я ничего не получаю. Весь мой чертеж в настоящее время содержится в методе drawLayer:inContext: моего представления, который реализует этот слой (потому что он возник как код drawRect, прежде чем я узнал, что должен был помещать его в слой, чтобы его можно было оживить, и я не преобразовал все), но sample code, который я скачал из Apple, похоже, что все в порядке.

Вот код рисования:

-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context 
{ 
    UIGraphicsPushContext(context); 
    [self drawTrackInContext:context]; 
    [self drawProgressInContext:context]; 
    UIGraphicsPopContext(); 
} 

- (void)drawProgressInContext:(CGContextRef)context 
{  
    if (self.progress < 1) 
    { 
     // No progress, get out 
     return; 
    } 

    CGSize size = self.bounds.size; 

    FlipContextVertically(size); 
    CGContextSaveGState(context); 
    { 
     UIBezierPath *circle = [self circle]; 
     CGContextSetLineWidth(context, 0.0); 

     CGFloat degrees = 360.0; 

     for (int i = 0; i < degrees; i++) 
     { 
      if (i > ((RMONCircularProgressLayer *)self.layer).progressDegrees) 
      { 
       break; 
      } 

      CGFloat theta = 2 * M_PI * (CGFloat) i/degrees; 
      CGFloat x = self.radius * sin(theta); 
      CGFloat y = self.radius * cos(theta); 

      MovePathCenterToPoint(circle, CGPointMake(x, y)); 
      OffsetPath(circle, CGSizeMake(size.width/2, size.height/2)); 

      CGContextSetFillColorWithColor(context, [self colorForDegree:i].CGColor); 
      CGContextAddPath(context, circle.CGPath); 
      CGContextDrawPath(context, kCGPathFillStroke); 
     } 
    } 
    CGContextRestoreGState(context); 
} 

- (void)drawTrackInContext:(CGContextRef)context 
{ 
    CGContextSaveGState(context); 
    { 
     // Draw the circle covering middle of the screen 
     CGSize size = self.bounds.size; 

     CGContextSetLineWidth(context, self.trackWidth); // will only be filled color 

     CGContextSetStrokeColorWithColor(context, self.trackColor.CGColor); 
     CGContextSetFillColorWithColor(context, self.fillColor.CGColor); 
     CGContextSetAlpha(context, 1.0); 

     CGContextAddArc(context, (CGFloat)size.width/2.0, (CGFloat)size.height/2.0, self.radius, 0, (CGFloat)M_PI*2.0, 1); 
     CGContextDrawPath(context, kCGPathFillStroke); 
    } 
    CGContextRestoreGState(context); 
} 

drawTrackInContext просто рисует круг, где круг прогресс будет содержаться. - это код чертежа, который я ожидаю оживить. Есть некоторые вспомогательные функции от iOS Drawing от Erica Sadun, которые выполняют некоторые манипуляции с контекстом и путями, которые я не включил, имена функций должны быть достаточно понятными.

Любая помощь была бы принята с благодарностью.

FYI: Я более чем готов сделать эту явную анимацию, если это проще, но я оказался в этом направлении, потому что я тоже не мог сделать эту работу.

+0

Пожалуйста, покажите свой код внутри 'drawLayer: inContext:' –

+0

Добавлен код чертежа, не знаю, почему я его забыл. –

ответ

2

Когда свойство слоя анимируется, значение этого свойства не изменяется со временем для представления промежуточных значений. Вместо этого значения свойства для данного заданного момента времени слоя выводятся из активной анимации и становятся доступными в .

Когда анимация активна, на слое презентации вызывается drawInContext:, а не основной слой. Аналогично, -drawLayer:inContext: получает слой презентации, а не основной слой.

Проблема, с которой вы столкнулись, заключается в том, что -drawProgressInContext: не использует слой от вызывающей функции и вместо этого считывает self.layer.progressDegrees, чтобы получить значение для рисования, которое всегда будет назначенным значением. Исправление пройти слой этой функции и прочитать значение:

- (void)drawProgressInLayer:(CALayer *)layer context:(CGContextRef)context 
{ 
    ... 
    if (i > ((RMONCircularProgressLayer *)layer).progressDegrees) { 
     ... 

У вас также есть проблема, когда вы прерываете на if (self.progress < 1). Это приведет к невозможности оживить при переходе от ненулевого значения к нулю. Это же исправление применимо: if ((RMONCircularProgressLayer *)layer).progressDegrees < 1)

+0

Благодарим вас за подробное объяснение. Я столкнулся с использованием значения слоев презентации, чтобы заставить анимацию работать, но мне пришлось добавить блок if (isAnimating) для преобразования между двумя, и я знал, что это должно быть проще. Я не понимал, что мой код рисования был вызван двумя разными слоями. Потрясающие. –

Смежные вопросы