Xamarin.Forms Swipecard Tutorial

We often need to create an app that will run on multiple platforms, and Xamarin.Forms is a great tool to use to do this.

In this article we take a look at implementing a swipe view control. This style of control was made popular by Tinder’s swipe right for “yes” swipe left for “no”, with many dating apps adopting its usage. But it’s not just for dating, there are countless other apps using this style of control; including fashion, food, real estate, and job hunting (14 apps that use swipe UI). The functionality essentially presents the user with a stack of cards that are swiped through, liking or disliking what is seen.

 
It would be impractical to create 100 card controls and stack them one on top of each other and allow the user to swipe their way through the stack. Instead we create the minimum number of UI controls to show a card, and switch in the data as the user swipes through the cards.

The number of cards you choose to have in your stack depends on your design, for example you could show a stack of 4 or 5 cards by applying a slight random rotation to each, as it is added to the stack (as Tinder does).

For this control we will use two cards (the minimum needed for it to work).

The back card will be scaled to appear smaller than the front. When the user drags the front card to the left or the right, we’ll rotate the front card and scale the back card to give the appearance of moving to the front.
We can have as many card data items as we need in a array or list. As the user swipes the front card away, we fill its place with the next card data item and show it at the back. This way we only ever need two UI controls to represent and endless stack of cards.

For this swipe control, we will need to know the touch start, moving, and end events, as well as the x and y location. Xamarin forms provides various Gesture Recognizers, one of which is the PanGestureRecognizer, that gives us exactly what we need.

To implement this control, we implement a CardView and a CardStackView. The CardView implements the content for a card, whilst the CardStackView manages the stack and moving the top card left or right to show the card below.

In the CardStackView we add a PanGestureRecognizer

1
2
3
4
5
var panGesture = new PanGestureRecognizer ();
 
panGesture.PanUpdated += OnPanUpdated;
 
GestureRecognizers.Add (panGesture);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void OnPanUpdated (object sender, PanUpdatedEventArgs e)
{
    switch (e.StatusType) {
       case GestureStatus.Started:
          HandleTouchStart();
          break;
       case GestureStatus.Running:
          HandleTouch((float)e.TotalX); 
          break;
       case GestureStatus.Completed: 
          HandleTouchEnd();
          break;
     }
 }

Translating and scaling UI objects is made very simple in Xamarin.Forms. You can directly change the Scale or Location member variables of a View.

1
2
3
Scale
TranslateX
TranslateY

or you can use the animation functions that include easing for free via

1
2
ScaleTo
TranslateTo

In CardStackView we use both. We change the member variables directly in HandleTouch(), whilst a card is being dragged, and we use the animations functions in HandleTouchEnd(), to put the card back if it not been dragged far enough to be considered swiped off the screen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// back card scale
const float BackCardScale = 0.8f;
 
// the card at the top of the stack
int topCardIndex;
 
// distance the card has been moved
float cardDistance = 0;
 
public void HandleTouch(float diff_x)
{
     var topCard = cards [topCardIndex];
     var backCard = cards [PrevCardIndex (topCardIndex)];
 
     // move the top card
     if (topCard.IsVisible) {
 
        // move the card
        topCard.TranslationX = (diff_x);
 
        // calculate a angle for the card
        float rotationAngel = (float)(0.3f * Math.Min (diff_x / this.Width, 1.0f));
        topCard.Rotation = rotationAngel * 57.2957795f;
 
        // keep a record of how far its moved
        cardDistance = diff_x;
     }
 
     // scale the backcard
     if (backCard.IsVisible) {
        backCard.Scale = Math.Min (BackCardScale + Math.Abs ((cardDistance /
        CardMoveDistance) * (1.0f - BackCardScale)), 1.0f);
     }
}

While the card is moving, HandleTouch() moves, scales, and rotates the CardView to achieve the desired effect.

When the user ends the drag by lifting their finger, HandleTouchEnd() is called, that determines which way the card was dragged, and if it was dragged far enough to be considered as swiped off screen.

HandleTouchEnd() then tells the Page if the user dragged a card to the left or to the right, and shows the next card.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
async public void HandleTouchEnd()
{
     ignoreTouch = true;
 
     var topCard = cards [topCardIndex];
 
     // if the card was move enough to be considered swiped off
     if (Math.Abs ((int)cardDistance) > CardMoveDistance) {
 
         // move off the screen
         await topCard.TranslateTo (cardDistance>0?this.Width:-this.Width, 0, AnimLength/2,
            Easing.SpringOut);
         topCard.IsVisible = false;
 
         if (SwipedRight != null && cardDistance > 0)
         {
         SwipedRight(itemIndex);
         }  
         else if (SwipedLeft != null)
         {
            SwipedLeft(itemIndex);
         }
 
         // show the next card
         ShowNextCard ();
 
     }
     // put the card back in the center
     else {
 
         // move the top card back to the center
         topCard.TranslateTo ((-topCard.X), - topCard.Y, AnimLength, Easing.SpringOut);
         topCard.RotateTo (0, AnimLength, Easing.SpringOut);
 
         // scale the back card down
         var prevCard = cards [PrevCardIndex (topCardIndex)];
         await prevCard.ScaleTo(BackCardScale, AnimLength, Easing.SpringOut);
 
     }
 
     ignoreTouch = false;
}

The CardView isn’t worth much discussion. It’s just a Xamarin.Forms View that is implemented to show some text and a picture for a card. If you reused this code for you own application this is the mostly likely class you will need to change to make it display the data and visuals in the way you need.

Incidentally, at Matchbox we use Principle to prototype these interactions. You can find a great selection of sample projects over on Principlerepo.com, like this one.

The full source code is available here.