眠いしお腹すいたし(´・ω・`)

C#関連を主に書きます。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Xamarin.FormsでSnackBarを表示してみた

はじめに

今回はAndroidのSnackBarをXamarin.FormsでAndroid/iOSで実装してみました。

完成動画はこちら

コードはこちら

github.com

SnackBarはAndroidのマテリアルデザインライブラリに含まれる表現で、画面下からちょこっと出てきて通知を行ったりToastと違いタップによって動作を行ったりすることができます。

今回はXamairn.FormsでSnackBarをAndroid/iOSで使用してみたいと思います。

共通部分解説

まずは共通ライブラリで使用するインターフェース

public interface ISnackBar
{
    void Show(string text, int duration, string actionText, Action action);
}

本来ならもっとオプション化が必要になりますが、今回はそれは主旨ではないので省きます。

Android実装開設

XamarinForms_SnackBar/AndroidSnackBar.cs at master · yuka1984/XamarinForms_SnackBar · GitHub

AndroidはSnackBarを直接呼び出しているだけなので特に特殊なことはないです。

iOS実装開設

XamarinForms_SnackBar/TouchSnackBar.cs at master · yuka1984/XamarinForms_SnackBar · GitHub

考え方としてはUIViewでSnackBarの見た目を構築してUIView.Animateにて表示アニメーション、時間経過後およびAction実行時に閉じるアニメーションを行っています。

KeyWindowにAddSubViewしているのでXamarin.Formsで画面遷移してもSnackBarは表示されたままになります。

これを例えば表示中のViewに表示したいようなケースの場合にはwindowsのPresentatedViewControllerにAddSubViewすればよいです。

How to acces the current view UIViewController from an external service — Xamarin Forums

SnackBarのレイアウトはLaytouAnchorを使用しています。

iOS 9で追加されたNSLayoutAnchor使うと簡単にわかりやすく間違えずにNSLayoutConstraint(制約)が作れます【Auto Layout】 - Qiita

UIView.Animateでは定義したTopのLayoutAnchorに対してConstantプロパティを変更してAnimationを行っています。

UIView.Animate(OpenDuration, () =>
{
    initialTop.Constant -= BoxHeighy;
    window.LayoutIfNeeded();
});

AndroidのSnackBarは重ねて表示するということがなくSnackBar表示中に別のSnackBarを表示した場合には、まず現在表示中のSnackBarが閉じて新しいSnackBarが開くという動作になるため、iOSでもそのように実装しました。

_addedsnacks にUIViewと表示待機ノ為のTask.Delayに設定したCancellationTokenSourceを保持させておきShowメソッドが呼び出された際にはClearSnackにてDelayをキャンセルさせて表示中SnackBarを閉じ、その後新しいSnackBarを表示します。

private async Task ClearSnack()
{
    var count = _addedsnacks.Count;
    foreach (var added in _addedsnacks)
        added.Item1.Cancel();

    await Task.Delay((int) (CloseDuration * 1000 * count));
}

Actionの実行に関しては引数で受け取ったActionクラスを使って表示待機をキャンセル後Actionを実行しています。

button.TouchUpInside += (sender, e) =>
{
    cancel.Cancel();
    action();
};

SnackBarの利用

めんどくさがってすいません。

もう少しちゃんと実装するならDependencyService使うなりContainer使うなりしてください。

    public partial class SnackBarSamplePage : ContentPage
    {

        public SnackBarSamplePage()
        {
            InitializeComponent();
            SnackButton.Clicked += ButtonOnClicked;
#if __IOS__
             snackbar = new SnackBarSample.iOS.TouchSnackBar();
#else
            snackbar = new SnackBarSample.Droid.AndroidSnackBar();
#endif
        }
        private ISnackBar snackbar = null;

        private void ButtonOnClicked(object sender, EventArgs eventArgs)
        {
            snackbar.Show("Description" + DateTime.Now.ToString(), 2500, "Click", () => { DisplayAlert("alert", "click", "close"); });
        }
    }

おわりに

いかがでしょうか?

こんな感じで割と簡単にアニメーション付きの特殊動作などを実行することができます。

ではでは( `ー´)ノ