How to Develop a Tinder-Like Koloda Animation in Swift: Source Code and Step-by-Step Explanation

Tinder’s swipe right to like and left to skip has become the killer feature of the app and quickly migrated to other applications. Shopping apps like Fancy are following suit with similar swipe-to-like app formats. Viber uses Tinder-like swipes to help users discover new channels, and even Chrome for iOS uses cards to manage bookmarks.

koloda animation

This shows that this animation is on high gear, offering a unique user experience. Items on a card grab users’ attention and urge them to take action, thus increasing user engagement.

Our designer Dmitry Goncharov decided to create an animation that follows Tinder’s trend. We called our Tinder-style card-based animation Koloda which is a Ukrainian word for deck (of cards), and it sounds like fun to us.

The component can be used in different local event apps, and even in Tinder if it adds a possibility to choose dating places. The concept created by Dmitriy was implemented by Eugene Andreyev, our iOS developer. Check out our Swift animation on GitHub.

Read also: How to Create an App Design That Works

How we built Koloda animation

by Eugene Andreyev

Tinder’s swipe-to-like interface has been borrowed by various apps, so there are a few ready-made mobile libraries and iOS animation examples out there that an app developer can use. At first, I decided to look at MDCSwipeToChoose and TinderSimpleSwipeCards but as it turned out, these solutions weren’t perfect for my particular case.

I wanted the animation to be as simple and convenient as the data source driven views like UITableView. Therefore, I created a custom component for the animation. It consists of the three main parts:

  1. DraggableCardView – a card which displays content.
  2. OverlayView – a dynamic view which changes depending on where a user drags a card (to the left or to the right).
  3. KolodaView – a view that controls loading and interactions between cards.

Koloda Swipe to like tinder interface

DraggableCardView implementation

As I already mentioned, DraggableCardView is a card that displays content. There are many tutorials on the internet that explain how to animate cards in the Tinder style. I chose one of these solutions, looked at it, changed a few things, and here I am with my DraggableCardView implemented with the help of UIPanGestureRecognizer and CGAffineTransform. See the coding part below:

func panGestureRecognized(gestureRecognizer: UIPanGestureRecognizer) {
       xDistanceFromCenter = gestureRecognizer.translationInView(self).x
       yDistanceFromCenter = gestureRecognizer.translationInView(self).y

       let touchLocation = gestureRecognizer.locationInView(self)
       switch gestureRecognizer.state {
       case .Began:
           originalLocation = center

           animationDirection = touchLocation.y >= frame.size.height / 2 ? -1.0 : 1.0      
           layer.shouldRasterize = true

       case .Changed:

           let rotationStrength = min(xDistanceFromCenter! / self.frame.size.width, rotationMax)
           let rotationAngle = animationDirection! * defaultRotationAngle * rotationStrength
           let scaleStrength = 1 - ((1 - scaleMin) * fabs(rotationStrength))
           let scale = max(scaleStrength, scaleMin)
           layer.rasterizationScale = scale * UIScreen.mainScreen().scale
           let transform = CGAffineTransformMakeRotation(rotationAngle)
           let scaleTransform = CGAffineTransformScale(transform, scale, scale)

           self.transform = scaleTransform
           center = CGPoint(x: originalLocation!.x + xDistanceFromCenter!, y: originalLocation!.y + yDistanceFromCenter!)
           updateOverlayWithFinishPercent(xDistanceFromCenter! / frame.size.width)
           //100% - for proportion
           delegate?.cardDraggedWithFinishPercent(self, percent: min(fabs(xDistanceFromCenter! * 100 / frame.size.width), 100))
       case .Ended:
           layer.shouldRasterize = false
       default :

When a user starts dragging a top card, it’s turning and becoming shorter all the way until it reaches an action margin (go or pass an event), and after that it moves away from the screen. The distance to the action margin is represented in percent (100%). While the top card is being dragged, the card below is reacting too – it’s either expanding or contracting. In other words, the animation of an upper and a lower card stops simultaneously.

The overlay gets updated with every move. It changes transparency in the process of animation ( 5% –  hardly seen, 100% – clearly seen).

In order to avoid a card’s edges becoming sharp during movement I used shouldRasterize layer option.

What’s more, I had to consider reset situation which happens once a card fails to reach the action margin (ending point) and comes back to the initial state. I used Facebook Pop framework for this situation, and also for the “undo” action. If you remember, this framework drives animations and transitions in Paper app. It supports dynamic bounce animations and allows to build realistic interactions based on physics with just a few lines of code.

OverlayView implementation

OverlayView is a view that is added on top of a card during animation. It has only one variable called overlayState with two options: when a user drags a card to the left, the overlayState adds a red hue to the card, and when a card is moved to the right, the variable uses the other option to make the UI become green.

To implement custom actions for the overlay, we should inherit from OverlayView, and reload the operation didSet in the overlayState:

public enum OverlayMode{
   case None
   case Left
   case Right

public class OverlayView: UIView {
      public var overlayState:OverlayMode = OverlayMode.None

class ExampleOverlayView: OverlayView {
override var overlayState:OverlayMode  {
       didSet {
           switch overlayState {
           case .Left :
               overlayImageView.image = UIImage(named: overlayLeftImageName)
           case .Right :
               overlayImageView.image = UIImage(named: overlayRightImageName)
               overlayImageView.image = nil




KolodaView implementation

The KolodaView class does card loading and card management job. You can either implement it in the code or in the Interface Builder. Then, you should specify a datasource and add a delegate (optional). After that, you should implement the following methods of the KolodaViewDataSource protocol in the datasource-class:

    func kolodaNumberOfCards(koloda: KolodaView) -> UInt
    func kolodaViewForCardAtIndex(koloda: KolodaView, index: UInt) -> UIView
    func kolodaViewForCardOverlayAtIndex(koloda: KolodaView, index: UInt) -> OverlayView?

Regarding the callbacks, we get them through the delegate’s methods.


Remember our story about developing the Guillotine menu animation for Android where Dmytro Denysenko, our Android developer, had to resort to high school Math course to build a custom interpolator? Geometry also helped me in my iOS development endeavours!

The most interesting thing in the Tinder-like animation is movement of lower cards while a user is dragging an upper card. I wanted to make the Koloda animation flexible, so that I could easily specify the number of cards I want to display on the screen. So I took a piece of paper and started my calculations.

KolodaViewhad to display a correct number of cards below the top card, and make them occupy the right positions when the animation starts. To make it possible, I had to calculate frames for all the cards by adding the corresponding indexes to each element. For example, the first card has an [i] index, the second one would have a [i+1] index, the third – [i+2], and so on.

You can see the calculations of the original frame and the size of the first card below:

developing Tinder cards

And in the code:

private func frameForCardAtIndex(index: UInt) -> CGRect {
       let bottomOffset:CGFloat = 0
       let topOffset = backgroundCardsTopMargin * CGFloat(self.countOfVisibleCards - 1)
       let xOffset = backgroundCardsLeftMargin * CGFloat(index)
       let scalePercent = backgroundCardsScalePercent
       let width = CGRectGetWidth(self.frame) * pow(scalePercent, CGFloat(index))
       let height = (CGRectGetHeight(self.frame) - bottomOffset - topOffset) * pow(scalePercent, CGFloat(index))
       let multiplier: CGFloat = index > 0 ? 1.0 : 0.0
       let previousCardFrame = index > 0 ? frameForCardAtIndex(max(index - 1, 0)) : CGRectZero
       let yOffset = (CGRectGetHeight(previousCardFrame) - height + previousCardFrame.origin.y + backgroundCardsTopMargin) * multiplier
       let frame = CGRect(x: xOffset, y: yOffset, width: width, height: height)     

       return frame

Now, since we know the indexes, card frames, and also a percent at which the animation ends (from the DraggableCardView), we can easily find out where the cards below will go once an upper card is swiped. After than we can implement PercentDrivenAnimation.

As a result, I achieved an easy to use UIView animation for iOS with an interesting name Koloda. Any developer can customize it by setting their content and overlay. In the future, I’d like to make it possible to customize frames’ calculations and animations so that any developer can make their own unique component.

Read also our article Koloda Tinder-like animation Version 2 where we improved funcationality of existing animation.

Check out KolodaView animaton on GitHub

4.3/ 5.0
Article rating
Remember those Facebook reactions? Well, we aren't Facebook but we love reactions too. They can give us valuable insights on how to improve what we're doing. Would you tell us how you feel about this article?
Excited to create something outstanding?

We share the same interests.

Contact us

We use cookies to personalize our service and to improve your experience on the website and its subdomains. We also use this information for analytics.