Xamarin.Formsでダークモード対応やってみた (Android/iOS)
AndroidとiOSについて、それぞれAndroid 10とiOS 13からダークモード対応ができるようになりました。 特にiOSアプリはいつ必須になるか分かりませんので、早めの準備や対応をしておくと安心です。 というわけで、Xamarin.FormsでAndroidとiOSのダークモード対応をやってみました。
環境
- Xamarin.Forms: 4.4.0.991265
- Xamarin.Essentials: 1.5.3.2
- Android: 10
- iOS: 13.4.1
まずはアプリを作る
プロジェクトを新規作成する
プロジェクトを新規作成します。
シンプルにするめテンプレートは「空白」を選択します。
画面を作る
MainPage.xaml
にSearchBarとButtonを追加します。あとiOS用にSaseArea設定も行います。
<?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:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:Page.UseSafeArea="True" x:Class="DarkModeSample.MainPage"> <StackLayout> <Label Text="Welcome to Xamarin.Forms!" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> <Button Text="Apple" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> </StackLayout> </ContentPage>
画面の様子
左側がライトモードで右側がダークモードの様子です。
Android
変化ありません。
iOS
ダークモード時(右側)では、時刻表示が消えちゃいました(文字色が白色になっている)。
ダークモード対応やってみる
Xamarin.Essentialsの更新
Xamarin.Essentialsを最低でも1.4.0
以上に更新します。
リソースを作成する
Resourcesフォルダを作成
Resources
フォルダを新規作成します。
Light用のテーマを作成
先ほど作成したResource
フォルダの配下にLightTheme.xaml
ファイルを新規作成します。
作りたいものはResourceDictionary
ですが、直接作成できないため「コンテンツページ」を選択しています。
(xamlファイルとxaml.csファイルを作りたい)
ファイルの作成後、それぞれをResourceDictionary
に書き換えます。
<?xml version="1.0" encoding="utf-8" ?> <ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DarkModeSample.Resources.LightTheme"> <Color x:Key="PageBackgroundColor">White</Color> <Color x:Key="PrimaryTextColor">Black</Color> <Color x:Key="ButtonBackgroundColor">LightBlue</Color> </ResourceDictionary>
using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace DarkModeSample.Resources { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class LightTheme : ResourceDictionary { public LightTheme() { InitializeComponent(); } } }
Dark用のテーマを作成
同様にDarkTheme.xaml
を作成します。
<?xml version="1.0" encoding="utf-8" ?> <ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DarkModeSample.Resources.DarkTheme"> <Color x:Key="PageBackgroundColor">Black</Color> <Color x:Key="PrimaryTextColor">White</Color> <Color x:Key="ButtonBackgroundColor">LightPink</Color> </ResourceDictionary>
using Xamarin.Forms; using Xamarin.Forms.Xaml; namespace DarkModeSample.Resources { [XamlCompilation(XamlCompilationOptions.Compile)] public partial class DarkTheme : ResourceDictionary { public DarkTheme() { InitializeComponent(); } } }
テーマの変更に追従する
テーマ変更の処理
App.xaml.cs
を下記にします。
using DarkModeSample.Resources; using Xamarin.Essentials; using Xamarin.Forms; namespace DarkModeSample { public partial class App : Application { public App() { InitializeComponent(); ApplyTheme(); MainPage = new MainPage(); } protected override void OnStart() { } protected override void OnSleep() { } protected override void OnResume() { } public static void ApplyTheme() { if (AppInfo.RequestedTheme == AppTheme.Dark) { App.Current.Resources = new DarkTheme(); } else { App.Current.Resources = new LightTheme(); } } } }
テーマ変更を伝える (iOS)
iOSプロジェクトにPageRenderer.cs
を新規作成します。
using System; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(ContentPage), typeof(DarkModeSample.iOS.PageRenderer))] namespace DarkModeSample.iOS { public class PageRenderer : Xamarin.Forms.Platform.iOS.PageRenderer { protected override void OnElementChanged(VisualElementChangedEventArgs e) { base.OnElementChanged(e); if (e.OldElement != null || Element == null) return; try { App.ApplyTheme(); } catch (Exception) { // ignored } } public override void TraitCollectionDidChange(UITraitCollection previousTraitCollection) { base.TraitCollectionDidChange(previousTraitCollection); App.ApplyTheme(); } } }
ライトモードとダークモードが変化したとき、App.ApplyTheme()
を呼んで反映させています。
Androidはこのような処理は不要でした。
動的リソースに対応する
画面の修正
MainPage.xaml
について、動的リソースを使うように修正します。
<?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:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:Page.UseSafeArea="True" x:Class="DarkModeSample.MainPage" BackgroundColor="{DynamicResource PageBackgroundColor}"> <StackLayout> <Label Text="Welcome to Xamarin.Forms!" TextColor="{DynamicResource PrimaryTextColor}" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> <Button Text="Apple" BackgroundColor="{DynamicResource ButtonBackgroundColor}" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" /> </StackLayout> </ContentPage>
画面の様子
左側がライトモードで右側がダークモードの様子です。イイカンジですね!
Android
iOS
補足
Style
を使うと書き方を少し変えることができます。UIパーツとデザインの分離ができます。HTMLとCSSみたいですね。
<?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:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" ios:Page.UseSafeArea="True" x:Class="DarkModeSample.MainPage" BackgroundColor="{DynamicResource PageBackgroundColor}"> <ContentPage.Resources> <ResourceDictionary> <Style TargetType="Label"> <Setter Property="TextColor" Value="{DynamicResource PrimaryTextColor}" /> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> </Style> <Style x:Key="FooButtonStyle" TargetType="Button"> <Setter Property="BackgroundColor" Value="{DynamicResource ButtonBackgroundColor}" /> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="VerticalOptions" Value="CenterAndExpand" /> </Style> </ResourceDictionary> </ContentPage.Resources> <StackLayout> <Label Text="Welcome to Xamarin.Forms!" /> <Button Text="Apple" Style="{StaticResource FooButtonStyle}" /> </StackLayout> </ContentPage>
Style
のx:Key
が未指定の場合はTargetType
で指定した全ての要素に反映され、x:Key
を指定した場合は任意箇所のみ反映できます。
さいごに
Xamarin.Formsを使ってダークモード対応をしてみました。まだ試験実装中の機能を使えば、さらにシンプルにできそうですね。他に良い方法があれば教えていただけると助かります。