.NET MAUIで画面を作っていると「OnPlatform」と「OnIdiom」という似たようなXAMLが出てきますが、違いがあいまいなまま何となくコピペで済ませていないでしょうか。本記事では、両者の役割と使い分け方を実務目線で整理し、スマホ・タブレット・デスクトップそれぞれで見やすく崩れにくいUIを作るための具体的なパターンをまとめます。
.NET MAUIのOnPlatform / OnIdiomを実務レベルで理解する
.NET MAUIは「1つのXAML / C#で複数OSをターゲットにできる」のが大きな魅力ですが、その裏側で重要な役割を担っているのが OnPlatform と OnIdiom です。どちらも「条件によって値を切り替える」ための仕組みですが、
- OnPlatform:OSごとの差分(Android / iOS / Windows など)
- OnIdiom:端末種別ごとの差分(Phone / Tablet / Desktop など)
というように、役割がはっきり分かれています。ここをきちんと理解しておくと、
- 「iOSだけ上マージンを増やしたい」
- 「タブレットのときだけ左右に余白を増やしたい」
- 「Windowsタブレットでも Desktop 判定になるケースをどう扱うか」
といった現場でよくある悩みを、シンプルなXAMLで解決できるようになります。
OnPlatform と OnIdiom のざっくり比較
まずは違いを一目で確認できるよう、比較表にまとめます。
| 項目 | OnPlatform | OnIdiom |
|---|---|---|
| 判定軸 | OS / プラットフォーム(Android, iOS, WinUI など) | 端末種別(Phone, Tablet, Desktop, TV, Watch など) |
| 主な用途 | OSごとの慣習・制限に応じた調整、機能分岐 | 画面サイズ・利用距離を意識したレイアウト密度の調整 |
| 典型例 | iOSのみ余白追加、Androidのみ高さ変更、Windowsのみフォント変更 | Phoneは余白少なめ、Tabletは余白多め、Desktopは3ペインレイアウトにする |
| 代表的な列挙体 | DevicePlatform.Android / iOS / WinUI / MacCatalyst など | DeviceIdiom.Phone / Tablet / Desktop / TV / Watch など |
| 想定する違い | OSのUIガイドライン・入力方法・API差 | 画面サイズ・解像度・持ち方、表示できる情報量 |
覚え方としては、
- OS差を吸収したいなら OnPlatform
- 画面サイズや端末種別で調整したいなら OnIdiom
と考えるのがシンプルです。
「Phone」「Tablet」はどのプラットフォームに当たるのか?
よくある混乱ポイントが「Phone / Tablet は OS なのか?」という点です。結論から言うと、
- Phone / Tablet は OS 名ではなく、端末種別(Idiom)の分類
です。代表的な組み合わせを表にすると次のようになります。
| 実デバイス例 | DeviceInfo.Platform(OS) | DeviceInfo.Idiom(端末種別) |
|---|---|---|
| iPhone | DevicePlatform.iOS | DeviceIdiom.Phone |
| iPad | DevicePlatform.iOS | DeviceIdiom.Tablet |
| Androidスマホ | DevicePlatform.Android | DeviceIdiom.Phone |
| Androidタブレット | DevicePlatform.Android | DeviceIdiom.Tablet(※画面サイズで判定) |
| Windows PC / ノートPC | DevicePlatform.WinUI | DeviceIdiom.Desktop |
| 多くのWindowsタブレット | DevicePlatform.WinUI | DeviceIdiom.Desktop(Tablet ではないことに注意) |
| macOS / MacCatalyst アプリ | DevicePlatform.MacCatalyst | DeviceIdiom.Desktop |
| Tizenスマートウォッチ | DevicePlatform.Tizen | DeviceIdiom.Watch |
特に注意したいのは、Windowsタブレットでも Idiom は Desktop になることが多いという点です。「Tablet だから OnIdiom.Tablet で分岐できるはず」と思っていると、意図した分岐にならないことがあります。
そのため、Windows タブレットのような境界的なデバイスをサポートする場合は、
- OnIdiom だけに頼らず、OnPlatform と画面幅(DeviceDisplay)を組み合わせる
といった工夫が必要です。
OnPlatform を使う場面と実例
OnPlatform は「OSごとの違い」を吸収する目的で使います。代表的なユースケースは次のようなものです。
- iOS だけステータスバー分の余白を追加したい
- Android だけ影(Elevation)の見え方を変えたい
- Windows だけフォントファミリを変更したい
- OSごとにファイルパス / 権限 / 機能有無を切り替えたい
XAMLでの基本的な書き方(Paddingの例)
OS別にページの Padding を変えるサンプルがこちらです。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
<On Platform="Android" Value="10,20,20,10" />
<!-- 必要なら WinUI, MacCatalyst, Tizen なども記述可能 -->
<!-- <On Platform="WinUI" Value="12,24,12,12" /> -->
</OnPlatform>
</ContentPage.Padding>
</ContentPage>
ポイントは次のとおりです。
OnPlatformに対してx:TypeArgumentsで型を指定する(ここではThickness)<On Platform="iOS" ... />のように OS 名を指定して値を切り替える- 指定されていない OS では「デフォルト値」またはプロパティ側の初期値が使われる
スタイル・リソースとして再利用する
同じような OnPlatform 記述を何度も書くと、保守性が一気に下がります。実務では ResourceDictionary にまとめてスタイル化しておくのがオススメです。
<ResourceDictionary xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<OnPlatform x:Key="PagePadding" x:TypeArguments="Thickness">
<On Platform="iOS" Value="0,20,0,0" />
<On Platform="Android" Value="10,20,20,10" />
<On Platform="WinUI" Value="12,24,12,12" />
</OnPlatform>
</ResourceDictionary>
ページ側では次のように簡潔に指定できます。
<ContentPage ... Padding="{StaticResource PagePadding}">
...
</ContentPage>
OSごとの見た目変更に対応するときは ResourceDictionary の定義だけ修正すればよく、既存のページ XAML を変更しなくて済むので、大規模アプリほど効果が大きくなります。
OnIdiom を使う場面と実例
OnIdiom は「端末種別」によって値を切り替えるための仕組みです。OS ではなく、画面サイズやデバイスの持ち方を意識した UI の調整に向いています。
- Phone:片手で持つ前提。画面が小さく、情報を詰めたい。
- Tablet:両手やスタンド置きが多い。画面が広く、余白を増やしたい。
- Desktop:マウスとキーボード中心。さらに多くの情報を同時表示したい。
余白(Margin)を端末種別で変える例
Phone / Tablet / Desktop で Margin を切り替える XAML は次のようになります。
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Margin>
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone> 0,20,0,0 </OnIdiom.Phone>
<OnIdiom.Tablet> 0,40,0,0 </OnIdiom.Tablet>
<OnIdiom.Desktop> 0,60,0,0 </OnIdiom.Desktop>
</OnIdiom>
</ContentPage.Margin>
</ContentPage>
ここでは、
- Phone:余白を少なめ(情報密度を高く)
- Tablet:上下余白をやや広め
- Desktop:さらに広い余白(画面が広い前提)
という方針で調整しています。重要なのは、「OSごと」ではなく「画面サイズ・利用スタイルごと」に UI の密度を変えている点です。
ナビゲーション構造を変える例
OnIdiom は、単なる余白だけでなく画面構造そのものを変えるのにも使えます。例えば:
- Phone:下部タブ+スタックナビゲーション
- Tablet:左側にメニュー、右側にコンテンツという 2 ペインレイアウト
- Desktop:3 ペイン(ナビゲーション / リスト / 詳細)
といった構成の切り替えです。実装例(擬似コード的なイメージ)は次のようになります。
<ContentPage ... xmlns:controls="clr-namespace:MyApp.Controls">
<ContentPage.Content>
<OnIdiom x:TypeArguments="View">
<OnIdiom.Phone>
<controls:BottomTabShell />
</OnIdiom.Phone>
<OnIdiom.Tablet>
<controls:TwoPaneShell />
</OnIdiom.Tablet>
<OnIdiom.Desktop>
<controls:ThreePaneShell />
</OnIdiom.Desktop>
</OnIdiom>
</ContentPage.Content>
</ContentPage>
このように、Idiom ごとに使用するカスタムコントロール自体を切り替えることで、「Phone では単純な画面遷移」「Tablet / Desktop では同時に多くの情報を表示」といった UX を自然に実現できます。
OnPlatform と OnIdiom を併用するときのパターン
実務では「iOS のときだけ Phone / Tablet でさらに分岐したい」といったケースもよく発生します。例えば、
- Android は基本レイアウトを共通にしたい
- iOS だけ Phone / Tablet で余白の付け方を変えたい
というようなケースです。その場合は、次のように OnPlatform の中で OnIdiom をさらにネストする形で書くことができます。
<ContentPage ...>
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<On Platform="iOS">
<OnIdiom x:TypeArguments="Thickness">
<OnIdiom.Phone> 0,20,0,0 </OnIdiom.Phone>
<OnIdiom.Tablet> 20,40,20,20 </OnIdiom.Tablet>
<OnIdiom.Desktop> 0,24,0,0 </OnIdiom.Desktop>
</OnIdiom>
</On>
<On Platform="Android" Value="10,20,20,10" />
</OnPlatform>
</ContentPage.Padding>
</ContentPage>
ここでの考え方は次の通りです。
- まず OnPlatform で「どの OS か」を大まかに分岐
- iOS の場合だけ、OnIdiom で端末種別(Phone / Tablet / Desktop)をさらに分岐
- Android はそこまで細かく変えないので、単一の値で済ませる
このように、OS の違いが支配的なところは OnPlatform、画面サイズの違いが効いてくるところは OnIdiom という役割分担を意識して設計すると、条件分岐が増えても整理された状態を保ちやすくなります。
C# コード側での分岐(DeviceInfo / DeviceDisplay)
OnPlatform / OnIdiom は XAML だけの機能ではなく、C# からも同様の分岐を行えます。もっとも直接的な方法が DeviceInfo と DeviceDisplay を使うやり方です。
DeviceInfo を使ったシンプルな分岐
using Microsoft.Maui.Devices;
Thickness padding =
DeviceInfo.Platform == DevicePlatform.iOS
? new Thickness(0, 20, 0, 0)
: new Thickness(10, 20, 20, 10);
Thickness margin = DeviceInfo.Idiom switch
{
DeviceIdiom.Phone => new Thickness(0, 20, 0, 0),
DeviceIdiom.Tablet => new Thickness(0, 40, 0, 0),
DeviceIdiom.Desktop => new Thickness(0, 60, 0, 0),
_ => new Thickness(0)
};
ポイントは次の通りです。
DeviceInfo.Platform:Android / iOS / WinUI などの OS 判定DeviceInfo.Idiom:Phone / Tablet / Desktop などの端末種別- 単純な if / switch で分岐させればよいので、ViewModel やサービスクラスからでも使いやすい
例えば「Phone のときだけ特定機能を無効にする」「Desktop のときだけショートカットキーを案内する」といった制御も、上記のような判定ロジックで簡単に実装できます。
画面幅と組み合わせた高度な分岐
先ほど触れたように、Windows タブレットは多くの場合 Idiom が Desktop になります。そのため、タブレットかどうかをより厳密に判定したい場合は画面幅(DeviceDisplay)との組み合わせを検討するとよいでしょう。
using Microsoft.Maui.Devices;
bool IsWideDesktopTablet()
{
var idiom = DeviceInfo.Idiom;
var displayInfo = DeviceDisplay.MainDisplayInfo;
// ピクセル単位の幅を論理ピクセルに変換
var width = displayInfo.Width / displayInfo.Density;
// Desktop かつ 一定以上の幅を「タブレット的な画面」とみなす例
return idiom == DeviceIdiom.Desktop && width >= 1000;
}
こういった関数を 1 箇所にまとめておき、
- Phone 的なレイアウト
- Tablet 的なレイアウト(Desktop タブレットも含む)
を切り替えることで、より現実的な UI 最適化が行えます。
代表的な使い分けパターンまとめ
ここまでの内容を、実務でよく遭遇するシチュエーションごとに整理した表がこちらです。
| やりたいこと | 主に使う仕組み | 補足・具体例 |
|---|---|---|
| iOS だけ上マージンを増やしたい | OnPlatform / DeviceInfo.Platform | ステータスバーやナビバーとの重なりを避けるための調整 |
| Android だけ影や高さの表現を変えたい | OnPlatform | Material Design 風の Elevation を Android でだけ強調 |
| スマホでは情報を詰めて表示したい | OnIdiom / DeviceInfo.Idiom | Phone のときだけフォントサイズを小さく / 行間を詰める |
| タブレットでは左右に余白を増やしたい | OnIdiom | Tablet のときだけ Margin / Padding を大きくする |
| Desktop では3ペインレイアウトにしたい | OnIdiom + カスタムレイアウト | Desktop のときのみナビゲーション / リスト / 詳細の3分割を実現 |
| WindowsタブレットをPhone/Tablet扱いしたい | OnPlatform + 画面幅(DeviceDisplay) | WinUI & 幅1000以上を Tablet 的な UI とみなすなど |
レスポンシブ設計と OnPlatform / OnIdiom の役割分担
OnPlatform / OnIdiom はあくまで「例外・調整用」であり、最初から OS / Idiom ごとにガチガチに分岐させると、すぐにメンテナンス不能になってしまいます。そこでおすすめなのが、次のような設計方針です。
- まずはレスポンシブなレイアウトで共通化
Gridの*(スター)レイアウトや相対サイズFlexLayoutやHorizontalStackLayout/VerticalStackLayoutで横幅に応じて自然に折り返すMaxWidthRequest/MinimumWidthRequestなどでコンテンツ幅を制御
- 共通レイアウトで吸収できない差分だけ OnPlatform / OnIdiom で上書き
- OS 固有の UI ガイドラインに従うための微調整
- Phone / Tablet / Desktop ごとの「見やすさ」の調整
- よく使う値は ResourceDictionary にまとめて再利用
PagePadding、DefaultMargin、TitleFontSizeなど- 「Phone 共通の余白」「Tablet 共通の余白」などもリソース化しておくと管理が楽
このように段階的に設計すると、
- OS 追加(例:今後新しいプラットフォームをサポート)
- デバイスカテゴリ追加(例:TV 向け UI の追加)
といった要求にも、最小限の修正で対応しやすくなります。
実務でハマりやすいポイントと回避策
Windowsタブレットは基本 Desktop 判定になる
繰り返しになりますが、実機で確認するとWindowsタブレットでも Idiom が Desktop になることが多いです。ここに気付かないまま、
<OnIdiom.Tablet>にタブレット用の値を書いている- しかし Windows タブレットでは Phone / Tablet どちらにも該当せず、Desktop の値が適用される
という状況に陥りがちです。
この問題を避けるための現実的な方針は次の通りです。
- 「Tablet」という言葉を OS 非依存な概念として使うのではなく、あくまで Idiom として表現される一種のカテゴリだと割り切る
- Windowsタブレットのような特殊ケースが重要なプロジェクトでは、画面幅+OnPlatform で判定ロジックを自作する
プリプロセッサディレクティブとの使い分け
.NET MAUI では #if ANDROID や #if IOS といったプリプロセッサディレクティブを使うこともできますが、UI 周りでは可能な限り OnPlatform / OnIdiom を優先するのがおすすめです。
| 手法 | 向いている場面 | コメント |
|---|---|---|
| OnPlatform / OnIdiom | 値や見た目の違い(Margin, Padding, Color, Template など) | XAML で完結しやすく、変更も追いやすい |
| プリプロセッサ指令(#if ANDROID など) | ネイティブ API の呼び分け、プラットフォーム固有サービスの実装 | 依存関係が増えるため、UI 部分では乱用しない方が無難 |
UI の値切り替えは OnPlatform / OnIdiom、ネイティブ API そのものの違いはプリプロセッサ、といった役割分担をしておくとコードベースが整理されます。
デバッグ時は Platform / Idiom をログに出す
「このデバイス、今何として判定されているんだ?」という疑問は、実機検証で必ず出てきます。そのときに毎回 UI を見た目だけで判断するのは大変なので、アプリ起動時に一度だけログに出すようなコードを仕込んでおくと便利です。
using Microsoft.Maui.Devices;
using System.Diagnostics;
void LogDeviceInfo()
{
Debug.WriteLine($"Platform: {DeviceInfo.Platform}");
Debug.WriteLine($"Idiom: {DeviceInfo.Idiom}");
var display = DeviceDisplay.MainDisplayInfo;
var width = display.Width / display.Density;
var height = display.Height / display.Density;
Debug.WriteLine($"Size: {width} x {height}");
}
このログを見れば、「期待どおり Phone / Tablet / Desktop として判定されているか」「画面幅のしきい値は妥当か」といった点を素早く見直すことができます。
まとめ:OnPlatform / OnIdiom 使い分けのチェックリスト
最後に、設計や実装のときに見直せるように、OnPlatform / OnIdiom 使い分けのチェックリストとして整理しておきます。
- OS差で切り替える値か?
- はい → OnPlatform / DeviceInfo.Platform で分岐
- 例:iOS だけ上マージン追加、Android だけ高さ変更、Windows だけフォント変更
- 画面サイズ・端末種別で切り替える値か?
- はい → OnIdiom / DeviceInfo.Idiom で分岐
- 例:Phone は余白少なめ、Tablet は広め、Desktop は3ペインレイアウト
- Windowsタブレットなどの特殊ケースに配慮が必要か?
- はい → OnPlatform + DeviceDisplay で独自ロジックを作る
- 同じ OnPlatform / OnIdiom 記述をコピペしていないか?
- している → ResourceDictionary に切り出し、共通リソース化する
- まずレスポンシブ設計で吸収できないか?
- できる → Grid / FlexLayout / StackLayout などで OS 共通のレイアウトを組む
- できない差分だけ OnPlatform / OnIdiom で上書きする
この方針で設計しておけば、アプリの画面数が増えても「どこで何を分岐させているのか」が追いやすくなり、保守や機能追加のコストを抑えることができます。OnPlatform と OnIdiom はどちらも強力な仕組みですが、役割を整理して「OS差」と「端末種別差」をきちんと分けて使うことが、.NET MAUI アプリを長く育てていくうえでの重要なポイントになります。

コメント