この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
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設定も行います。
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">
<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
に書き換えます。
LightTheme.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.LightTheme">
<Color x:Key="PageBackgroundColor">White</Color>
<Color x:Key="PrimaryTextColor">Black</Color>
<Color x:Key="ButtonBackgroundColor">LightBlue</Color>
</ResourceDictionary>
LightTheme.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace DarkModeSample.Resources
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LightTheme : ResourceDictionary
{
public LightTheme()
{
InitializeComponent();
}
}
}
Dark用のテーマを作成
同様にDarkTheme.xaml
を作成します。
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>
DarkTheme.xaml.cs
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace DarkModeSample.Resources
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class DarkTheme : ResourceDictionary
{
public DarkTheme()
{
InitializeComponent();
}
}
}
テーマの変更に追従する
テーマ変更の処理
App.xaml.cs
を下記にします。
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
を新規作成します。
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
について、動的リソースを使うように修正します。
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みたいですね。
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}">
<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を使ってダークモード対応をしてみました。まだ試験実装中の機能を使えば、さらにシンプルにできそうですね。他に良い方法があれば教えていただけると助かります。