Burak Üstün

Swipe Cards

For Original Post Click Here

Overview

Ever wonder how to create swipe cards like on the chrome app, tinder and many other apps? Well so did I. After reading many amazing tutorials, I decided to make a simplified version for myself. Here’s a list of the tutorials that inspired me.

Content

There are 3 components that people seem to be using.

  • DraggableCardView: card that displays content
  • OverlayView: This is a dynamic view that is shown when people swipe to a side. (Think the Like, Dislike on tinder cards)
  • DeckView: Loads the cards, shows them

Let’s start off simple.

 class ViewController: UIViewController {

    var cardView:UIView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.cardView = createCardView()
        self.view.addSubview(cardView)
    }
    
    override func viewWillLayoutSubviews() {
        cardView.center = self.view.center
    }

    func createCardView() -> UIView {
        let width = self.view.frame.width * 0.8
        let height = self.view.frame.height * 0.8
        let rect = CGRectMake(0, 0, width, height)
        
        let tempCardView = UIView(frame: rect)
        tempCardView.backgroundColor = UIColor.blueColor()
        tempCardView.layer.cornerRadius = 8;
        tempCardView.layer.shadowOffset = CGSizeMake(7, 7);
        tempCardView.layer.shadowRadius = 5;
        tempCardView.layer.shadowOpacity = 0.5;
        return tempCardView
    }
}

Step 1

We add a UIPanGestureRecognizer on line 9 and 10. The method panGestureRecognized on line 21 will handle what happens when the user tries to swipe.

class ViewController: UIViewController {

    var cardView:UIView!
    var panGestureRecognizer:UIPanGestureRecognizer!
    var originalPoint: CGPoint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panGestureRecognized:")
        self.view.addGestureRecognizer(panGestureRecognizer)
    
        self.cardView = createCardView()
        self.view.addSubview(cardView)
    }
    

    override func viewWillLayoutSubviews()
    
    func createCardView() -> UIView

    func panGestureRecognized(gestureRecognizer: UIPanGestureRecognizer) {
        let xDistance = gestureRecognizer.translationInView(self.view).x
        let yDistance = gestureRecognizer.translationInView(self.view).y
        
        switch gestureRecognizer.state {
        case .Began:
            self.originalPoint = self.view.center
            break
            
        case .Changed:
            updateCardViewWithDistances(xDistance, yDistance)
            break
            
        case .Ended:
            resetViewPositionAndTransformations()
            break
            
        default:
            break
        }
    }
}

We’re missing updateCardViewWithDistances and resetViewPositionAndTransformations.

Step 2

The function updateCardViewWithDistances will update the rotation based on how far you drag the view. It will also scale the view down for added effect.

func updateCardViewWithDistances(xDistance:CGFloat, _ yDistance:CGFloat) {
    let rotationStrength = min(xDistance / 320, 1)
    let fullCircle = (CGFloat)(2*M_PI)
    
    let rotationAngle:CGFloat = fullCircle * rotationStrength / 16
    let scaleStrength:CGFloat = (CGFloat) (1 - fabsf(Float(rotationStrength)) / 2)
    let scale = max(scaleStrength, 0.93)
    
    let newX = self.originalPoint.x + xDistance
    let newY = self.originalPoint.y + yDistance
    
    let transform = CGAffineTransformMakeRotation(rotationAngle)
    let scaleTransform = CGAffineTransformScale(transform, scale, scale)
    
    self.cardView.center = CGPointMake(newX, newY)
    self.cardView.transform = scaleTransform
}

Step 3

The function resetViewPositionAndTransformations will reset the center and transform back to normal.

func resetViewPositionAndTransformations() {
    UIView.animateWithDuration(0.2, animations: {
        self.cardView.center = self.originalPoint;
        self.cardView.transform = CGAffineTransformMakeRotation(0);
    })
}

Result

class ViewController: UIViewController {

    var cardView:UIView!
    var panGestureRecognizer:UIPanGestureRecognizer!
    var originalPoint: CGPoint!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panGestureRecognized:")
        self.view.addGestureRecognizer(panGestureRecognizer)
        
        self.cardView = createCardView()
        self.view.addSubview(cardView)
    }
    
    
    override func viewWillLayoutSubviews() {
        cardView.center = self.view.center
    }
    

    func createCardView() -> UIView {
        let width = self.view.frame.width * 0.5
        let height = self.view.frame.height * 0.5
        let rect = CGRectMake(0, 0, width, height)
        
        let tempCardView = UIView(frame: rect)
        tempCardView.backgroundColor = UIColor.blueColor()
        tempCardView.layer.cornerRadius = 8;
        tempCardView.layer.shadowOffset = CGSizeMake(7, 7);
        tempCardView.layer.shadowRadius = 5;
        tempCardView.layer.shadowOpacity = 0.5;
        return tempCardView
    }
    

    func panGestureRecognized(gestureRecognizer: UIPanGestureRecognizer) {
        let xDistance = gestureRecognizer.translationInView(self.view).x
        let yDistance = gestureRecognizer.translationInView(self.view).y
        
        switch gestureRecognizer.state {
        case .Began:
            self.originalPoint = self.view.center
            break
            
        case .Changed:
            updateCardViewWithDistances(xDistance, yDistance)
            break
            
        case .Ended:
            resetViewPositionAndTransformations()
            break
            
        default:
            break
        }
    }
    
    
    func updateCardViewWithDistances(xDistance:CGFloat, _ yDistance:CGFloat) {
        let rotationStrength = min(xDistance / 320, 1)
        let fullCircle = (CGFloat)(2*M_PI)
        
        let rotationAngle:CGFloat = fullCircle * rotationStrength / 16
        let scaleStrength:CGFloat = (CGFloat) (1 - fabsf(Float(rotationStrength)) / 2)
        let scale = max(scaleStrength, 0.93)
        
        let newX = self.originalPoint.x + xDistance
        let newY = self.originalPoint.y + yDistance
        
        let transform = CGAffineTransformMakeRotation(rotationAngle)
        let scaleTransform = CGAffineTransformScale(transform, scale, scale)
        
        self.cardView.center = CGPointMake(newX, newY)
        self.cardView.transform = scaleTransform
    }
    
    
    func resetViewPositionAndTransformations() {
        UIView.animateWithDuration(0.2, animations: {
            self.cardView.center = self.originalPoint;
            self.cardView.transform = CGAffineTransformMakeRotation(0);
        })
    }
}
May 16, 2017