.NET MAUI でナビゲーションバーの左側にアイテムを配置する

.NET MAUI も Xamarin Forms と同様に、標準ではナビゲーションバーの左側に ToolbarItem を配置できない。Xamarin Forms の頃は、カスタム Renderer で対応していた。

tnakamura.hatenablog.com

MAUI ではどうすればいいかというと、カスタム Handler で対応できた。

using Microsoft.Maui.Handlers;
#if IOS
using System.Reflection;
using UIKit;
using ContentView = Microsoft.Maui.Platform.ContentView;
#endif

namespace HelloMaui.Handlers;

public class CustomContentPageHandler : PageHandler
{
#if IOS
    private Page? _page;

    protected override void ConnectHandler(ContentView platformView)
    {
        if (VirtualView is Page page)
        {
            _page = page;
            _page.Loaded += HandleLoaded;
        }
        base.ConnectHandler(platformView);
    }

    private void HandleLoaded(object? sender, EventArgs e)
    {
        if (_page is not null)
        {
            _page.Loaded -= HandleLoaded;
            _page = null;

            var navigationController = ViewController?.NavigationController;
            if (navigationController?.NavigationBar is not null)
            {
                CustomizeNavigationBar(navigationController);
            }
        }
    }

    private void CustomizeNavigationBar(UINavigationController navigationController)
    {
        var navigationItem = navigationController.TopViewController.NavigationItem;
        var leftNativeButtons = (navigationItem.LeftBarButtonItems ?? []).ToList();
        var rightNativeButtons = (navigationItem.RightBarButtonItems ?? []).ToList();
        var newLeftButtons = new List<UIBarButtonItem>();
        var newRightButtons = new List<UIBarButtonItem>();
        foreach (var nativeItem in rightNativeButtons)
        {
            var field = nativeItem.GetType().GetField("_item", BindingFlags.NonPublic | BindingFlags.Instance);
            if (field is null)
            {
                return;
            }
            if (field.GetValue(nativeItem) is not ToolbarItem info)
            {
                return;
            }
            if (info.Priority < 0)
            {
                newLeftButtons.Add(nativeItem);
            }
            else
            {
                newRightButtons.Add(nativeItem);
            }
        }
        foreach (var nativeItem in leftNativeButtons)
        {
            newLeftButtons.Add(nativeItem);
        }
        navigationItem.RightBarButtonItems = newRightButtons.ToArray();
        navigationItem.LeftBarButtonItems = newLeftButtons.ToArray();
    }
#endif
}

作成したカスタム Handler は登録しておく必要がある。

using HelloMaui.Handlers;
using Microsoft.Extensions.Logging;

namespace HelloMaui;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddHandler<ContentPage, CustomContentPageHandler>();
            })
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            });
#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}