Как оживить увеличивающийся номер в UILabel - PullRequest
34 голосов
/ 12 февраля 2012

У меня есть метка, показывающая число, и я хочу изменить его на большее число, однако я бы хотел добавить к нему немного блика.Я бы хотел, чтобы число увеличивалось до более высокого числа с кривой легкого ввода-вывода, чтобы она ускорялась, а затем замедлялась.В этом ответе показано, как увеличить его (2-й ответ, а не принятый ответ), но я бы предпочел анимировать его, чтобы я мог немного увеличить его размер, а затем снова уменьшить, а также кривую замедления. как сделать анимацию бегового счета в iphone sdk

Есть идеи, как этого добиться?Спасибо

Начальные / конечные числа будут введены пользователем, и я хочу, чтобы они увеличивали конечный номер за то же время.Поэтому, если у меня есть начало 10, конец 100 или начало 10, конец 1000, я хочу, чтобы он считал до конечного числа, скажем, за 5 секунд.

Ответы [ 9 ]

68 голосов
/ 12 февраля 2013

Я действительно создал такой класс только для этого, который называется UICountingLabel:

http://github.com/dataxpress/UICountingLabel

Позволяет указать, хотите ли вы, чтобы режим подсчета был линейным, упрощенным, упрощенным или упрощенным. Простота входа / выхода начинает отсчитываться медленно, ускоряется, а затем медленно заканчивается - все за указанное вами время.

В настоящее время он не поддерживает установку фактического размера шрифта надписи на основе текущего значения, хотя я могу добавить поддержку для этого, если это востребованная функция. У большинства надписей в моих макетах недостаточно места для роста или сжатия, поэтому я не уверен, как вы хотите их использовать. Тем не менее, он ведет себя как обычная метка, поэтому вы также можете изменить размер шрифта самостоятельно.

17 голосов
/ 07 февраля 2015

Вы можете использовать GCD для смещения задержек в фоновые потоки.

Вот пример анимации значения (от 1 до 100 за 10 секунд)

float animationPeriod = 10;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    for (int i = 1; i < 101; i ++) {
        usleep(animationPeriod/100 * 1000000); // sleep in microseconds
        dispatch_async(dispatch_get_main_queue(), ^{
            yourLabel.text = [NSString stringWithFormat:@"%d", i];
        });
    }
});
13 голосов
/ 05 декабря 2016

Здесь ответ @ malex в быстрой версии 3.

func incrementLabel(to endValue: Int) {
    let duration: Double = 2.0 //seconds
    DispatchQueue.global().async {
        for i in 0 ..< (endValue + 1) {
            let sleepTime = UInt32(duration/Double(endValue) * 1000000.0)
            usleep(sleepTime)
            DispatchQueue.main.async {
                self.myLabel.text = "\(i)"
            }
        }
    }
}

Однако я настоятельно рекомендую просто загрузить этот класс из GitHub и перетащить его в ваш проект. Я прибег к использованиюэто потому, что время в моем коде, кажется, не корректируется для меньшего / большего числа чисел.Этот класс отлично работает и выглядит очень хорошо.См. эту среднюю статью для справки.

4 голосов
/ 12 февраля 2012

Вы можете использовать флаг, чтобы увидеть, должен ли он подниматься или опускаться.Вместо цикла for используйте цикл while.Таким образом, вы создаете цикл, который продолжает работать, поэтому вам нужно найти способ остановить его, например, нажатием кнопки.

0 голосов
/ 13 декабря 2018

Код Swift 4:

let animationPeriod: Float = 1
    DispatchQueue.global(qos: .default).async(execute: {
        for i in 1..<Int(endValue) {
            usleep(useconds_t(animationPeriod / 10 * 10000)) // sleep in microseconds
            DispatchQueue.main.async(execute: {
                self.lbl.text = "\(i+1)"
            })
        }
    })
0 голосов
/ 24 сентября 2018

Код Swift 4

let animationPeriod: Float = 1
    DispatchQueue.global(qos: .default).async(execute: {
        for i in 1..<10000)! {
            usleep(useconds_t(animationPeriod / 10 * 10000)) // sleep in microseconds
            DispatchQueue.main.async(execute: {
                self.lblCounter.text = "\(i)"
            })
        }
    })
0 голосов
/ 14 марта 2018

Подробнее

Xcode 9.2, swift 4

Решение

class LoadingProcess {

    let minValue: Int
    let maxValue: Int
    var currentValue: Int

    private let progressQueue = DispatchQueue(label: "ProgressView")
    private let semaphore = DispatchSemaphore(value: 1)

    init (minValue: Int, maxValue: Int) {
        self.minValue = minValue
        self.currentValue = minValue
        self.maxValue = maxValue
    }

    private func delay(stepDelayUsec: useconds_t, completion: @escaping ()->()) {
        usleep(stepDelayUsec)
        DispatchQueue.main.async {
            completion()
        }
    }

    func simulateLoading(toValue: Int, step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
                         valueChanged: @escaping (_ currentValue: Int)->(),
                         completion: ((_ currentValue: Int)->())? = nil) {

        semaphore.wait()
        progressQueue.sync {
            if currentValue <= toValue && currentValue <= maxValue {
                usleep(stepDelayUsec!)
                DispatchQueue.main.async {
                    valueChanged(self.currentValue)
                    self.currentValue += step
                    self.semaphore.signal()
                    self.simulateLoading(toValue: toValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
                }

            } else {
                self.semaphore.signal()
                completion?(currentValue)
            }
        }
    }

    func finish(step: Int = 1, stepDelayUsec: useconds_t? = 10_000,
                valueChanged: @escaping (_ currentValue: Int)->(),
                completion: ((_ currentValue: Int)->())? = nil) {
        simulateLoading(toValue: maxValue, step: step, stepDelayUsec: stepDelayUsec, valueChanged: valueChanged, completion: completion)
    }
}

Использование

let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)

loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
    // Update views
})

DispatchQueue.global(qos: .background).async {
    print("Start loading data")
    sleep(5)
    print("Data loaded")
    loadingProcess.finish(valueChanged: { currentValue in
        // Update views
    }) { _ in
        print("End")
    }
}

Полный образец

Не забудьте добавить код решения здесь

import UIKit

class ViewController: UIViewController {

    weak var counterLabel: UILabel!
    weak var progressView: UIProgressView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let stackView = UIStackView()
        stackView.axis = .vertical
        stackView.alignment = .fill
        stackView.distribution = .fillProportionally
        stackView.spacing = 8
        stackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stackView)
        stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 80).isActive = true
        stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -80).isActive = true

        let label = UILabel()
        label.textAlignment = .center
        label.text = "0"
        label.font = UIFont.systemFont(ofSize: 46)
        stackView.addArrangedSubview(label)
        counterLabel = label

        let progressView = UIProgressView()
        progressView.trackTintColor = .lightGray
        progressView.progressTintColor = .blue
        progressView.layer.cornerRadius = 4
        progressView.clipsToBounds = true
        progressView.heightAnchor.constraint(equalToConstant: 8).isActive = true
        stackView.addArrangedSubview(progressView)
        self.progressView = progressView

        let button = UIButton()
        button.setTitle("Start", for: .normal)
        button.addTarget(self, action: #selector(startButtonTapped), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        button.heightAnchor.constraint(equalToConstant: 30).isActive = true
        stackView.addArrangedSubview(button)
    }

    @objc func startButtonTapped() {
        sample()
    }

    private func setProcess(currentValue: Int) {
        let value = 0.01 * Float(currentValue)
        self.counterLabel?.text = "\(currentValue)"
        self.progressView?.setProgress(value, animated: true)
        print("\(currentValue)")
    }

    func sample() {

        let loadingProcess = LoadingProcess(minValue: 0, maxValue: 100)

        loadingProcess.simulateLoading(toValue: 80, valueChanged: { currentValue in
            self.setProcess(currentValue: currentValue)
        })

        DispatchQueue.global(qos: .background).async {
            print("Start loading data")
            sleep(5)
            print("Data loaded")
            loadingProcess.finish(valueChanged: { currentValue in
                self.setProcess(currentValue: currentValue)
            }) { _ in
                print("end")
            }
        }
    }
}

Результаты

enter image description here

0 голосов
/ 29 декабря 2016

Вы также можете проверить https://github.com/leszek-s/LSCategories

Это позволяет увеличивать / уменьшать число в UILabel с помощью одной строки кода, например:

[self.label lsAnimateCounterWithStartValue:10 endValue:100 duration:5 completionBlock:nil];
0 голосов
/ 29 мая 2015

Вот как я это сделал:

- (void)setupAndStartCounter:(CGFloat)duration {
    NSUInteger step = 3;//use your own logic here to define the step.
    NSUInteger remainder = numberYouWantToCount%step;//for me it was 30

    if (step < remainder) {
        remainder = remainder%step;
    }

    self.aTimer = [self getTimer:[self getInvocation:@selector(animateLabel:increment:) arguments:[NSMutableArray arrayWithObjects:[NSNumber numberWithInteger:remainder], [NSNumber numberWithInteger:step], nil]] timeInterval:(duration * (step/(float) numberYouWantToCountTo)) willRepeat:YES];
    [self addTimerToRunLoop:self.aTimer];
}

- (void)animateLabel:(NSNumber*)remainder increment:(NSNumber*)increment {
    NSInteger finish = finalValue;

    if ([self.aLabel.text integerValue] <= (finish) && ([self.aLabel.text integerValue] + [increment integerValue]<= (finish))) {
        self.aLabel.text = [NSString stringWithFormat:@"%lu",(unsigned long)([self.aLabel.text integerValue] + [increment integerValue])];
    }else{
        self.aLabel.text = [NSString stringWithFormat:@"%lu",(unsigned long)([self.aLabel.text integerValue] + [remainder integerValue])];
        [self.aTimer invalidate];
        self.aTimer = nil;
    }
}

#pragma mark -
#pragma mark Timer related Functions

- (NSTimer*)getTimer:(NSInvocation *)invocation timeInterval:(NSTimeInterval)timeInterval willRepeat:(BOOL)willRepeat
{
    return [NSTimer timerWithTimeInterval:timeInterval invocation:invocation repeats:willRepeat];
}

- (NSInvocation*)getInvocation:(SEL)methodName arguments:(NSMutableArray*)arguments
{
    NSMethodSignature *sig = [self methodSignatureForSelector:methodName];
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig];
    [invocation setTarget:self];
    [invocation setSelector:methodName];
    if (arguments != nil)
    {
        id arg1 = [arguments objectAtIndex:0];
        id arg2 = [arguments objectAtIndex:1];
        [invocation setArgument:&arg1 atIndex:2];
        [invocation setArgument:&arg2 atIndex:3];
    }
    return invocation;
}

- (void)addTimerToRunLoop:(NSTimer*)timer
{
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...