UIView animateWithDuration и UIProgressView setProgress

Я хочу анимировать свою UIProgressView прогрессию от 0 до 1 в течение 10 секунд.

Код:

[UIView animateWithDuration:10.0 animations:^{
    [_myProgressView setProgress:1 animated:YES];
} completion:(BOOL finished)^{
    if (finished) NSLog(@"animation finished);
}];

Анимация работает нормально, за исключением того, что завершение и NSLog всегда вызываются мгновенно.

Я пробовал animateWithDuration: withDelay:, но задержка не соблюдается и выполняется немедленно.

Кто-нибудь сталкивался с такой же проблемой?

Спасибо за помощь.


person NtskX    schedule 22.05.2014    source источник
comment
Я также столкнулся с той же проблемой, и мне еще предстоит найти какое-то решение, которое хорошо оживляет прогресс. Нашли не тормозящее решение?   -  person EmilDo    schedule 27.06.2014
comment
То же самое с Эмилио. У вас есть хорошее решение?   -  person osrl    schedule 11.12.2014
comment
seanallen.co/posts/animated-progress-bar   -  person nitin.agam    schedule 14.09.2017


Ответы (5)


Это старая проблема, но я все еще публикую решение здесь. Решение оказалось очень простым. Все, что вам нужно сделать, это:

[_myProgressView setProgress:1];
[UIView animateWithDuration:10.0 animations:^{
    [_myProgressView layoutIfNeeded];
} completion:^(BOOL finished){
    if (finished) NSLog(@"animation finished");
}];

Вот хорошее объяснение того, почему у вас все работало неправильно: http://blog.spacemanlabs.com/2012/08/premature-completion-an-embarrassing-problem/

Таким образом, вы не можете использовать метод setProgress:animated: для анимации представления прогресса и получения желаемого поведения блока completion. Однако просто установка progress внутри блока не анимирует его, поскольку progress не является анимируемым свойством.

Обратитесь к документации Apple, в которой говорится, что свойство должно быть анимируемым: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/occ/clm/UIView/animateWithDuration:animations< /а>:

Таким образом, вы просто обновляете значение свойства progress вне блока анимации и выполняете компоновку представления внутри блока анимации.

person paresh    schedule 18.05.2016
comment
очень хорошо. В этом случае вы, вероятно, захотите использовать линейную временную кривую [UIView animateWithDuration:10.0 delay:0 options:UIViewAnimationOptionCurveLinear animations:^{ - person CupawnTae; 23.05.2017
comment
Правильный ответ: анимация анимации с использованием UIView.animate(:) неверна и не сбрасывает слой анимации в конце. - person Pedro Paulo Amorim; 08.09.2017
comment
Привет, ребята, не используйте эту строку, иначе вы не сможете увидеть анимацию. если вы переходите на следующий экран. [_myProgressView setProgress:1.0f animated:YES]; - person Anup Gupta; 22.12.2017
comment
этот ответ надо напечатать золотом и повесить на каждом перекрестке :) - person raistlin; 05.08.2019

Ни один из этих ответов не работал для меня. Мне удалось анимировать прогресс, но по какой-то причине в начале была задержка (особенно для более длительных периодов времени).

Ключевым для меня было вызвать layoutIfNeeded внутри блока анимации и дождаться завершения, прежде чем делать вторую анимацию. Этот код вызывается из viewDidLoad. С помощью следующего кода я смог добиться идеально гладкой анимации таймера.

    // For some reason, the first layoutIfNeeded() needs to be wrapped in an animation block, even though it is supposedly a synchronous call.  Otherwise, the progress just jumps right to 100%
    UIView.animate(withDuration: 0.0, animations: {
        self.progressView.layoutIfNeeded()
    }, completion: { finished in
        self.progressView.progress = 1.0

        UIView.animate(withDuration: self.timerDuration, delay: 0.0, options: [.curveLinear], animations: {
            self.progressView.layoutIfNeeded()
        }, completion: { finished in
            print("animation completed")
        })
    })
person kwahn    schedule 13.03.2019
comment
Не знаю почему, но это было единственное решение, которое сработало и для меня. - person Aleksander; 21.01.2020

Вместо этого сделайте это проще, используя NSTimer, например,

[progressview setProgress:0 animated:NO];
NSTimer *t=[NSTimer scheduledTimerWithTimeInterval: 1.0f
                                         target: self
                                       selector: @selector(progressupdate)
                                       userInfo: nil
                                        repeats: YES];

Ниже приведена функция, которая обновляет прогресс,

-(void)progressupdate{
   if(progressview.progress<1){
      [progressview setProgress:(progressview.progress+=0.2) animated:YES];
   }
   else{
      [t invalidate];
   }
} 

Надеюсь, поможет....

person Pradheep Narendran P    schedule 21.01.2016
comment
Анимация на основе таймера не такая плавная, как при использовании реальных классов анимации. - person Sentry.co; 19.12.2018

То, что я обнаружил, представляет собой набор как анимаций UIview, так и анимаций представления прогресса, которые работают намного лучше и плавно анимируют представление прогресса. Если вы просто используете комбинацию анимации и анимации прогресса, она запускается напрямую, независимо от таймера анимации UIview.

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    timer = [NSTimer scheduledTimerWithTimeInterval: 1.0f
                                             target: self
                                           selector: @selector(updateTimer)
                                           userInfo: nil
                                            repeats: YES];
}


- (void)updateTimer
{
    if (progressView.progress >= 1.0) {
        [timer invalidate];
    }
    [UIView animateWithDuration:1 animations:^{
        float newProgress = [self.progressView progress] + 0.125;
        [self.progressView setProgress:newProgress animated:YES];
    }];
}

Не стесняйтесь настраивать время анимации, чтобы переходы были еще лучше и плавнее.

person EmilDo    schedule 27.06.2014

Это старый вопрос, но у меня была похожая проблема, и вот мое решение (извините, это на Swift). Часть завершения вызывается, когда анимация заканчивается. Он находится внутри пользовательского класса UIProgressView.

override func setProgress(progress: Float, animated: Bool) {

    self.progress = progress
    if animated {
        let duration: NSTimeInterval = 10.0
        UIView.animateWithDuration(duration, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations: {
            self.layoutIfNeeded()
            }, completion: { (completed) in
                self.animationDuration = nil
        })
    } else {
        self.layoutIfNeeded()
    }
}
person Alexander Johmann    schedule 21.01.2016