Search code examples
mauiswipe-gesture

How to implement Swipe Button(with events) in MAUI App?


How to imeplemnt swipe button with an event in MAUI app? Looking for a button with an image or icon able to swipe left or right end.

Attached sample image

enter image description here


Solution

  • To implement the swipe button with an event in MAUI, you need to write a custom control that represents thumb and track-bar and also can invoke SlideCompleted() event when the slide action is completed.

    Here's the demo for your reference:

    1. Create a custom control: MySlideView.cs that implements AbsoluteLayout:
       public class MySlideView : AbsoluteLayout
        {
    
            public View MyTrackBar
            {
                get { return (View)GetValue(TrackBarProperty); }
                set { SetValue(TrackBarProperty, value); }
            }
    
            public static readonly BindableProperty TrackBarProperty =
                BindableProperty.Create(
                    "MyTrackBar", typeof(View), typeof(MySlideView),
                    defaultValue: default(View));
    
            public View MyFillBar
            {
                get { return (View)GetValue(FillBarProperty); }
                set { SetValue(FillBarProperty, value); }
            }
    
            public static readonly BindableProperty FillBarProperty =
                BindableProperty.Create(
                    "MyFillBar", typeof(View), typeof(MySlideView),
                    defaultValue: default(View));
    
            public View MyThumb
            {
                get { return (View)GetValue(ThumbProperty); }
                set { SetValue(ThumbProperty, value); }
            }
    
            public static readonly BindableProperty ThumbProperty =
           BindableProperty.Create(
               "MyThumb", typeof(View), typeof(MySlideView),
               defaultValue: default(View));
    
       
            private PanGestureRecognizer _panGesture = new PanGestureRecognizer();
            private View _gestureListener;
            public MySlideView()
            {
                _panGesture.PanUpdated += OnPanGestureUpdated;
                SizeChanged += OnSizeChanged;
    
                _gestureListener = new ContentView { BackgroundColor = Colors.White, Opacity = 0.05 };
                _gestureListener.GestureRecognizers.Add(_panGesture);
            }
    
            public event EventHandler SlideCompleted;
    
            private const double _fadeEffect = 0.5;
            private const uint _animLength = 50;
            async void OnPanGestureUpdated(object sender, PanUpdatedEventArgs e)
            {
                if (MyThumb == null || MyTrackBar == null || MyFillBar == null)
                    return;
    
                switch (e.StatusType)
                {
                    case GestureStatus.Started:
                        await MyTrackBar.FadeTo(_fadeEffect, _animLength);
                        break;
    
                    case GestureStatus.Running:
                       
                        var x = Math.Max(0, e.TotalX);
                        var y = Math.Max(0, e.TotalY);
                        if (x > (Width - MyThumb.Width))
                            x = (Width - MyThumb.Width);
                        if (y > (Height - MyThumb.Height)) y = (Height - MyThumb.Height);
    
                        MyThumb.TranslationX = x;
                        SetLayoutBounds((IView)MyFillBar, new Rect(0, 0, x + MyThumb.Width / 2, Height));
                        break;
    
                    case GestureStatus.Completed:
                        var posX = MyThumb.TranslationX;
                        SetLayoutBounds((IView)MyFillBar, new Rect(0, 0, 0, this.Height));
    
                       
                        await Task.WhenAll(new Task[]{
                        MyTrackBar.FadeTo(1, _animLength),
                        MyThumb.TranslateTo(0, 0, _animLength * 2, Easing.CubicIn),
                    });
    
                        if (posX >= (Width - MyThumb.Width - 10/* keep some margin for error*/))
                            SlideCompleted?.Invoke(this, EventArgs.Empty);
                        break;
                }
            }
    
            void OnSizeChanged(object sender, EventArgs e)
            {
                if (Width == 0 || Height == 0)
                    return;
                if (MyThumb == null || MyTrackBar == null || MyFillBar == null)
                    return;
    
    
                Children.Clear();
    
                SetLayoutFlags((IView)MyTrackBar, AbsoluteLayoutFlags.SizeProportional);
                SetLayoutBounds((IView)MyTrackBar, new Rect(0, 0, 1, 1));
                Children.Add(MyTrackBar);
    
                SetLayoutFlags((IView)MyFillBar, AbsoluteLayoutFlags.None);
                SetLayoutBounds((IView)MyFillBar, new Rect(0, 0, 0, this.Height));
                Children.Add(MyFillBar);
    
                SetLayoutFlags((IView)MyThumb, AbsoluteLayoutFlags.None);
                SetLayoutBounds((IView)MyThumb, new Rect(0, 0, this.Width / 5, this.Height));
                Children.Add(MyThumb);
    
                SetLayoutFlags((IView)_gestureListener, AbsoluteLayoutFlags.SizeProportional);
                SetLayoutBounds((IView)_gestureListener, new Rect(0, 0, 1, 1));
                Children.Add(_gestureListener);
    
            }
        }
    
    

    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"
                 xmlns:local="clr-namespace:MauiAppSwipe"
                 x:Class="MauiAppSwipe.MainPage">
    
     
       <VerticalStackLayout
                Spacing="25"
                Padding="30,0"
                VerticalOptions="Center">
    
            <local:MySlideView HeightRequest="60" SlideCompleted="SlideCompleted"  >
                <local:MySlideView.MyThumb>
                    <Border StrokeShape="RoundRectangle 60,60,60,60" StrokeThickness="0" BackgroundColor="AliceBlue" Padding="0" Margin="-20,0,0,0" HeightRequest="60" WidthRequest="60" x:Name="myframe" >
                        <Image x:Name="myimg" Source="dotnet_bot.png" Aspect="AspectFit"  VerticalOptions="Center" HorizontalOptions="Center" HeightRequest="60" WidthRequest="60" />
                    </Border>
                </local:MySlideView.MyThumb>
    
                <local:MySlideView.MyTrackBar>
                    <Border StrokeShape="RoundRectangle 60,60,60,60" StrokeThickness="0" BackgroundColor="DarkGray" Padding="0">
                        <Label TextColor="White" Text="SWIPE TO ACTIVATE" HorizontalOptions="CenterAndExpand" VerticalOptions="CenterAndExpand" />
                    </Border>
                </local:MySlideView.MyTrackBar>
    
                <local:MySlideView.MyFillBar>
                    <Border  StrokeShape="RoundRectangle 60,60,60,60" StrokeThickness="0" BackgroundColor="Transparent" Padding="0,0,20,20" />
                </local:MySlideView.MyFillBar>
            </local:MySlideView>
          </VerticalStackLayout>
    
    </ContentPage>
    
    

    Code-behind:

    
    public partial class MainPage : ContentPage
    {
      
          public MainPage()
          {
                InitializeComponent();
          }
    
    
    
        private void SlideCompleted(object sender, EventArgs e)
        {
            myframe.Margin= 20;
           //Raise SlideCompleted() event when the slide action is completed.
        }
    }
    
    

    Output:

    enter image description here