Как плавно обновить toValue/конечную точку анимации в Core Animation?

Я пытаюсь подражать эффекту Apple в качестве индикатора прогресса при загрузке приложения в iOS 7, что-то вроде круговой диаграммы:

введите здесь описание изображения

Мой код идет довольно хорошо. Я могу дать ему значение для перехода, и оно будет обновлено до этого.

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];

    CGFloat radius = CGRectGetWidth(self.frame) / 2;
    CGFloat inset  = 1;
    CAShapeLayer *ring = [CAShapeLayer layer];
    ring.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)cornerRadius:radius-inset].CGPath;

    ring.fillColor = [UIColor clearColor].CGColor;
    ring.strokeColor = [UIColor whiteColor].CGColor;
    ring.lineWidth = 2;

    self.innerPie = [CAShapeLayer layer];
    inset = radius/2; 
    self.innerPie.path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, inset, inset)
                                               cornerRadius:radius-inset].CGPath;
    self.innerPie.fillColor   = [UIColor clearColor].CGColor;
    self.innerPie.strokeColor = [UIColor whiteColor].CGColor;
    self.innerPie.lineWidth   = (radius-inset)*2;

    [self.layer addSublayer:ring];
    [self.layer addSublayer:self.innerPie];

    self.progress = 0.0;
    self.innerPie.hidden = YES;
}

- (void)setProgress:(CGFloat)progress animated:(BOOL)animated {
    if (animated) {
        self.innerPie.hidden = NO;
        self.innerPie.strokeEnd = progress;

        CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
        pathAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
        pathAnimation.duration = 0.5;
        pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];
        pathAnimation.toValue = [NSNumber numberWithFloat:progress];

        [self.innerPie addAnimation:pathAnimation forKey:@"strokeEnd"];
    }
    else {
        [CATransaction setDisableActions:YES];
        [CATransaction begin];
        self.innerPie.hidden = NO;
        self.innerPie.strokeEnd = progress;
        [CATransaction commit];
    }

    self.progress = progress;
}

Моя проблема заключается в обновлении прогресса в середине анимации. Например, если я загружаю файл, и прогресс скачет с 0,1 до 0,2 (от 10% до 20%), я хочу анимировать это. Скажем, я даю ему 0,5-секундную анимацию. Что, если через 0,2 секунды после 0,5-секундной анимации оно подскочит до 0,4 (40%)?

С моим текущим кодом он переходит туда, где должен был закончиться (когда он должен был анимироваться) в первой анимации (20%), а затем снова начинает анимацию до 40%.

Я бы хотел, чтобы он почти обновил toValue до 0.4. По крайней мере, это то, что я думаю, что хочу. Концептуально я хотел бы, чтобы анимация продолжалась до нового значения, не прерывая предыдущее значение. Плавность, проще говоря.

Как бы я это сделал?

Я знаю, что для этого есть библиотеки. Я хочу сделать это сам в учебных целях.


person Doug Smith    schedule 18.03.2014    source источник


Ответы (1)


Вы должны заменить

pathAnimation.fromValue = [NSNumber numberWithFloat:self.progress];

с участием :

pathAnimation.fromValue = [NSNumber numberWithFloat:[self.innerPie.presentationLayer strokeEnd]];

Первоначально [self.innerPie.presentationLayer strokeEnd]]будет равно 1, поэтому вам нужно будет установить начальное значение равным 0. Добавьте эти 2 строки в место, где вы создаете свой 'innerPie':

self.innerPie.strokeStart = 0;
self.innerPie.strokeEnd = 0;

Я проверил, и это работает.

person namanhams    schedule 18.03.2014
comment
Это хорошо работает. Мой единственный вопрос сейчас заключается в том, возможно ли, чтобы прогресс круга заполнялся с постоянной скоростью. Прямо сейчас, если я заполню его независимо от того, сколько это занимает 0,5 секунды. Это означает, что маленькие заполнения будут двигаться медленно, а большие - быстро. Можно ли заставить его заполняться с постоянной скоростью? - person Doug Smith; 19.03.2014
comment
@DougSmith: да, это просто. Обратите внимание, что продолжительность = расстояние/скорость. Здесь «расстояние» — это разница между fromValue и toValue. «скорость» — это значение, которое вы определяете (например, 0,5 в 1 секунду). Затем вы можете рассчитать «длительность». Используйте его в pathAnimation.duration = [длительность, рассчитанная выше]; - person namanhams; 20.03.2014
comment
@DougSmith: был бы признателен, если бы вы могли отметить мой пост как принятый ответ, если он работает для вас. - person namanhams; 20.03.2014