狠狠撸

狠狠撸Share a Scribd company logo
4
Most read
6
Most read
意外と苦労する、一部の画面のみ
ランドスケープ表示を許容する方法
2015/05/13【第17回】potatotips(iOS/Android開発Tips共有会)
自己紹介
? Twitter: @_mono, Facebook: mono0926
? みんなでつくるスポーツニュースアプリ『Player!』
? 先月リリースして日々アップデート中
? LINEクリエイターズスタンプ作って販売中
? http://bit.ly/love-stamp
? 昨年6月からずっとSwift触ってます
一部の画面だけ回転(ランドスケープ表示)を
許容させるのつらい
? 全部許容 or 一部の画面だけ非許容は簡単
? Stackover?owやブログなどで色々情報あるが、
それぞれ苦労している感があるし、画面構成に
よってはうまく動かなかったりでつらい
? カテゴリ拡張でオーバーライド?
もどきする方法微妙(後述)
愚直な方法
? AppDelegateのsupportedInterfaceOrientationsForWindowにて縦横許容
? 縦固定にしたい各UIViewControllerのsupportedInterfaceOrientationsで
回転を制限
? ルートのViewControllerの設定が効くので、UINavigationControllerや
UITabBarControllerなど使っている場合はわざわざ継承して、かつ現
在表示中のViewControllerを見て適切な値を返す
? わざわざ継承するの面倒
? 漏れなく実装するのが大変?デグりやすい
統一的な方法として
ちょくちょく見かけるやり方
? UIViewControllerカテゴリ拡張でオーバーライドもどき
? func supportedInterfaceOrientations() -> Int などを
? カテゴリ拡張は現状、挙動的には以下の順に優先されるが、仕様的には不定
なので極力避けたい
1. カスタムクラスでの実装
2. カテゴリによるクラス拡張での実装
3. デフォルト実装(プロジェクト設定通りになる)
? selfがルートのViewControllerだったら表示中のViewControllerを っていっ
て、そこに定義してある実装を返す
AppDelegateの
application:supportedInterfaceOrientationsForWindow:
に処理を集約するやり方
? 実装している場合、まずこれが呼ばれるので、その時の画面
状態によって回転可能な向きを絞るようにすれば
AppDelegateに処理をまとめられる
? アプリの状態はどのように判別する??
アプリの状態は
どのように判別するか
? rootのViewControllerから、最前面の画面を る
? selectedViewController
? topViewController
? presentedViewControler
? visibleViewController
? アプリの画面構成に依存してしまう
? がんばれば汎用的にも書けるかも?
今回やってみた方法: Aspectsによ
るMethod Swizzling
? Aspectsとは
? Method Swizzlingのラッパーライブラリ
? 特定クラスインスタンスのメソッド実行の前後に処理を挟んだり、すり替えたりが簡単に出来る
? GitHubのStarも1600を超えている信頼出来そうなライブラリ
? Google Analyticsの埋め込みなどにも便利
? Method Swizzlingもトリッキーな方法ではあるが、カテゴリ拡張でのオーバーライドより筋が良い
具体的な実装
? 縦横回転許容する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
}
}
具体的な実装
? 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)
}
}
具体的な実装
/** 適切なタイミングで回転をハンドリング */
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)
}
}
iPad, iOS 6/7は?
? 適用したプロジェクトがiPhone?iOS8オンリー
なので、あまり深くチェック出来ていませんが、
現状 +アレンジ程度で正常に動くようになると思
います
? iOS5以前は回転の仕組みが違うので別対応必要
? ぐぐる時もこの古い情報がまだけっこう引っか
かるので注意
アレンジ
? 一部の特殊な画面では、さらに追加実装が必要になるが、
それに耐えうるか?
? 例: WebViewでの動画フルスクリーン再生
? YouTubeなどの動画が配置されている画面で、再生ボタン
を押すとフルスクリーンになるが、その時だけ回転を許
容したい
? AppDelegateを弄るだけで対応出来た(最後のスライド
に載せたレポジトリのソース参照)
その他回転周りの注意点
? プロジェクト設定で縦横許容すると、ランドスケープ対応端末(iPhone 6 Plusなど)で横
画面にして起動するとスプラッシュ画面が真っ黒になる
? 全体がランドスケープ対応の場合:
? ランドスケープ用のスプラッシュ画像を配置
? 非対応の場合:
? プロジェクト設定は縦のみにして、AppDelegateの
supportedInterfaceOrientationsForWindowにて縦横許容とすると、起動時にちゃ
んと縦用のスプラッシュ画面が出る
ソースコード
? https://github.com/mono0926/ios-rotation-sandbox
? 開発中のアプリは副作用があってハマったり、ビルド時
間かかったりするので、検証環境を用意すると る
? ビルド時間はSwift 1.2のインクリメントビルドで、
ちょっとした変更後のビルドは体感10倍くらいになっ
たものの
? 今回紹介した方法をベースにアレンジする場合などにもご
活用ください
Ad

Recommended

????????? #4 - ????? ?????
????????? #4 - ????? ?????
Seungmo Koo
?
01.???? ????
01.???? ????
Hankyo
?
??? ??? ??? ???
??? ??? ??? ???
Junyi Song
?
Lock free queue
Lock free queue
Bongseok Cho
?
殺しても死なないアプリ ?Core Bluetooth の「状態の保存と復元」機能?
殺しても死なないアプリ ?Core Bluetooth の「状態の保存と復元」機能?
Shuichi Tsutsumi
?
厂蚕尝颈迟别顿补迟补产补蝉别を无理矢理覗く
厂蚕尝颈迟别顿补迟补产补蝉别を无理矢理覗く
Takao Sumitomo
?
础辫辫厂迟辞谤别申请を一式まるっと自动化する
础辫辫厂迟辞谤别申请を一式まるっと自动化する
Tomoki Hasegawa
?
Google Maps を使ったアプリを作ってみた
Google Maps を使ったアプリを作ってみた
Shigeki Yamato
?
碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
shinnosuke kugimiya
?
Swift - Result<t>型で結果を返すのは邪道か,王道か
Swift - Result<t>型で結果を返すのは邪道か,王道か
Yuichi Yoshida
?
How to measure UIView position on Native App
How to measure UIView position on Native App
Daisuke Yamashita
?
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
Hiroyuki Kusu
?
RealmとApp Extensionのガイダンス/Realm meetup vol.6
RealmとApp Extensionのガイダンス/Realm meetup vol.6
Yuta Hoshino
?
Xcode tips
Xcode tips
Motoki Narita
?
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
akatsuki174
?
ゲンバの厂飞颈蹿迟
ゲンバの厂飞颈蹿迟
Yuichi Adachi
?
勉强会のこちら侧とあちら侧
勉强会のこちら侧とあちら侧
Tomoki Hasegawa
?
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
康廣 木目沢
?
#yidev 第25回勉強会 オープニング
#yidev 第25回勉強会 オープニング
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
Tomohiro Kumagai
?
App extension for iOS
App extension for iOS
toyship
?
闯痴惭言语を使ってみようの歌
闯痴惭言语を使ってみようの歌
YujiSoftware
?
Java in the Past, Java in the Future
Java in the Past, Java in the Future
Yuichi Sakuraba
?
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
Tomohiro Kumagai
?
Swift + GraphQL
Swift + GraphQL
Sommer Panage
?
3D touch for iOS
3D touch for iOS
toyship
?

More Related Content

Viewers also liked (20)

碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
shinnosuke kugimiya
?
Swift - Result<t>型で結果を返すのは邪道か,王道か
Swift - Result<t>型で結果を返すのは邪道か,王道か
Yuichi Yoshida
?
How to measure UIView position on Native App
How to measure UIView position on Native App
Daisuke Yamashita
?
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
Hiroyuki Kusu
?
RealmとApp Extensionのガイダンス/Realm meetup vol.6
RealmとApp Extensionのガイダンス/Realm meetup vol.6
Yuta Hoshino
?
Xcode tips
Xcode tips
Motoki Narita
?
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
akatsuki174
?
ゲンバの厂飞颈蹿迟
ゲンバの厂飞颈蹿迟
Yuichi Adachi
?
勉强会のこちら侧とあちら侧
勉强会のこちら侧とあちら侧
Tomoki Hasegawa
?
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
康廣 木目沢
?
#yidev 第25回勉強会 オープニング
#yidev 第25回勉強会 オープニング
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
Tomohiro Kumagai
?
App extension for iOS
App extension for iOS
toyship
?
闯痴惭言语を使ってみようの歌
闯痴惭言语を使ってみようの歌
YujiSoftware
?
Java in the Past, Java in the Future
Java in the Past, Java in the Future
Yuichi Sakuraba
?
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
Tomohiro Kumagai
?
Swift + GraphQL
Swift + GraphQL
Sommer Panage
?
3D touch for iOS
3D touch for iOS
toyship
?
碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
碍辞迟濒颈苍つかって蚕颈颈迟补クライアント作った时の话
shinnosuke kugimiya
?
Swift - Result<t>型で結果を返すのは邪道か,王道か
Swift - Result<t>型で結果を返すのは邪道か,王道か
Yuichi Yoshida
?
How to measure UIView position on Native App
How to measure UIView position on Native App
Daisuke Yamashita
?
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
【顿谤辞颈诲碍补颈驳颈2015】初学者に嬉しい础苍诲谤辞颈诲开発环境(あと惭痴颁とか)
Hiroyuki Kusu
?
RealmとApp Extensionのガイダンス/Realm meetup vol.6
RealmとApp Extensionのガイダンス/Realm meetup vol.6
Yuta Hoshino
?
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
既存フ?ロシ?ェクトに厂飞颈蹿迟尝颈苍迟を导入した话
akatsuki174
?
ゲンバの厂飞颈蹿迟
ゲンバの厂飞颈蹿迟
Yuichi Adachi
?
勉强会のこちら侧とあちら侧
勉强会のこちら侧とあちら侧
Tomoki Hasegawa
?
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
あなたのオフ?シ?ェクト指向はオフ?シ?ェクト指向て?はないかもしれない
康廣 木目沢
?
#yidev 第25回勉強会 オープニング
#yidev 第25回勉強会 オープニング
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 3rd #minna_de_swift
Tomohiro Kumagai
?
App extension for iOS
App extension for iOS
toyship
?
闯痴惭言语を使ってみようの歌
闯痴惭言语を使ってみようの歌
YujiSoftware
?
Java in the Past, Java in the Future
Java in the Past, Java in the Future
Yuichi Sakuraba
?
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
みんなで Swift 復習会での談笑用スライド – in 札幌 1st′ #minna_de_swift
Tomohiro Kumagai
?
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
みんなで Swift 復習会での談笑用スライド – 5th #minna_de_swift
Tomohiro Kumagai
?
3D touch for iOS
3D touch for iOS
toyship
?

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