Xamarin.Forms MasterDetailPage の Master の幅を可変にする (iOS 編)


MasterDetailPage で表示される Master の幅はサイズが固定になっています。
これを UI 側から変更できるような仕組みが欲しいと思い、MasterDetailPage の拡張クラスを作成しました。

完成イメージはこちら

MasterDetail クラスを拡張

標準で幅を変更するためのプロパティが提供されていないため、MasterDetailPage クラスを継承し、プロパティを追加します。

public static readonly BindableProperty MasterFractionProperty =
    BindableProperty.Create(
        propertyName: nameof(MasterFraction),
        returnType: typeof(double),
        declaringType: typeof(VariableMasterDetailPage),
        defaultValue: 0.0,
        defaultBindingMode: BindingMode.TwoWay,
        propertyChanged: OnMasterFractionChanged);

public double MasterFraction
{
    get { return (double)GetValue(MasterFractionProperty); }
    set { SetValue(MasterFractionProperty, value); }
}

protected static void OnMasterFractionChanged(BindableObject bindable, object oldValue, object newValue)
{
    var page = (VariableMasterDetailPage)bindable;

    var newFraction = (double)newValue;
    if (newFraction < page.MinimumFraction || page.MaximumFraction < newFraction)
        throw new ArgumentOutOfRangeException();

    var oldFraction = (double)oldValue;
    if (oldFraction.Equals(newFraction) == false)
    {
        page.MasterFraction = newFraction;
        page.OnPropertyChanged(nameof(page.MasterFraction));
    }
}

CustomRenderer

TabletMasterDetailRenderer を継承した CustomRenderer を iOS のプロジェクトに追加します。MaximumPrimaryColumnWidth を変更しておくのがポイント。

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using UIKit;
using VariableMaster;
using VariableMaster.Controls;
using VariableMaster.iOS;

[assembly: ExportRenderer(typeof(VariableMasterDetailPage), typeof(VarriableMasterDetailPageRenderer))]

namespace VariableMaster.iOS
{
    public class VarriableMasterDetailPageRenderer : TabletMasterDetailRenderer
    {
        protected override void OnElementChanged(VisualElementChangedEventArgs e)
        {
            var page = e.NewElement as VariableMasterDetailPage;
            if (page != null)
            {
                page.PropertyChanged += (sender, args) =>
                {
                    if (args.PropertyName == "MasterFraction")
                    {
                        MaximumPrimaryColumnWidth = (nfloat)(page.Width * page.MasterFraction / 100);
                        PreferredPrimaryColumnWidthFraction = (nfloat)page.MasterFraction / 100;
                    }
                };
            }

            base.OnElementChanged(e);
        }
    }
}

全体のソースコードはこちらのリポジトリを参照してください。おかしなところがあれば PR よろしくお願いします。
https://github.com/smallgeek/VariableMaster