Xamarin.Forms でガワネイティブアプリを作るときのテンプレートプロジェクトを作る１

今年は専ら Angular で Webアプリを作ったり、ガワネイティブアプリを作ったりしていますが Xamarin のアドベントカレンダーと聞いてやってきました。

## 概要

最近は、モバイルネイティブアプリよりも Webアプリ(SPA/PWA)、そしてそれを利用したガワネイティブアプリを推している私ですが、ガワネイティブアプリを作る時の「ガワ」には Xamarin(Xamarin.Forms) を採用しています。

なぜガワネイティブなのか？ネイティブではなく、Web(PWA)でもないのか？については、

* [Angular と Firebase で月間PV1億超えの PWA を作った話 - Qiita](https://qiita.com/MasanobuAkiba/items/d2d4be3f8fd2e23c0c1a)

のエントリがよく解説されていますのでご一読を。私のケースは BtoC ではなく BtoB であるため、全面的に一致するわけではありませんが、内容については大いに同意できます。

ガワネイティブアプリは、Webアプリがネイティブの機能を欲するから採用されるわけで、それを制御するためにWebアプリとネイティブ機能の相互通信が必要になります。

また、「ガワ」は ``WebView`` なわけですが、それがアプリとして自然に振る舞うために、いくつかの「設定」をしてあげる必要があります。

この記事は、そのような「Xamarin(.Forms) でガワネイティブアプリを作るときのリファレンス」になればよいなと思って書きます。

尚、~~Advent Calendar の締め切りに間に合わせるために~~ 意外と情報量が多かったので、何回かに分けます。
今回は初回です。

## 目次

1.**【今回】日本語入力時の画面高さの調整**
2.**【今回】ステータスバー、あるいは SafeArea(ノッチ部)の色**
3.【[第2回](https://qiita.com/amay077/items/32833031c955ad2ddc77)】スプラッシュスクリーンおよび初回読み込み時の対応
4.【第2回】アプリに必要な権限の許可を要求する
5.【次回以降予定】アプリ情報の Web 側への引き渡し
6.【次回以降予定】<input type="xxx"> への対応
7.【次回以降予定】Back ボタンハンドリングの Web 側への移譲

## 1. 日本語入力時の画面高さの調整

ソフトウェアキーボードが、コンテンツの手前が重なってしまう問題の解決です。

これは、

* [Xamarin.Forms でソフトウェアキーボードが表示された時に画面が隠れないようにする - Qiita](https://qiita.com/amay077/items/6fcdec829a96bc604532)

を適用して解決します。

### Android の場合

今回のように WebView だけ対応すればよい場合は、 ``MainActivity.cs`` に ```.UseWindowSoftInputModeAdjust()`` の行を追加してあげればよいみたいです。

```csharp
// MainActivity.cs
protected override void OnCreate(Bundle bundle)
{
    TabLayoutResource = Resource.Layout.Tabbar;
    ToolbarResource = Resource.Layout.Toolbar;

    base.OnCreate(bundle);
    global::Xamarin.Forms.Forms.Init(this, bundle);
    LoadApplication(new App());

	Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>()
		.UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); // ←ここ！！
}
```

### iOS の場合

iOS の場合、前出のわたしのエントリを改善して頂いた、

* [Xamarin.Formsでソフトウェアキーボードで入力欄が隠れないようにする2018 - Qiita](https://qiita.com/sun_bacon/items/0cafaeaf34abe9f86e95#_reference-cb351a7b1ca951eb8084)

があるのですが、WebView だけ対応すればよい場合、特にやることは無いですｗ

### 対応結果

これらの対応を行うと、下図のようにソフトウェアキーボードを表示していても、WebView のコンテンツが隠れることはなくなります。
なお、この例では Materialize-CSS の [Starter Template](https://materializecss.com/templates/starter-template/preview.html) を表示させています。

<table>
<thead>
<tr>
<th align="center">元コンテンツ</th>
<th align="center">対応前</th>
<th align="center">対応後(Android)</th>
<th align="center">対応後(iOS)</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/fbc5fa61-fac7-cc35-403e-ec900034818e.png" style="max-width:100%;"></td>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/cbe9b28e-794e-7724-aa0b-328ec6648fdf.png" style="max-width:100%;"></td>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/2a4cad06-79a9-ae7c-1766-f6f428485e38.png" style="max-width:100%;"></td>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/266a927f-b9e3-5533-4a5e-e9cf88039df6.png" style="max-width:100%;"></td>
</tr>
</tbody>
</table>

## 2. ステータスバー、あるいは SafeArea(ノッチ部)の色

Android と iOS のステータスバーの色は、Webアプリのテーマ色に合わせたいものです。
また、iOS では SafeArea(ノッチのところ)を除けるような対応が必要になります。

ガワのプロジェクト作成直後は下図のように、Android では青系のステータスバーに、iOS では白色になってしまいます。

<table>
<thead>
<tr>
<th align="center">対応前(Android)</th>
<th align="center">対応前(iOS)</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/bbbab54b-e862-9982-60ff-e076f9124912.png" style="max-width:100%;"></td>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/08272693-ebe9-ef49-5b4c-d8e1b52b900e.png" style="max-width:100%;"></td>
</tr>
</tbody>
</table>

この例では Web アプリのテーマ色が緑なので、どちらもステータスバーを緑色にします。

### Android の場合

Android プロジェクトにある ``Resources/values/styles.xml`` の ``colorPrimaryDark `` の色を修正します。下の例では ``#66BB6A`` に書き換えました。(``colorPrimary`` も変えておいた方が良いかもしれませんね。)

**Resources/values/styles.xml**

```xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="MainTheme" parent="MainTheme.Base">
    </style>
    <!-- Base theme applied no matter what API -->
    <style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
        <!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
        <item name="windowNoTitle">true</item>
        <!--We will be using the toolbar so no need to show ActionBar-->
        <item name="windowActionBar">false</item>
        <!-- Set theme colors from https://aka.ms/material-colors -->
        <!-- colorPrimary is used for the default action bar background -->
        <item name="colorPrimary">#2196F3</item>
        <!-- colorPrimaryDark is used for the status bar -->
        <item name="colorPrimaryDark">#66BB6A</item>  ←ここ！！！
        <!-- colorAccent is used as the default value for colorControlActivated
         which is used to tint widgets -->
        <item name="colorAccent">#FF4081</item>
        <!-- You can also set colorControlNormal, colorControlActivated
         colorControlHighlight and colorSwitchThumbNormal. -->
        <item name="windowActionModeOverlay">true</item>
        <item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
    </style>
    <style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
        <item name="colorAccent">#FF4081</item>
    </style>
</resources>
```

### iOS の場合

ノッチの対応がちょっと面倒です。
こちらの方法をとります。

* [SafeAreaInsets For Xamarin.Forms In iOS - Xamarin Help](https://xamarinhelp.com/safeareainsets-xamarin-forms-ios/)

ノッチ(SafeArea)の領域分のパディングを設ける Effects を作って、Page に適用する方法です。

まずは、Forms の共通プロジェクトに ``SafeAreaPaddingEffect`` を作成します。

**SafeAreaPaddingEffect.cs**

```csharp
using System;
using Xamarin.Forms;

namespace GawaNativeGettingStarted
{
    public class SafeAreaPaddingEffect : RoutingEffect
    {
        public SafeAreaPaddingEffect() : base("GawaNativeGettingStarted.SafeAreaPaddingEffect")
        {
        }
    }
}
```

先に ``MainPage.xaml`` に適用しちゃいましょう。
尚、``BackgroundColor="#66BB6A"`` で指定した色が、Web アプリ側の緑のテーマ色です。

**MainPage.xaml**

```xml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    xmlns:local="clr-namespace:GawaNativeGettingStarted;assembly=GawaNativeGettingStarted"
    xmlns:effect="clr-namespace:GawaNativeGettingStarted"
    x:Class="GawaNativeGettingStarted.MainPage">
    <StackLayout Orientation="Vertical" BackgroundColor="#66BB6A">
        <StackLayout.Effects>
            <effect:SafeAreaPaddingEffect />
        </StackLayout.Effects>
        <WebView
            HorizontalOptions="FillAndExpand"
            VerticalOptions="FillAndExpand"
            Source="https://eeaab7d6.ngrok.io"/>
    </StackLayout>
</ContentPage>
```

次に iOS のプロジェクトにも ``SafeAreaPaddingEffect`` を作成し、SafeArea 考慮の実装をします。

```csharp
using GawaNativeGettingStarted.iOS.Effects;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;

[assembly: ResolutionGroupName("GawaNativeGettingStarted")]
[assembly: ExportEffect(typeof(SafeAreaPaddingEffect), nameof(SafeAreaPaddingEffect))]
namespace GawaNativeGettingStarted.iOS.Effects
{
    class SafeAreaPaddingEffect : PlatformEffect
    {
        Thickness _padding;
        protected override void OnAttached()
        {
            if (Element is Layout element)
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(11, 0))
                {
                    _padding = element.Padding;
                    var insets = UIApplication.SharedApplication.Windows[0].SafeAreaInsets; // Can't use KeyWindow this early
                    if (insets.Top > 0) // We have a notch
                    {
                        element.Padding = new Thickness(_padding.Left + insets.Left, _padding.Top + insets.Top, _padding.Right + insets.Right, _padding.Bottom);
                        return;
                    }
                }
                // Uses a default Padding of 20. Could use an property to modify if you wanted.
                element.Padding = new Thickness(_padding.Left, _padding.Top + 20, _padding.Right, _padding.Bottom);
            }
        }

        protected override void OnDetached()
        {
            if (Element is Layout element)
            {
                element.Padding = _padding;
            }
        }
    }
}
```

これは前出のリンク先の内容そのものです。

ここまでの対応で、SafeArea 分の余白を設け、背景色を Web アプリ側に合わせることができました。

<table>
<thead>
<tr>
<th align="center">対応中(iOS)</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/3a383c34-f4f7-c179-ce26-03d59c9c9403.png" style="max-width:100%;"></td>
</tr>
</tbody>
</table>

しかし、ステータスバーの文字色が黒になっています。これを白にしましょう。
参考になるエントリはこちら↓です。

* [ステータスバーのスタイル(色)変更方法優先度 まとめ - Qiita](https://qiita.com/ux_design_tokyo/items/8e62977b7609e68755c7)

これを参考に iOS プロジェクトの ``info.plist`` を編集し、次のエントリを追加します。

* Status bar style : White
* View controller-based status bar appearance : No

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/05ae8a60-1482-2d6c-1726-9b07fd14c155.png)

これで、ステータスバーの文字色が白になります。

### 対応結果

対応後の見た目はこんな感じになります。結構ネイティブアプリっぽくなって来たでしょう？

<table>
<thead>
<tr>
<th align="center">対応後(Android)</th>
<th align="center">対応後(iOS)</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/a4e528e8-51ef-71a6-3965-d4c37ac29068.png" style="max-width:100%;"></td>
<td align="center"><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8227/46131d4f-3537-78d9-2888-9aef4876c59f.png" style="max-width:100%;"></td>
</tr>
</tbody>
</table>

## まとめ

とりあえず初回ということで、見た目中心の対応内容を挙げてみました。
一通り揃ったら、GitHub にサンプルを上げて、あわよくば可能なところは nuget パッケージに切り出して利用できるようにしていきたいと思います。

見た目の対応でも、ダークテーマ対応などはまったく無知なのでこれから勉強していきます。
次回エントリは、、、年内いける・・・かな？

### 2019.12.25 追記

年内に続き書いた！

* [Xamarin.Forms でガワネイティブアプリを作るときのテンプレートプロジェクトを作る２ - Qiita](https://qiita.com/amay077/items/32833031c955ad2ddc77)


## 参考(次回以降のも含む)

* [iOSでWebViewを開発する際に気をつけるべき9のこと - Qiita](https://qiita.com/nikadon/items/1a38761012d530db112a)
* [(Xamarin)Webサイトのオフライン閲覧アプリを作るときの注意 - Qiita](https://qiita.com/khosokawa/items/e319c5ca65e3de9c81db)
* [WebViewの<input type="file">でカメラ、画像からデータを取得する [Android]](https://www.petitmonte.com/java/android_webview_camera.html)
* [html5 カメラ起動 capture属性について - Qiita](https://qiita.com/RIKIgigasu/items/0c710fa608dd5a7a45cb)
* [Materialize](https://materializecss.com/)
* [SafeAreaInsets For Xamarin.Forms In iOS - Xamarin Help](https://xamarinhelp.com/safeareainsets-xamarin-forms-ios/)
* [Angular と Firebase で月間PV1億超えの PWA を作った話 - Qiita](https://qiita.com/MasanobuAkiba/items/d2d4be3f8fd2e23c0c1a)