I read, that we can use platform-specific C# in MAUI projects, but I have the need to use a platform-specific XAML file. Couldn't find anything in the docs.
Background: On Android a SwipeView inside a ListView throws a Java error when removing an item from the bound ObservableCollection ("The specified child already has a parent. You must call removeView() on the child's parent first."), so I use a CollectionView here (no error). On iOS, the CollectionView does extend outside the lower end of the page, when used together with other elements on a page (it seems, on iOS a CollectionView does want to take the full screen height), so I have to use ListView here. On iOS though, the above mentioned error does not occur.
You can achieve this with separate XAML files for each platform. Just write two different versions of your XAML, one for iOS and one for Android. Then, during runtime, instantiate the right one based on the system your application is running on.
For entire pages, you could just create one version of the page for Android and one for iOS (and other ones for the remaining platforms respectively, if needed).
Let's call the Android page HelloFromAndroid.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.HelloFromAndroid"
Title="HelloFromAndroid"
Shell.PresentationMode="Modal">
<VerticalStackLayout>
<Label
Text="Hello from Android!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
The iOS version will be called HelloFromiOS.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.HelloFromiOS"
Title="HelloFromiOS"
Shell.PresentationMode="Modal">
<VerticalStackLayout>
<Label
Text="Hello from iOS!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentPage>
You can either decide which version to open during runtime, or you can use multi-targeting/conditional compilation. You do this from your code behind, e.g. the MainPage.xaml.cs:
private async void OpenHelloView(object sender, EventArgs e)
{
if (DeviceInfo.Platform == DevicePlatform.Android)
{
await Navigation.PushAsync(new HelloFromAndroid());
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
await Navigation.PushAsync(new HelloFromiOS());
}
}
private async void OpenHelloView(object sender, EventArgs e)
{
#if ANDROID
await Navigation.PushAsync(new HelloFromAndroid());
#elif IOS
await Navigation.PushAsync(new HelloFromiOS());
#endif
}
This version is the preferred way, because the compiled code only contains the required platform-specific call.
For Views, this is a little more complicated, but also possible.
If you need a platform-specific View, you cannot add it to your page's XAML and instead need to instantiate it dynamically during runtime from the code-behind and add it as a child to another View or a Layout.
Let's create a View called ViewAndroid:
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.ViewAndroid">
<Label
Text="Hello from Android!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
And let's also create a View called ViewiOS:
XAML
<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.ViewiOS">
<Label
Text="Hello from iOS!"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentView>
Now, let's create a Page that consumes the platform-specific Views. First, we create some XAML. For simplicity, I'm using an empty VerticalStackLayout
that I give a name using the x:Name
property:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiSamples.Views.Platform.PageWithPlatformSpecificView"
Title="PageWithPlatformSpecificView"
Shell.PresentationMode="Modal">
<VerticalStackLayout x:Name="VerticalLayout" />
</ContentPage>
Now, I can access the VerticalLayout
field from my code-behind and add child elements to it. This can again be done dynamically either via a runtime decision or via conditional compilation.
public partial class PageWithPlatformSpecificView : ContentPage
{
public PageWithPlatformSpecificView()
{
InitializeComponent();
if (DeviceInfo.Platform == DevicePlatform.Android)
{
VerticalLayout.Add(new ViewAndroid());
}
if (DeviceInfo.Platform == DevicePlatform.iOS)
{
VerticalLayout.Add(new ViewiOS());
}
}
}
public partial class PageWithPlatformSpecificView : ContentPage
{
public PageWithPlatformSpecificView()
{
InitializeComponent();
#if ANDROID
VerticalLayout.Add(new ViewAndroid());
#elif IOS
VerticalLayout.Add(new ViewiOS());
#endif
}
}
Again, the conditional compilation is the preferred way.
<OnPlatform>
You can also do this in XAML, but beware that this approach will include all views of all platforms in the app bundle, e.g.:
<ContentView>
<OnPlatform x:TypeArguments="View">
<On Platform="Android">
<android:ViewAndroid />
</On>
<On Platform="iOS">
<ios:ViewiOS />
</On>
</OnPlatform>
</ContentView>
The same works with <OnIdiom>
and other markup.
Technically, you could also use filename-based multi-targeting to only include the right version of the XAML in the final build.
However, it requires a complex setup in the .csproj file to configure multi-targeting for XAML files, which is not officially supported. I have written an answer on this for a similar problem that deals with platform-specific resource dictionaries here: https://stackoverflow.com/a/74338355/4308455
As you can see, there are different approaches and possibilities to accomplish this with platform-specific XAML. It's not exactly the same as platform-specific C# code, but it can be useful for certain scenarios. I hope this helps you and anyone else with similar requirements.
I've tried this myself and added the code to my MAUI samples repository, if you're interested.