[iOS] UIProgressViewのprogressに0.0以下、1.0以上の値を指定した時の挙動

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

こんにちは。モバイルアプリサービス部の加藤潤です。
今回はUIProgressViewprogress0.0以下、1.0以上の値を設定した時の挙動を調べてみました。

開発環境

  • Xcode 7.3.1 (7D1014)
  • iPhone 6s シミュレータ / iOS 9.3 (13E230)
  • Swift 2.2

UIProgressViewとは

本題に入る前にごく簡単にUIProgressViewの説明をしておきます。 UIProgressViewはファイルのダウンロードなど、作業の進捗状況を表す際に使用されるUIコンポーネントです。

スタイルがUIProgressViewStyle.Defaultだとこんなの。 ios-progress-style-default

UIProgressViewStyle.Barだとこんなのです。 ios-progress-style-bar

進捗値の指定について

さて、ここからが本題です。
UIProgressViewの進捗値はprogressプロパティに値を指定するか、public func setProgress(progress: Float, animated: Bool)関数で指定します。関数の方は進捗値とあわせてアニメーションの有無も指定することが可能です。
※iOS 9からはobservedProgressも使用可能ですが、本記事では扱いません。

この進捗値ですが、クラスリファレンスには以下のように書かれています。

The current progress is represented by a floating-point value between 0.0 and 1.0, inclusive, where 1.0 indicates the completion of the task. The default value is 0.0. Values less than 0.0 and greater than 1.0 are pinned to those limits.

値の範囲としては0.01.0のようですが、範囲外の値を指定した時の挙動がどうなるのか気になったので調べてみました。

1.0以上の進捗値を指定した場合

まずは1.0以上のprogressを指定した場合の挙動を確認してみます。
確認用に以下のようなアプリを用意しました。

ios-progress-view-sample-app

setProgress関数を各ボタンに書かれた進捗値でそれぞれアニメーションのありなしのパターンで呼び出すことができるようにしました。
ソースコードは以下のようになっています。

import UIKit

class ViewController: UIViewController {

    /// プログレスビュー
    @IBOutlet weak var progressView: UIProgressView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        reset()
    }

    // MARK: - アニメーションなし

    @IBAction func didTapNonAnimated1_0Button(sender: AnyObject) {
        // 1.0
        setProgress(1.0, animated: false)
    }
    @IBAction func didTapNonAnimated5_0Button(sender: AnyObject) {
        // 5.0
        setProgress(5.0, animated: false)
    }

    @IBAction func didTapNonAnimated10_0Button(sender: AnyObject) {
        // 10.0
        setProgress(10.0, animated: false)
    }

    // MARK: - アニメーションあり

    @IBAction func didTapAnimated1_0Button(sender: AnyObject) {
        // 1.0
        setProgress(1.0, animated: true)
    }

    @IBAction func didTapAnimated5_0Button(sender: AnyObject) {
        // 5.0
        setProgress(5.0, animated: true)
    }

    @IBAction func didTapAnimated10_0Button(sender: AnyObject) {
        // 10.0
        setProgress(10.0, animated: true)
    }

    @IBAction func didTapResetButton(sender: AnyObject) {
        reset()
    }

    // MARK: - Private

    private func reset() {
        progressView.progress = 0.0
    }

    private func setProgress(progress: Float, animated: Bool) {
        CATransaction.setCompletionBlock {
            NSLog("finish animation")
        }
        progressView.setProgress(progress, animated: animated)
        NSLog("begin animation")
    }

}

setProgressのアニメーションの時間を計測するためにCATransactionsetCompletionBlockを実行しています。

実行結果(1.0以上の進捗値)

このアプリを動かしてみた結果がこちらです。

ios-progress-view-sample-app

見てわかる通り、アニメーションがない場合は1.0, 5.0, 10.0全て一瞬で進捗率が100%となります。
アニメーションしないのだから当然の動きです。
対してアニメーションがある場合は1.0, 5.0, 10.0の全てが最終的には進捗率100%となりますが、アニメーションの時間が異なります。 1.0の場合はおよそ1秒、5.0の場合はおよそ5秒、10.0の場合はおよそ10秒となっています。 このことが何を意味するか...つまり極端に大きい数をprogressに指定してしまうと、アニメーションの時間が長くなるので、見た目にはプログレスバーの進捗がほとんど進まなくなってしまうということです。

0.0以下の進捗値を指定した場合

次に0.0以下のprogressを指定した場合の挙動も確認してみます。

以下のようなアプリを用意しました。 ios-progress-view-sample-app-minus

ソースコードは以下のようになっています。

import UIKit

class ViewController: UIViewController {

    /// プログレスビュー
    @IBOutlet weak var progressView: UIProgressView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        reset()
    }

    // MARK: - アニメーションなし

    @IBAction func didTapNonAnimated0_0Button(sender: AnyObject) {
        // 0.0
        setProgress(0.0, animated: false)
    }

    @IBAction func didTapNonAnimatedMinus5_0Button(sender: AnyObject) {
        // -5.0
        setProgress(-5.0, animated: false)
    }

    @IBAction func didTapNonAnimatedMinus10_0Button(sender: AnyObject) {
        // -10.0
        setProgress(-10.0, animated: false)
    }

    // MARK: - アニメーションあり

    @IBAction func didTapAnimated0_0Button(sender: AnyObject) {
        // 0.0
        setProgress(0.0, animated: true)
    }

    @IBAction func didTapAnimatedMinus5_0Button(sender: AnyObject) {
        // -5.0
        setProgress(-5.0, animated: true)
    }

    @IBAction func didTapAnimatedMinus10_0Button(sender: AnyObject) {
        // -10.0
        setProgress(-10.0, animated: true)
    }

    @IBAction func didTapResetButton(sender: AnyObject) {
        reset()
    }

    // MARK: - Private

    private func reset() {
        progressView.progress = 1.0
    }

    private func setProgress(progress: Float, animated: Bool) {
        CATransaction.setCompletionBlock {
            NSLog("finish animation")
        }
        progressView.setProgress(progress, animated: animated)
        NSLog("begin animation")
    }

}

実行結果(0.0以下の進捗値)

このアプリを動かしてみた結果がこちらです。

progress_minus

アニメーションがない場合は0.0, -5.0, -10.0全て一瞬で進捗率が0%となります。 対してアニメーションがある場合は0.0, -5.0, -10.0の全てが最終的には進捗率0%となりますが、やはりアニメーションの時間が異なります。 0.0の場合はおよそ1(1 - 0.0)秒、-5.0の場合はおよそ6(1 - (-5.0))秒、-10.0の場合はおよそ11(1 - (-10.0))秒となっています。 つまりこちらについても極端に小さい数をprogressに指定してしまうと、アニメーションの時間が長くなるので、見た目にはプログレスバーの進捗がほとんど進まなくなってしまいます。

まとめ

今回調査した内容をまとめると以下になります。

  • 1.0を超える極端に大きい数をUIProgressViewprogressに指定してしまうと、アニメーションする場合にプログレスバーの進捗がほとんど進まなくなってしまう
  • 0.0未満の極端に小さい数をUIProgressViewprogressに指定してしまうと、アニメーションする場合にプログレスバーの進捗がほとんど進まなくなってしまう
  • UIProgressViewは進捗値とは別にアニメーションの時間を指定するようなことはできないが、progressの値を調整することで間接的にアニメーションの時間を調整できる。ただし、上記のようなことが起こるため、極端に大きい数、小さい数を指定しないようにする必要がある。
  • アニメーションの時間に特にこだわりがない場合はドキュメントに書いてある通り、0.01.0の範囲の値を指定するようにしましょう。

参考

UIProgressView Class Reference