開発本部APP・UXチームの玉置(@tamappe )です。
今回の投稿が3回目になります。お盆は夏季休暇を利用して地元に帰省していました。
帰省中は暑いながらもどんな記事を書こうかなと思いながら時間が無下に過ぎ去ってしまい直近になって焦りながら記事を書いております。
今回は最近リリースしましたレッスンルームのiPad 横向き対応について技術的な話しをしたいと思います。
最初は端末回転だけをサポートすればいいのかと思っていましたが色々ハマったポイントがありましたので後述します。
レッスンルームとは
そもそもレアジョブアプリを使ったことがない方向けにレッスンルームについてご紹介したいと思います。
レッスンルームとはレアジョブが提供している講師とのレッスンの場です。
lesson_room_ipad _portrait
(画像はiPad での縦向きでのレッスンルームの様子です)
それぞれのUIを細かく分類すると下記のようになります。
lesson_room_ipad _portrait_description
動画の部分は本来はフィリピン講師が映る形になりますが開発中はレッスンルームを出入りできますしカメラで自分の顔が見れていますので
顔部分だけ加工してみました。
今回はこのレッスンルームに端末回転を許可する形で横向きのレイアウトを開発することがタスクでした。
注意したいことはこの端末回転はiPad だけですのでiPhone ではそもそも横向きはサポートしていません。
lesson_room_ipad _landscape
(iPad を横向きにして入室した場合の画面)
lesson_room_ipad _landscape_open
(教材を開いている時の画面)
こちらが今回のiPad 横画面対応しました横画面のレイアウトになります。
lesson_room_landscape_description
このように分類分けするレイアウトにしました。
レアジョブアプリのデザインは基本的にstoryboardを使ってデザインを開発しています。
そのため、storyboardにautolayoutを貼って端末間のサイズを調整しています。
本来のやり方であればiPad 横画面用のstoryboard ファイルを作成して横画面のレイアウトを作るのが最短だと思います。
ですがそのstoryboardを確認すると1つのstoryboardにUINavigationController
や複数のContainerView
が存在していましたので
レッスンルームのControllerだけを切り分けして横画面用のstoryboardを作るのは難しそうだと判断しました。
そこで今回採用したのがSizeClass
という技術です。
これを使って1 つのstoryboard内で完結する様に調整したのです。
SizeClassとは
SizeClassとはApple が提供しているデバイス のサイズを判別するための新しいクラスでiOS 8から導入されました。
iPhone 4 / iPhone 4S までは 端末のサイズが 320 x 480 だったのが
iPhone 5 の登場で初めて320 x 480 以外のサイズで iOS 端末が出てきたのがきっかけです。iPhone 5 は 320 x 568 で高さが高くなっています。
さらに翌年には iPhone 6 の登場で高さだけでなく横幅も320 以外のサイズが登場してしまいiOS アプリエンジニアは端末対応に追われていた当時が懐かしく感じます。
当時のApple はこのようなサイズ違いの端末の対応として AutoLayout という技術を使って解決するように促していましたが、
今度はタブレット 端末として登場したiPad でとうとうAutoLayoutでは対応しきれない状況になりました。
iPhone とiPad で別々でstoryboardを管理したりコードでView を切り分けたりはできますが同じViewでそれぞれ2つのクラスを用意するのは非常に大変です。
さらに端末回転もサポートすると縦横比が変わってレイアウトが崩れてしまいますね
そこで登場したのがSize Class というクラスです。
AutoLayoutやSize Class の使い分けは Apple の Human Interface Guidelines にある Adaptivity and Layout という記事が参考になります。
developer.apple.com
非常に簡略化してこれを説明すると
iOS 端末のサイズを大きく2つの概念に分けました。
Regular (denotes expansive space)
Compact (denotes constrained space)
このRegular と Compactをそれぞれ幅と高さに当てはめると
Regular width, Regular height
Compact width, Compact height
Regular width, Compact height
Compact width, Regular height
のように4通りに切り分けられます。この4通りは上記の参考記事にも記載されています。
この概念をiPhone とiPad にそれぞれ適用させると iPad などのタブレット はPortrait(縦向き)やLandscape(横向き)に関わらず Regular width, Regular height に、iPhone 端末は一部例外がありますがPortrait(縦向き) のときは Compact width, Regular height, Landscape(横向き) のときは Regular width, Compact height になります。
このように分類分けするときの技術が Size Class になります。
Size Class の使い方について
これをレッスンルームのstoryboardに当てはめて iPad の横画面の場合にのみ レイアウトを大幅に変える対応を行います。
記事の冒頭でお見せしましたレッスンルームのstoryboardは次の画像のようになります。
lesson_room_storyboard
基準がiPhone 8 Plus となっていてこれをベースにタブレット 端末で横画面のときにのみレイアウトを切り替える仕様になります。
storyboardのベースの切り替え方は次のようにします。
lesson_room_storyboard_2
iPad を選択します。そうすると次のようにstoryboardのviewのサイズがiPad Pro のサイズに切り替わります。
lesson_room_storyboard_3
このようになります。
また端末の縦向き・横向きの切り替えはOrientation を切り替えることでPortrait やLandscape に切り替えられます。
これがSizeClass を適用させる上での前提知識になります。
次にstoryboardを使っている場合はAutoLayoutでレイアウト間のマージンをそれぞれ設定していると思います。
SizeClass の場合はこれが肝となります。
AutoLaytoutのNSLayoutConstraint をそれぞれのViewにRegular Size(以下R)のときの値 , Compact Size(以下C)の時の値をそれぞれ設定することができるのです。
私の場合、当てはめ方としては
Rは iPad の幅と高さのすべて
Cは iPhone の幅と高さ
iPhone 6 以上の高さが 縦向き のときだけR
Plus 端末の幅が横向きのときだけR ( iPhone XS Max とiPhone XR の幅は横向きのときにR)
のルールで覚えています。これでも例外として() の部分が漏れてしまいます。
今回の仕様はiPad の場合でかつ横向きだったときにレイアウトを変更すればよいので例外ルールは考える必要はありません。
1のルールに従ってiPad は基本RなのでR指定 でConstraint を指定すればよいと考えます。
つまりはstoryboard 上で 通常時の constraint と R 用の constraint をそれぞれ設定します。
R用のconstraint の設定の方法を紹介します。
今回は参考として 画像の灰色のView(チャット部分のView)のbottomに Regular の場合は20 px マージンを取るように変更してみます。
lesson_room_storyboard_4
灰色のViewが選択された状態で新しく bottom のconstraint を指定します。 20pxとします。
lesson_room_landscape_5
20pxのbottom のconstraint を作るとエラーが発生しますがエラーを無視します。
lesson_room_constraint
bottom_constraint
作成した20px のbottom のconstraint をダブルクリックして編集画面を表示させます。
bottom_constraint_2
このように表示されたあとは一番下に「+ installed」の項目があります。
この左側の「+」をタップすると
bottom_constraint_3
CompactとRegularを選べますのでwidth / height ともに Regular を選択して「Add Variation」をクリックして
新しい「wR hR installed」が作成されます。
これが width Regular かつ height Regular だった場合の contraint です。
ただ2つともinstalled のチェックボックス が入っているときの挙動だと不安定になりますので
「wR hR installed」の部分だけチェックボックス が入るように1つ目のチェックボックス を外します。
bottom_constraint_4
これで Regular width と Regular height の場合のみこのconstraint が使われるようになります。
ですがこれだけだともともとのbottomの 0px マージンのconstraint がすべての場合に適用されますのでエラーは消えません。
そこでもともとのbottomの 0px マージンのconstraint の方を修正します。
bottomの 0px マージンのconstraint をダブルクリックしてconstraint の編集画面を開きます。
bottom_constraint_5
このcontraint に 「wR hR installed」のvariation を追加します。追加した「wR hR installed」のチェックボックス を外します。
bottom_constraint_6
この設定をすればさきほどのエラーが解消されたことが分かります。
これでiPad でRegular size だったときの constraint の追加が完成します。
ただSizeClass においてはiPad はPortrait とLandscape の両方で R ですので端末が縦向きであったときと横向きだったときのハンドリングができません。
そのためここで黒魔術を使って iPad で横向きのときには 高さを C に変換する必要がありました。
特定条件で 端末のSizeを Compact や Regular に変換する
前回のSizeClass の登場とともに端末回転で重要なUITraitCollection というクラスが登場しました。
端末の向きに関するクラスUIInterfaceOrientation が非推奨になり、UITraitCollection を使って実装しなければなりません。
結論からいうとUITraitCollection のoverrideTraitCollection(forChildViewController:)を使うことで端末の Regular size と Compact size を入れ替えることができます。
こちらのメソッドはUIViewController に存在します。
レッスンルームの画面はUINavigationControllerが持ちますのでNavigationController を継承したカスタムクラスを作成して
これをUINavigationController に使います。
class SampleNavigationController : UINavigationController {
override func overrideTraitCollection (forChild childViewController: UIViewController ) -> UITraitCollection ? {
let any = UITraitCollection(verticalSizeClass: .unspecified)
let compact = UITraitCollection(verticalSizeClass: .compact)
if [iPad端末のフラグ] && self .view.frame.width > self .view.frame.height {
return UITraitCollection(traitsFrom: [any, compact] )
}
return UITraitCollection(traitsFrom: [any, any] )
}
}
このようにコードを書いたクラスをプロジェクトファイルに追加してstoryboard のUINavigationController に継承させます。
これによって iPad で横画面になったときに検知して 高さが Regular から Compact に切り替わります。
iPad _convert
最後になりますが、今回紹介したSize Class で上のレクチャーは「wR hR installed」でしたがプロダクトコードを確認したら
「hC installed」とちゃんとheight がcompact のときのconstraint を設定していました。
「wR hR installed」ではなくて 「hC installed」になります。
これでiPad でかつ 横向き用のConstraint が機能して横向き用のレイアウトが表示されるようになります。
この技術を使って1つのstoryboard でiPhone とiPad それぞれ別々のconstraint を設定して見栄えの異なるデザインを設計できました。
このようにうまくSize Class を利用すると特定のstoryboard にだけレイアウトの違うデザインを実現させることができます。
ぜひSize class を使ってみてください。
最後に勉強会イベントの話しを
余談としてイベントの告知をします。
来週28日(水)にメドピア社と共同で「IT x 社会貢献」をテーマに勉強会を開催します。
medpeer.connpass.com
レアジョブとしては「学習体験の向上」を軸に、アプリ開発 、データ活用をメインコンテンツとしてお話します。
私はアプリ開発 者として「レアジョブアプリでのアクセス負荷で生じたAPI 遅延問題をアプリエンジニア視点で対策を考えてみる」をテーマにして登壇します。
近年のモバイルアプリの開発においてもはやサーバーサイドとの連携は切っても切れない役割を果たしています。 今回のイベントではそのAPI を考慮したWebRTCとの連携開発で起こり得たツラミを知見としてまとめて発表する予定です。
懇親会もあるので楽しめるイベントになると思います。 ご興味のある方はぜひこのイベントに参加していただければいいなと思います。