Thursday, December 3, 2015

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.

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
 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
    }
}
 

Initial

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.

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
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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.

1
2
3
4
5
6
func resetViewPositionAndTransformations() {
    UIView.animateWithDuration(0.2, animations: {
        self.cardView.center = self.originalPoint;
        self.cardView.transform = CGAffineTransformMakeRotation(0);
    })
}

Result

Swipe

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
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);
        })
    }
}

Random Posts