🌟

[Ionic] Android DesignでもCollapsible Large Titlesを使いたい!

2022/04/21に公開

Ionic5から、Ionicで「Collapsible Large Titles」が実装されました。未スクロールの状態ではLerge Titleを表示して、スクロールするとheader部分に小さなタイトルを表示させる見せ方です。

なのですが、この表示はiOSモード( ios )のみで有効で、Androidモード( md )では利用することができません。

しかしながら、Androidも近年はCollapsible Large Titlesを採用しています。例えば、以下はPixel5a / Android12での設定画面です。

これによってAndroid DesignでCollapsible Large Titlesを使う!というのはアンチパターンでなくなりました(iOSにしかない挙動をクロスプラットフォームにするためだけに無理やりAndroidに実装するのはアンチパターンだと思っています)。
では、Android DesignでCollapsible Large Titlesを使うためにはどうすればいいでしょうか。

何も考えない場合

AndroidでもiOSデザインを強制してしまえば、AndroidでもCollapsible Large Titlesを使うことができます。

<ion-header [translucent]="true" mode='ios'>
  <ion-toolbar>
    <ion-title>{{ title }}</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense" mode='ios'>
    <ion-toolbar>
      <ion-title size="large">{{ title }}</ion-title>
    </ion-toolbar>
  </ion-header>

Ionicのコンポーネントは mode=ios でiOSデザインに固定することができます(Androidデザインに固定したい場合は、 mode=md )。また、指定がない限り入れ子になっているコンポーネントは親コンポーネントのデザインを受け継ぐので、これで実現することができます。

けど、タイトルがiOS表示に固定されてしまいます。

(左がiOSデザイン、右がAndroidデザイン)

Androidデザインでは、ページのタイトルは左寄りで比較的細文字、iOSデザインは中央寄せで太文字です。他に「戻る」ボタンの形状であったり、それぞれプラットフォーム毎に設定されているものがiOSに固定化されてしまいます。これは嫌だ。

そもそもなぜAndroidで使うことができないのかを整理する

https://forum.ionicframework.com/t/is-there-anyway-to-have-collapsable-title-on-android-too/219791 でMikeが「iOSのためにハードコーディングされてるからAndroidで使えないよ」と返事しています。ハードコーディングしているところを探してみましょう。

まず、デザインをあててるCSS。

https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/header/header.ios.scss#L36-L109

ion-headerにがっつりハードコーディングされていますね。Androidで無効化しているのは以下のコードです。

https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/header/header.md.scss#L25-L31

display: none してるだけならCSSあてればどうにかなるかなと思ったんですが、続いて以下のハードコーディングが立ち上はだかります。

https://github.com/ionic-team/ionic-framework/blob/main/core/src/components/header/header.tsx#L73-L78

checkCollapsibleHeader メソッドは簡単にいうと、Collapsible Large Titlesが設定されているかを確認して、スクロール量を計算して、うまく表示表示アニメーションを制御しているメソッドです。これが mode=ios でないと動かない・・・!!!!

なのですが、 ion-header コンポーネントがiOS/Androidで異なる処理・デザインをしているのはCollapsible Large Titlesのための分岐だけなんですよね。iOS/Androidで高さを分岐させてるのは ion-toolbar だし、もしかしてこれはワンチャンある??

iOSモードの問題点を考える

Androidデザインでは、ページのタイトルは左寄りで比較的細文字、iOSデザインは中央寄せで太文字です。他に「戻る」ボタンの形状であったり、それぞれプラットフォーム毎に設定されているものがiOSに固定化されてしまいます。これは嫌だ。

と述べましたが、これは ion-header で設定されているものではありません。ion-header から入れ子になっており、modeを継承している ion-toolbar による問題です。では、 ion-toolbar でもmodeを指定してしまったらどうなる??

<ion-header [translucent]="true" mode='ios'>
  <ion-toolbar mode="md">
    <ion-title>{{ title }}</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense" mode='ios'>
    <ion-toolbar mode="md">
      <ion-title size="large">{{ title }}</ion-title>
    </ion-toolbar>
  </ion-header>

お、結構理想に近づいてきました。よく考えるとAndroidデザインにはLarge Titleがないので、それは別途設定しないといけませんね。グローバルCSSに以下を追加します。(日本語だと上が少し切れるので30px程度の方が使いやすいです)。

ion-title[size="large"] {
  font-size: 34px;
}

/* display:noneが先に読み込まれることがあるので詳細度をあげて上書き */
ion-app.md .header-collapse-condense {
    display: block;
}

これで大体挙動は大丈夫ですね。ただ、これだとiOSの時もAndroidデザインになってしまうので、 @ionic/angular にある Platform APIを使って実行端末を判定しましょう。 ついでに、私の手元のAndroidではブラー効果はなかったので、その効果も切ります。

<ion-header [translucent]="platform.is('ios')" mode="ios">
  <ion-toolbar [mode]="!platform.is('ios') ? 'md': undefined">
    <ion-title>Tab One</ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense" mode="ios">
    <ion-toolbar [mode]="!platform.is('ios') ? 'md': undefined">
      <ion-title size="large">Tab One</ion-title>
    </ion-toolbar>
  </ion-header>

左がiOS、右がAndroidです。大体再現できました。

まとめ

https://github.com/ionic-team/ionic-framework/issues/25157 で、Issueはあげておいたのですが、実装・リリースはまだ先になると思います。それでもAndroidでもCollapsible Large Titlesを使いたいという方はぜひご参考にしていただければ幸いです。

実プロダクトで実装する場合は、全ページにmodeやplatformを指定するのはしんどいので、Directiveを使ってDOMの操作でこれらを行うとスマートでいいかなと思います。

それでは、また。

Discussion