Ideal Reality

興味の赴くままに

[iOS, Swift]で画面回転時のアニメーションを無効化する

iOSのアプリでは、Auto Layoutを使っていたり、適切にlayoutSubviewsでビューの配置を行なっていれば、画面が回転した際に綺麗にアニメーションしてくれます。

しかし、画面の向きによってレイアウトを変えていたり、drawRectを利用していたりと、複雑なレイアウトの場合はうまくアニメーションしてくれません。

回転時アニメーションあり(アニメーションを遅くしてます)

そんなときはいっそのこと画面回転のアニメーションを無効化した方が見栄えが良くなります。

Contents
スポンサーリンク

アプリ全体で画面回転のアニメーションを無効化する

AppDelegate.swiftのdidFinishLaunchingWithOptionsメソッド内に以下のコードを入れてください。

NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarOrientationNotification, object: nil, queue: nil) { (_) in
    UIView.setAnimationsEnabled(false)
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
        UIView.setAnimationsEnabled(true)
    })
}

応用

このままのコードだとアプリ全体で回転のアニメーションがオフになってしましますが、適切にif文を入れてやるとアニメーションのオンオフを制御できます。

ViewControllerごとに変える

AppDelegate.swiftのdidFinishLaunchingWithOptionsメソッドあたりを

var preventRotateAnimation = true

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarOrientationNotification, object: nil, queue: nil) { (_) in
        if !preventRotateAnimation { return }
        UIView.setAnimationsEnabled(false)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
            UIView.setAnimationsEnabled(true)
        })
    }

    return true
}

こんな感じにして、ViewControllerのviewDidAppearとviewWillDisappearで

(UIApplication.shared.delegate as? AppDelegate)?.preventRotateAnimation = true // 画面回転のアニメーションの無効化
(UIApplication.shared.delegate as? AppDelegate)?.preventRotateAnimation = false // 画面回転のアニメーションの有効化

こんな感じのコードを入れてやれば簡単にオンオフできます。

rootViewControllerのみアニメーションを無効化

上の方で載せているアニメーションは僕が公開しているRPN Anywhereというアプリなのですが、レイアウトが複雑なのは最初に表示される電卓のViewControllerだけで、あとは設定画面のTableViewControllerになります。

今回は、最初のViewControllerだけでアニメーションを無効化し、そこから画面遷移があった場合はアニメーションを有効化したかった。

だけど、上記のように変数を追加してViewControllerも書き換えてってのは面倒だったので、AppDelegateがUIWindowのインスタンスを保持していることをいいことに、

NotificationCenter.default.addObserver(forName: UIApplication.willChangeStatusBarOrientationNotification, object: nil, queue: nil) { (_) in
    if self.window?.rootViewController?.presentedViewController != nil { return }
    UIView.setAnimationsEnabled(false)
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
        UIView.setAnimationsEnabled(true)
    })
}

こんな感じのコードにしています。

スポンサーリンク

コメント

投稿されたコメントはありません

名前

メールアドレス(任意)

コメント

関連する投稿

‘configure(withApplicationID:)’ is deprecatedと言われた

NSTextFieldにadjustsFontSizeToFitWidthがないから自作する

[Swift]Dictionaryに順番を持たせたい

[SwiftUI]EnvironmentObjectをウィンドウごとに別々のインスタンスにしたい

iOS13でViewController上部の隙間をなくす

NO IMAGE

[SwiftUI]SwiftUI App Life Cycleに変更したアプリが、アップデートした端末で起動できない時の対処法