Se ha denunciado esta presentación.
Utilizamos tu perfil de LinkedIn y tus datos de actividad para personalizar los anuncios y mostrarte publicidad más relevante. Puedes cambiar tus preferencias de publicidad en cualquier momento.

意外と苦労する、一部の画面のみ ランドスケープ表示を許容する方法 (potatotips 第17回)

10.874 visualizaciones

Publicado el

意外と苦労する、一部の画面のみランドスケープ表示を許容する方法

Publicado en: Ingeniería
  • Sé el primero en comentar

意外と苦労する、一部の画面のみ ランドスケープ表示を許容する方法 (potatotips 第17回)

  1. 1. 意外と苦労する、一部の画面のみ ランドスケープ表示を許容する方法 2015/05/13【第17回】potatotips(iOS/Android開発Tips共有会)
  2. 2. 自己紹介 • Twitter: @_mono, Facebook: mono0926 • みんなでつくるスポーツニュースアプリ『Player!』 • 先月リリースして日々アップデート中 • LINEクリエイターズスタンプ作って販売中 • http://bit.ly/love-stamp • 昨年6月からずっとSwift触ってます
  3. 3. 一部の画面だけ回転(ランドスケープ表示)を 許容させるのつらい • 全部許容 or 一部の画面だけ非許容は簡単 • Stackoverflowやブログなどで色々情報あるが、 それぞれ苦労している感があるし、画面構成に よってはうまく動かなかったりでつらい • カテゴリ拡張でオーバーライド
 もどきする方法微妙(後述)
  4. 4. 愚直な方法 • AppDelegateのsupportedInterfaceOrientationsForWindowにて縦横許容 • 縦固定にしたい各UIViewControllerのsupportedInterfaceOrientationsで 回転を制限 • ルートのViewControllerの設定が効くので、UINavigationControllerや UITabBarControllerなど使っている場合はわざわざ継承して、かつ現 在表示中のViewControllerを見て適切な値を返す • わざわざ継承するの面倒 • 漏れなく実装するのが大変・デグりやすい
  5. 5. 統一的な方法として ちょくちょく見かけるやり方 • UIViewControllerカテゴリ拡張でオーバーライドもどき • func supportedInterfaceOrientations() -> Int などを • カテゴリ拡張は現状、挙動的には以下の順に優先されるが、仕様的には不定 なので極力避けたい 1. カスタムクラスでの実装 2. カテゴリによるクラス拡張での実装 3. デフォルト実装(プロジェクト設定通りになる) • selfがルートのViewControllerだったら表示中のViewControllerを っていっ て、そこに定義してある実装を返す
  6. 6. AppDelegateの application:supportedInterfaceOrientationsForWindow: に処理を集約するやり方 • 実装している場合、まずこれが呼ばれるので、その時の画面 状態によって回転可能な向きを絞るようにすれば AppDelegateに処理をまとめられる • アプリの状態はどのように判別する??
  7. 7. アプリの状態は どのように判別するか • rootのViewControllerから、最前面の画面を る • selectedViewController • topViewController • presentedViewControler • visibleViewController • アプリの画面構成に依存してしまう • がんばれば汎用的にも書けるかも?
  8. 8. 今回やってみた方法: Aspectsによ るMethod Swizzling • Aspectsとは • Method Swizzlingのラッパーライブラリ • 特定クラスインスタンスのメソッド実行の前後に処理を挟んだり、すり替えたりが簡単に出来る • GitHubのStarも1600を超えている信頼出来そうなライブラリ • Google Analyticsの埋め込みなどにも便利 • Method Swizzlingもトリッキーな方法ではあるが、カテゴリ拡張でのオーバーライドより筋が良い
  9. 9. 具体的な実装 • 縦横回転許容するViewControllerにてallowRotationプロパティを trueにする • allowRotationプロパティは、カテゴリ拡張によって定義した Associated Object private var allowRotationKey: UInt8 = 0 extension UIViewController { var allowRotation: Bool { get { return (objc_getAssociatedObject(self, &allowRotationKey) as? Bool) ?? false } set { objc_setAssociatedObject(self, &allowRotationKey, newValue, UInt(OBJC_ASSOCIATION_RETAIN)) } } } class ModalViewController1: UIViewController { override func viewDidLoad() { super.viewDidLoad() allowRotation = true } }
  10. 10. 具体的な実装 • AppDelegateでAspectsを使ってViewController に回転周りの実装を注入 class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? var allowRotation = false func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // 起動時にAspectsの設定 hookForRotation() return true } /** 適切なタイミングで回転をハンドリング */ private func hookForRotation() { // 次のスライドで } func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int { // 常にに横許容 return Int(UIInterfaceOrientationMask.AllButUpsideDown.rawValue) } }
  11. 11. 具体的な実装 /** 適切なタイミングで回転をハンドリング */ private func hookForRotation() { ObjcHelper.aspect_viewControllerHookSelector("viewWillAppear:", withOptions: .PositionBefore, error: nil) { info in let vc = info.instance() as UIViewController self.allowRotation = vc.allowRotation } // 非表示時にAppDelegateのallowRotationを切り替え ObjcHelper.aspect_navigationControllerHookSelector("popViewControllerAnimated:", withOptions: .PositionBefore, error: nil) { info in let vc = info.instance() as UINavigationController let vcs = vc.viewControllers self.allowRotation = (vcs[vcs.count - 2] as UIViewController).allowRotation } ObjcHelper.aspect_viewControllerHookSelector("dismissViewControllerAnimated:completion:", withOptions: .PositionBefore, error: nil) { info in let vc = info.instance() as UIViewController if let presentingViewController = vc.presentingViewController { self.allowRotation = presentingViewController.allowRotation } } // supportedInterfaceOrientationsの実装をすり替え ObjcHelper.aspect_viewControllerHookSelector("supportedInterfaceOrientations", withOptions: .PositionInstead, error: nil) { info in let vc = info.instance() as UIViewController let invocation = info.originalInvocation() var ret = Int(self.allowRotation ? UIInterfaceOrientationMask.AllButUpsideDown.rawValue : UIInterfaceOrientationMask.Portrait.rawValue) invocation.setReturnValue(&ret) } }
  12. 12. iPad, iOS 6/7は? • 適用したプロジェクトがiPhone・iOS8オンリー なので、あまり深くチェック出来ていませんが、 現状 +アレンジ程度で正常に動くようになると思 います • iOS5以前は回転の仕組みが違うので別対応必要 • ぐぐる時もこの古い情報がまだけっこう引っか かるので注意
  13. 13. アレンジ • 一部の特殊な画面では、さらに追加実装が必要になるが、 それに耐えうるか? • 例: WebViewでの動画フルスクリーン再生 • YouTubeなどの動画が配置されている画面で、再生ボタン を押すとフルスクリーンになるが、その時だけ回転を許 容したい • AppDelegateを弄るだけで対応出来た(最後のスライド に載せたレポジトリのソース参照)
  14. 14. その他回転周りの注意点 • プロジェクト設定で縦横許容すると、ランドスケープ対応端末(iPhone 6 Plusなど)で横 画面にして起動するとスプラッシュ画面が真っ黒になる • 全体がランドスケープ対応の場合: • ランドスケープ用のスプラッシュ画像を配置 • 非対応の場合: • プロジェクト設定は縦のみにして、AppDelegateの supportedInterfaceOrientationsForWindowにて縦横許容とすると、起動時にちゃ んと縦用のスプラッシュ画面が出る
  15. 15. ソースコード • https://github.com/mono0926/ios-rotation-sandbox • 開発中のアプリは副作用があってハマったり、ビルド時 間かかったりするので、検証環境を用意すると る • ビルド時間はSwift 1.2のインクリメントビルドで、 ちょっとした変更後のビルドは体感10倍くらいになっ たものの • 今回紹介した方法をベースにアレンジする場合などにもご 活用ください

×