質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.95%

ホームボタン押下時のアニメーションの処理

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 2,458

score 214

swiftに関する質問です。
現在下記のように20秒かけてバーを減少さるアニメーションを行っています。
ホームボタン押下時にこのバーを一度停止したいためAppDelegate.swiftでtimerbarstop()を実行しています。

再び画面を開いた際にバーの減少を停止状態から再開するように考えているのですが、現状、再び画面を開くとバーが消えてしまっている状態です。

状況だけで大変恐縮ですが、どのようなことが考えられるかをご教示いただけないでしょうか。

よろしくお願いいたします。

KenteiViewController.swift


private var myImageView: UIImageView!

    func timerbar(){
        let barHeight = scHei*0.011
        let barWidth = scWid*0.58
        let barXPosition = scWid*0.14
        let barYPosition = scHei*0.367
        let barXPositionEnd = barXPosition + barWidth
        // UIImageViewを作成する.
        myImageView = UIImageView()
        // 表示する画像を設定する.
        let myImage = UIImage(named: "jikanbar.jpg")
        // 画像をUIImageViewに設定する.
        myImageView.image = myImage
        // 画像の表示する座標を指定する.
        myImageView.frame = CGRectMake(barXPosition ,barYPosition ,barWidth ,barHeight)
        // UIImageViewをViewに追加する.
        self.view.addSubview(myImageView)

        UIView.animateWithDuration(20, delay: 0.0, options : UIViewAnimationOptions.CurveLinear, animations: {() -> Void  in
            self.myImageView.frame = CGRectMake(barXPositionEnd, barYPosition, 0, barHeight)

            },
            completion: {(finished: Bool) -> Void in
            // アニメーション終了後の処理
        })

    }

    //制限時間バーを停止するメソッド
    func timerbarstop(){
        let pausedTime : CFTimeInterval! = myImageView?.layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
        myImageView?.layer.speed = 0.0
        myImageView?.layer.timeOffset = pausedTime
    }
AppDelegate.swift

func applicationDidEnterBackground(application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        let kentei = KenteiViewController()
        kentei.timerbarstop()
    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

0

まず、提示されたコードだとapplicationDidEnterBackground()内で新たにKenteiViewControllerを作ってしまっているため、アニメーションの停止自体ができていません。
すでに存在しているオブジェクトがあるはずなのでそちらのtimerbarstop()を呼び出すようにしなければ意味がありません。

その修正をしてもアニメーションが消えるのは変わりません。
バックグラウンドに行くとアニメーションが消えてしまうからみたいです。(知らなかった…)

それの回答ですがやっぱり StackOverflow にあります。

簡単に言うと、バックグラウンドに行く時(applicationDidEnterBackground())にanimationForKey()でCAAnimationオブジェクトをメンバ変数等に保存しておいて、フォアグラウンドに復帰する際(applicationWillEnterForeground())に再度 addAnimation(forKey:) で追加し直してあげればいいです。

 6/26追記

一応動かせるコードを載せます。

  • AppDelegateからViewControllerを探すのは面倒ですし画面構成によって探し方も変わるのでViewController内で通知を受信してアニメーションをポーズさせるように変更しました。
  • アニメーション開始のコードがなかったのでviewDidAppearでとりあえず開始するようにしました。
  • scHeiとscWidってなんなのかわかんなかったのでとりあえず画面サイズっぽい値を入れてみました。
KenteiViewController.swift

class KenteiViewController: UIViewController {

    private var myImageView: UIImageView!
    var pausedAnimations: [String: CAAnimation]?

    override func viewDidLoad() {
        super.viewDidLoad()

        // バックグラウンドへ遷移する通知の受信開始
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(KenteiViewController.didEnterBackgroundNotification(_:)), name: UIApplicationDidEnterBackgroundNotification, object: nil)
        // バックグラウンドから復帰する通知の受信開始
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(KenteiViewController.willEnterForegroundNotification(_:)), name: UIApplicationWillEnterForegroundNotification, object: nil)
    }

    deinit {
        // バックグラウンドへ遷移する通知の受信解除
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIApplicationDidEnterBackgroundNotification, object: nil)
        // バックグラウンドから復帰する通知の受信解除
        NSNotificationCenter.defaultCenter().removeObserver(self, name: UIApplicationWillEnterForegroundNotification, object: nil)
    }

    // バックグラウンドへ遷移する通知受信時の処理
    func didEnterBackgroundNotification(notification: NSNotification) {
        guard let myImageView = myImageView else { return }

        // myImageViewのレイヤに設定されているCAAnimationオブジェクトをpausedAnimationsに保持する
        var animations = [String: CAAnimation]()
        myImageView.layer.animationKeys()?.forEach({ (key) in
            animations[key] = myImageView.layer.animationForKey(key)
        })
        if !animations.isEmpty {
            pausedAnimations = animations
            pauseLayer(myImageView.layer)
        }
    }

    // バックグラウンドから復帰する通知受信時の処理
    func willEnterForegroundNotification(notification: NSNotification) {
        guard let myImageView = myImageView else { return }

        if let animations = pausedAnimations {
            // pausedAnimationsに保持していたCAAnimationオブジェクトをmyImageViewのレイヤに追加し直す
            animations.forEach({ (key, animation) in
                myImageView.layer.addAnimation(animation, forKey: key)
            })
            self.pausedAnimations = nil
            resumeLayer(myImageView.layer)
        }
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)

        // とりあえずココでアニメーション開始
        timerbar()
    }

    // よくわかんないので画面サイズっぽい値にする
    let scHei: CGFloat = 480.0
    let scWid: CGFloat = 320.0

    func timerbar(){
        let barHeight = scHei*0.011
        let barWidth = scWid*0.58
        let barXPosition = scWid*0.14
        let barYPosition = scHei*0.367
        let barXPositionEnd = barXPosition + barWidth
        // UIImageViewを作成する.
        myImageView = UIImageView()
        // 表示する画像を設定する.
        let myImage = UIImage(named: "jikanbar.jpg")
        // 画像をUIImageViewに設定する.
        myImageView.image = myImage
        // 画像の表示する座標を指定する.
        myImageView.frame = CGRectMake(barXPosition ,barYPosition ,barWidth ,barHeight)
        // UIImageViewをViewに追加する.
        self.view.addSubview(myImageView)

        UIView.animateWithDuration(20, delay: 0.0, options : UIViewAnimationOptions.CurveLinear, animations: {() -> Void  in
            self.myImageView.frame = CGRectMake(barXPositionEnd, barYPosition, 0, barHeight)
            }, completion: {(finished: Bool) -> Void in
                // アニメーション終了後の処理
        })

    }

    func pauseLayer(layer: CALayer) {
        let pausedTime = layer.convertTime(CACurrentMediaTime(), fromLayer: nil)
        layer.speed = 0.0
        layer.timeOffset = pausedTime
    }

    func resumeLayer(layer: CALayer) {
        let pausedTime = layer.timeOffset
        layer.speed = 1.0
        layer.timeOffset = 0.0
        layer.beginTime = 0.0
        let timeSincePause = layer.convertTime(CACurrentMediaTime(), fromLayer: nil) - pausedTime
        layer.beginTime = timeSincePause
    }
}

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/06/28 22:33

    度々の質問大変恐縮ですが、最後に1点だけ質問をよろしいでしょうか。
    ホームボタンで戻った際にcompletionのfinishedがfalseで返ってきてしまいます。
    「completion: {(finished: Bool) -> Void in」の後に次のアニメーションの処理などを行いたいと考えているのため、ホームボタンで戻った際にfalseが返らないようにし、かつ、復帰後のアニメーション終了時にtrueを返すような方法を取ることは可能でしょうか。
    ご教示いただければ幸いです。

    キャンセル

  • 2016/06/28 22:51

    追加し直したとはいえ、元のアニメーション自体はキャンセルされていますので完了ブロックがfalseで呼ばれてしまうのは当然と言えば当然の動作なんですよね。
    なので、
    > ホームボタンで戻った際にfalseが返らないようにし、かつ、復帰後のアニメーション終了時にtrueを返すような方法
    は不可能と思います。

    キャンセル

  • 2016/06/29 00:42

    やはり不可能ですか……。度々のご回答ありがとうございました。
    また、よろしくお願いいたします!

    キャンセル

0

間違って投稿したので削除

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.95%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る