MAUI Gesture Detection Part-2


I’ll explain the key gestures you can use in MAUI, and then I’ll go through an example covering:

  • Tap Gesture: Taps on elements of the UI can be detected.
  • Pinch Gesture: Giving us a way to detect pinch actions (zoom in/out).
  • Swipe Gesture: Different ways to detect swiping.
  • Pan Gesture: Detecting dragging motions.

They are implemented in .NET MAUI by using GestureRecognizers.

.NET MAUI GestureRecognizers

  • TapGestureRecognizer: Taps captures (e.g. single, double).
  • PinchGestureRecognizer: Pinch actions are captured and generally used for zooming.
  • SwipeGestureRecognizer: It captures swipes (left, right, up, down).
  • PanGestureRecognizer: Catches panning / dragging.

Define the User Interface in XAML

MainPage.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="MauiAppExample.MainPage">

    <StackLayout Padding="20" Spacing="40" VerticalOptions="Center" HorizontalOptions="Center">

        <!-- Label with Tap Gesture -->
        <Label Text="Tap Me!"
               FontSize="Large"
               HorizontalOptions="Center">
            <Label.GestureRecognizers>
                <TapGestureRecognizer NumberOfTapsRequired="1"
                                      Tapped="OnLabelTapped" />
            </Label.GestureRecognizers>
        </Label>

        <!-- Image with Pinch Gesture -->
        <Image Source="dotnet_bot.png"
               WidthRequest="200"
               HeightRequest="200"
               HorizontalOptions="Center">
            <Image.GestureRecognizers>
                <PinchGestureRecognizer PinchUpdated="OnPinchUpdated" />
            </Image.GestureRecognizers>
        </Image>

        <!-- BoxView with Swipe Gesture -->
        <BoxView Color="LightBlue"
                 WidthRequest="150"
                 HeightRequest="150"
                 HorizontalOptions="Center">
            <BoxView.GestureRecognizers>
                <SwipeGestureRecognizer Direction="Left" Swiped="OnBoxViewSwiped" />
                <SwipeGestureRecognizer Direction="Right" Swiped="OnBoxViewSwiped" />
                <SwipeGestureRecognizer Direction="Up" Swiped="OnBoxViewSwiped" />
                <SwipeGestureRecognizer Direction="Down" Swiped="OnBoxViewSwiped" />
            </BoxView.GestureRecognizers>
        </BoxView>

        <!-- Label with Pan Gesture -->
        <Label Text="Drag Me"
               FontSize="Large"
               HorizontalOptions="Center">
            <Label.GestureRecognizers>
                <PanGestureRecognizer PanUpdated="OnPanUpdated" />
            </Label.GestureRecognizers>
        </Label>

    </StackLayout>
</ContentPage>
  • Tap Gesture (TapGestureRecognizer): An event is triggered when the label is simply tapped.
  • Pinch Gesture (PinchGestureRecognizer): It supports pinch to change the zoom in and out of the image.
  • Swipe Gesture (SwipeGestureRecognizer): The BoxView defines different directions (Left, Right, Up, Down).
  • Pan Gesture (PanGestureRecognizer): Enables to drag a label around the screen.

MainPage.xaml.cs(Handle Gesture Events in Code-Behind)

namespace MauiAppExample
{
    public partial class MainPage : ContentPage
    {
        private double _currentScale = 1;
        private double _xOffset = 0;
        private double _yOffset = 0;

        public MainPage()
        {
            InitializeComponent();
        }

        // Tap Gesture Handler
        private async void OnLabelTapped(object sender, EventArgs e)
        {
            await DisplayAlert("Tapped", "Label was tapped!", "OK");
        }

        // Pinch Gesture Handler
        private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            if (e.Status == GestureStatus.Started)
            {
                // Store the current scale factor
                _currentScale = ((Image)sender).Scale;
            }
            else if (e.Status == GestureStatus.Running)
            {
                // Calculate the scale factor relative to the starting scale
                double currentScale = _currentScale * e.Scale;
                currentScale = Math.Max(0.1, currentScale); // Minimum scale
                currentScale = Math.Min(5, currentScale); // Maximum scale

                // Apply the scale to the image
                ((Image)sender).Scale = currentScale;
            }
        }

        // Swipe Gesture Handler
        private void OnBoxViewSwiped(object sender, SwipedEventArgs e)
        {
            BoxView boxView = (BoxView)sender;

            switch (e.Direction)
            {
                case SwipeDirection.Left:
                    boxView.Color = Colors.Red;
                    break;
                case SwipeDirection.Right:
                    boxView.Color = Colors.Green;
                    break;
                case SwipeDirection.Up:
                    boxView.Color = Colors.Yellow;
                    break;
                case SwipeDirection.Down:
                    boxView.Color = Colors.Blue;
                    break;
            }
        }

        // Pan Gesture Handler
        private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            if (e.StatusType == GestureStatus.Running)
            {
                // Translate the label based on the pan gesture
                ((Label)sender).TranslationX = _xOffset + e.TotalX;
                ((Label)sender).TranslationY = _yOffset + e.TotalY;
            }
            else if (e.StatusType == GestureStatus.Completed)
            {
                // Store the translation applied during the pan gesture
                _xOffset += e.TotalX;
                _yOffset += e.TotalY;
            }
        }
    }
}

Tap Gesture (OnLabelTapped):

  • Upon tapping the label, an alert is shown.

Pinch Gesture (OnPinchUpdated):

  • GestureStatus.Started: When the gesture starts, the current scale is stored.
  • GestureStatus.Running: When the pinch action occurs, the image scale is updated.
  • The scale is only allowed to range between a minimum (0.1) and a maximum (5), so as not to get too close.

Swipe Gesture (OnBoxViewSwiped):

  • Color Change of BoxView based onDirection swipe (Left Right Up Down).

Pan Gesture (OnPanUpdated):

  • GestureStatus.Running: Position of the label will be updated based on when the user drags it.
  • GestureStatus.Completed: It stores the translation after the gesture is finished so if the user drags again it’ll continue smoothly.

Further bringing in Interactivity through Gestures

  • Double Tap Gesture:
    • TapGestureRecognizer allows you to set NumberOfTapsRequired to 2 and consider the two taps as different.
  • Multiple GestureRecognizers:
    • But you can attach as many gesture recognizers as you want to the same element (e.g., tap and pinch) in order to provide complex interactions.
  • Animation:
    • An additional means of running interaction in motion can be achieved by combining gestures with animations to create a more dynamic user experience. Imagine animating the scaling whenever you finish a pinch gesture.

Summary of Gesture Types and Use Cases

Gesture RecognizerUse Case
TapGestureRecognizerDetect a tap action, like selecting or activating an element.
PinchGestureRecognizerDetect a pinch action, typically used for zooming.
SwipeGestureRecognizerDetect swiping in specific directions, like deleting an item or scrolling.
PanGestureRecognizerDetect dragging, like moving an item around the screen.