web-dev-qa-db-ja.com

UILabelでインクリメントする数値をアニメーション化する方法

番号を示すラベルがあり、それをより高い番号に変更したいのですが、少しフレアを追加したいと思います。速度を上げてから遅くするために、イージーインアウトカーブを使用して、数値をより高い数値まで増やしたいと思います。この答えは、それを増分する方法を示しています(受け入れられた答えではなく、2番目の答え)が、アニメーション化して、サイズを少し大きくしてから再び縮めるようにするだけでなく、イージングインアウトカーブもできます。 iPhone SDKでランニングスコアアニメーションを行う方法

これを達成する最善の方法はありますか?ありがとう

開始/終了番号はユーザーが入力するので、同じ時間内に終了番号を増やしてほしい。したがって、開始10終了100または開始10終了1000がある場合、5秒で終了番号までカウントするようにします。

36
Darren

フラグを使用して、上下する必要があるかどうかを確認できます。 forループの代わりに、whileループを使用します。このように、あなたは継続して行くループを作成しているので、それを停止する方法も見つける必要があります。ボタンを押す。

4

実際に、UICountingLabelと呼ばれるこのためのクラスを作成しました。

http://github.com/dataxpress/UICountingLabel

カウントモードを線形、イーズイン、イーズアウト、イーズイン/アウトのいずれにするかを指定できます。イーズイン/アウトは、ゆっくりとカウントを開始し、スピードアップし、ゆっくりと終了します-すべて指定した時間になります。

現在、現在の値に基づいてラベルの実際のフォントサイズを設定することはサポートしていませんが、必要な機能である場合はサポートを追加できます。私のレイアウトのほとんどのラベルには、拡大または縮小する余地があまりないため、どのように使用するのかわかりません。ただし、通常のラベルとまったく同じように動作するため、独自にフォントサイズを変更することもできます。

68
Tim

GCDを使用して、遅延をバックグラウンドスレッドにシフトできます。

バリューアニメーションの例(10秒で1から100)

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];
        });
    }
});
17
malex

ここでSwift 3.の@malexの答え.

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からこのクラスをダウンロードする を強くお勧めします。プロジェクトにドラッグします。コードのタイミングがカウントの増減に適切に調整されていないようであるため、使用することに頼りました。数字。このクラスはうまく機能し、見た目もとても良いです。 この中記事 を参照してください。

13
Travis M.

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
kishor soneji

詳細

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

これは私がそれをやった方法です:

- (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];
}
0
Simon

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
kishor soneji

https://github.com/leszek-s/LSCategories も確認できます

次のような1行のコードで、UILabelで数値をインクリメント/デクリメントできます。

[self.label lsAnimateCounterWithStartValue:10 endValue:100 duration:5 completionBlock:nil];
0
Leszek Szary