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

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

ただいまの
回答率

87.50%

selectorを使ってviewDidAppear内のメソッドを呼び出すには

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,263

score 12

現在、アプリの起動直後にボタンが回転するというアニメーションを
考えています。
少々説明が難しいのですが、質問させていただきます。
質問内容は下記の通りです。
■質問内容
selectorを使ってviewDidAppear内のメソッドを呼び出すには
どのようなコーディングをすべきでしょうか。

はじめは、下記の様なコーディングをしました。

   class LoginViewController: UIViewController {
      @IBOutlet weak var buttonTest: UIButton!
         override func viewDidLoad() {
            super.viewDidLoad()
            Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(self.rotate), userInfo: nil, repeats: true)
         }
         func rotate() {
            UIView.animate(withDuration: 6.0, delay: 0.0, options: [.curveEaseInOut], animations: {
            回転の処理
             }
         }
      }

ボタンは回転するのですが、
アプリが起動して数秒たってから回転のアニメーションが始まってしまいます。
アプリが起動してアニメーションが始まるまでの時間を短くさせたくて
下記の様なコーディングをしました。

   class LoginViewController: UIViewController {
      @IBOutlet weak var buttonTest: UIButton!
         override func viewDidLoad() {
            super.viewDidLoad()
            Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(self.rotate), userInfo: nil, repeats: true)
         }
         override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated: Bool)
            func rotate() {
               UIView.animate(withDuration: 6.0, delay: 0.0, options: [.curveEaseInOut], animations: {
            回転の処理
             }
         }
      }


上記のようなコーディングをすると、#selectorのところで以下の様なエラーに
なりました。

イメージ説明

そのため、#selectorのところを下記の様にコーディングをしてみました。

      Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(LoginViewController.viewAppear(_:)), userInfo: nil, repeats: true)


コーディングエラーはでないものの
実機テストを行うとアニメーションが始まらなくなりました。
selectorを使ってviewDidAppear内のrotate()を呼び出すには
どのようなコーディングをすべきでしょうか。
お手数をおかけいたしますが、ご教示いただけますでしょうか。
よろしくお願い致します。

■補足
swift:3
xcode:8.2.1

  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+1

質問のエラーが出ている原因は、必要な処理をselectorでうまく指定できないためであり、
その問題の根本はメソッド(viewDidAppear)内でメソッド(rotate)を定義しているためですが、
そのエラーを直しても「アプリが起動してアニメーションが始まるまでの時間を短くさせたくて」
というのは実現できません。

Timer.scheduledTimer(timeInterval: 8.0, 〜の意味はわかっているでしょうか?
これは、タイマーをかけて8秒後にselectorで指定したメソッドを呼び出すということですから、
これを使ってアニメーション開始することを修正しない限り、
どうやってもアニメーション開始までに8秒かかります。

画面を表示してすぐにアニメーションしたいのであれば、
タイマーをかけてselector指定でアニメーションを開始するのではなく、
viewDidAppearから直接rotateメソッドを呼び出せばいいです。

(修正例)

   class LoginViewController: UIViewController {
      @IBOutlet weak var buttonTest: UIButton!
         override func viewDidLoad() {
            super.viewDidLoad()
            // Timer.scheduledTimerは使用しない
         }
         override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated: Bool)
            // rotateメソッド定義
            func rotate() {
               // アニメーション開始
               UIView.animate(withDuration: 6.0, delay: 0.0, options: [.curveEaseInOut], animations: {
                   // 回転の処理
               }
            }
            // rotateメソッド呼び出し
            rotete()
      }

ただ、rotateメソッドをviewDidAppearの中からしか使わないのであれば、
そもそもメソッド内にメソッドなど定義せずに直接アニメーション処理した方がスッキリします。

         override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated: Bool)
            // アニメーション開始
            UIView.animate(withDuration: 6.0, delay: 0.0, options: [.curveEaseInOut], animations: {
                // 回転の処理
            }
         }

これで十分だと思います。


(5/19 11:15追記)

リピートタイマーを使ってアニメーションを繰り返すのであれば、
以下のようにすればいいです。

(例)

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!

    // タイマー
    var timer = Timer()

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // リピートタイマー開始
        timer = Timer.scheduledTimer(timeInterval: 8.0, target: self, selector: #selector(self.rotate), userInfo: nil, repeats: true)
        // 初回だけすぐにタイムアウトさせる
        timer.fire()
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        timer.invalidate()
    }

    func rotate() {
        self.button.transform =  CGAffineTransform(rotationAngle: 0)
        // アニメーション開始
        UIView.animate(withDuration: 6.0, delay: 0.0, options: [.curveEaseInOut], animations: {
            // 回転の処理
            self.button.transform =  CGAffineTransform(rotationAngle: CGFloat.pi / 2)
        })
    }
}

ポイントは、タイマーはviewDidLoadではなくviewDidAppearのタイミングで開始することと、rotateメソッドはviewDidAppearの外で定義することです(でないとタイマーから呼び出せません)。

そうした上で、タイマー開始直後にtimer.fire()を使えば、初回だけすぐにタイムアウトさせて、すぐにアニメーションを開始することができます。

それと、ここから別の画面に遷移するなら、その時にタイマーを止める必要がありますので、念のためにviewWillDisappearでタイマーを止める処理も入れておきました。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/19 09:21 編集

    回答頂いたとおりviewDidLoad内でTimer.scheduledTimerは記述せずに、viewDidAppear内でUIView.animateを記述すると起動してすぐにアニメーションが開始することができました。ありがとうございます。また、質問を重ねる形になり申し訳ないのですが。回転のアニメーション自体は約3秒ほどで終了するものです。アプリが起動して(約3秒間の)回転のアニメーションを8秒ごとに実施したいと思い、viewDidLoad内でTimer.scheduledTimerを使用していました。プログラミングを独学でやっていてつまづくことが多く悩んでしまうのですが、回転のアニメーションを8秒ごとに実施したい場合、Timer.scheduledTimerを使用してコーディングをすべきでしょうか。また、Timer.scheduledTimerを使わない方法などございましたら、ご教示いただけますでしょうか。

    キャンセル

  • 2017/05/19 10:02 編集

    回答するのもあれなのでここに書きます。ちょっと見づらいですけど。

    時間の指定は、全体で8.0sec、回転処理は3.0sec(8.0*0.375)となっています。
    UIView.animateKeyframes(withDuration: 8.0, delay: 0.0, options: [.repeat], animations: {
    UIView.addKeyframe(withRelativeStartTime: 0.0, relativeDuration: 0.375, animations: {
    //回転処理
    })
    }, completion: nil)

    キャンセル

  • 2017/05/19 11:16

    なるほど、アニメーションを8秒ごとに繰り返す目的があって「repeats:true」のリピートタイマーを起動していたのですね。
    見落としていました。

    fuzzballさんのやり方でもできると思いますが、burdockさんのやり方を尊重して、リピートタイマーを使ってアニメーションを繰り返すならこうするという例を回答に追記しておきます。

    キャンセル

  • 2017/05/19 18:31 編集

    回答コードを元に再度コーディングさせていただいたところ、自分の思い描いたアニメーションに非常に近くなりました。viewWillDiappearも使用するなどライフサイクルの勉強になりました。ありがとうございます。

    キャンセル

0

反復してアニメーションさせるためにTimerを試されたのでしょうか?
UIViewAnimationOptionsには反復に関する設定があるようですので、こちらを利用するという手が使えるかもしれません。
なお、オートレイアウト機能などとの干渉により、viewDidLoadの時点でアニメーションさせようとすると意図通りにならない可能性がありますので、TakeOneさんのようにviewDidAppearまで待った方がよさそうです。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/05/19 09:42

    質問の説明不足で申し訳ありません。以前UIViewAnimationOptionsのrepeatCount = MAXFLOATなど試しましたが、絶え間なく回転し続けてしまい、なんだか忙しいアニメーションだったので、約3秒間の回転アニメーションをアプリが起動して8秒間隔で実施したいと思い、質問内容のようにTimerを使いました。回答いただきありがとうございます。

    キャンセル

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

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

関連した質問

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