Skip to content

Commit c4d1340

Browse files
committed
Added programmatically called animations
1 parent 6285cd3 commit c4d1340

File tree

7 files changed

+133
-9
lines changed

7 files changed

+133
-9
lines changed

CardsViewController.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'CardsViewController'
3-
s.version = '1.0.0'
3+
s.version = '1.1.0'
44
s.summary = 'CardsViewController is an implementation of collection swipeable cards.'
55
s.description = <<-DESC
66
TODO: Add long description of the pod here.

CardsViewController/Classes/AnimationHelpers.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,18 @@ struct AnimationHelpers {
8585
let scale = CGFloat(visibleIndex) * -0.07 + 1.0
8686
return transform.scaledBy(x: scale, y: scale)
8787
}
88+
89+
static func velosity(for direction: SwipeDirection) -> CGPoint {
90+
let const = 2000
91+
switch direction {
92+
case .right:
93+
return CGPoint(x: const, y: -const/2)
94+
case .left:
95+
return CGPoint(x: -const, y: const/2)
96+
case .up:
97+
return CGPoint(x: const/2, y: -const)
98+
case .down:
99+
return CGPoint(x: -const/2, y: const)
100+
}
101+
}
88102
}

CardsViewController/Classes/CardsViewController.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,28 @@ public final class CardsViewController: UIViewController {
5454
}
5555
}
5656

57+
/// Programatically run an animation for the topmost card.
58+
/// The type of animation will be requested from the delegate according with the card index and direction.
59+
public func performCardSwipeAnimation(direction: SwipeDirection) {
60+
guard let card = cards.first else { return }
61+
let animation = swipeAnimation(at: card.absoluteIndex, direction: direction)
62+
guard animation != .none else { return }
63+
finishSwipe(card: card, velocity: AnimationHelpers.velosity(for: direction), direction: direction)
64+
}
65+
66+
/// Animated shake the topmost card to the left and right
67+
public func shakeCard() {
68+
guard let card = cards.first else { return }
69+
card.state = .transformAnimation
70+
let shake = ShakeAnimation(
71+
view: card.containerView,
72+
width: view.bounds.width,
73+
completion: {
74+
card.state = .inStack
75+
}
76+
)
77+
shake.animator.startAnimation()
78+
}
5779

5880
// MARK: - Private methods
5981

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// ShakeAnimation.swift
3+
// CardsViewController
4+
//
5+
// Created by Anton Glezman on 01.08.2020.
6+
//
7+
8+
import UIKit
9+
10+
/// Animation for shake the card to the left and right
11+
struct ShakeAnimation {
12+
13+
let view: UIView
14+
let width: CGFloat
15+
let completion: () -> Void
16+
17+
var animator: UIViewPropertyAnimator {
18+
let dx = width / 8
19+
let angle = CGFloat.pi / 8 * dx / width
20+
21+
let p1 = UIViewPropertyAnimator(duration: 0.12, curve: .easeOut) {
22+
let translation = CGPoint(x: dx, y: 0)
23+
let rotation = CGAffineTransform(rotationAngle: angle)
24+
self.view.transform = rotation.translatedBy(x: translation.x, y: translation.y)
25+
}
26+
27+
let p2 = UIViewPropertyAnimator(duration: 0.2, curve: .easeInOut) {
28+
let translation = CGPoint(x: -dx, y: 0)
29+
let rotation = CGAffineTransform(rotationAngle: -angle)
30+
self.view.transform = rotation.translatedBy(x: translation.x, y: translation.y)
31+
}
32+
33+
let timing = UISpringTimingParameters(dampingRatio: 0.6)
34+
let p3 = UIViewPropertyAnimator(duration: 0.4, timingParameters: timing)
35+
p3.addAnimations {
36+
self.view.transform = .identity
37+
}
38+
39+
p3.addCompletion { _ in
40+
self.completion()
41+
}
42+
p2.addCompletion { _ in
43+
p3.startAnimation()
44+
}
45+
p1.addCompletion { _ in
46+
p2.startAnimation()
47+
}
48+
49+
return p1
50+
}
51+
}

Example/CardsViewController.xcodeproj/project.pbxproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@
490490
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
491491
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
492492
MODULE_NAME = ExampleApp;
493-
PRODUCT_BUNDLE_IDENTIFIER = "com.glezman.CardsViewController-Example2";
493+
PRODUCT_BUNDLE_IDENTIFIER = "com.glezman.CardsViewController-Example3";
494494
PRODUCT_NAME = "$(TARGET_NAME)";
495495
PROVISIONING_PROFILE_SPECIFIER = "";
496496
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
@@ -510,7 +510,7 @@
510510
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
511511
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
512512
MODULE_NAME = ExampleApp;
513-
PRODUCT_BUNDLE_IDENTIFIER = "com.glezman.CardsViewController-Example2";
513+
PRODUCT_BUNDLE_IDENTIFIER = "com.glezman.CardsViewController-Example3";
514514
PRODUCT_NAME = "$(TARGET_NAME)";
515515
PROVISIONING_PROFILE_SPECIFIER = "";
516516
SWIFT_SWIFT3_OBJC_INFERENCE = Default;

Example/CardsViewController/Base.lproj/Main.storyboard

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
3-
<device id="retina4_7" orientation="portrait">
4-
<adaptation id="fullscreen"/>
5-
</device>
2+
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="vXZ-lx-hvc">
3+
<device id="retina4_7" orientation="portrait" appearance="light"/>
64
<dependencies>
7-
<deployment identifier="iOS"/>
8-
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
5+
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
96
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
107
</dependencies>
118
<scenes>
@@ -20,11 +17,40 @@
2017
<view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
2118
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
2219
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
20+
<subviews>
21+
<stackView opaque="NO" contentMode="scaleToFill" spacing="16" translatesAutoresizingMaskIntoConstraints="NO" id="i34-fY-1XS">
22+
<rect key="frame" x="148.5" y="20" width="78" height="30"/>
23+
<subviews>
24+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="f7D-BY-YhD">
25+
<rect key="frame" x="0.0" y="0.0" width="30" height="30"/>
26+
<state key="normal" title="left"/>
27+
<connections>
28+
<action selector="performLeftAnimation" destination="vXZ-lx-hvc" eventType="touchUpInside" id="iS9-cu-0OI"/>
29+
</connections>
30+
</button>
31+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Ril-CM-xny">
32+
<rect key="frame" x="46" y="0.0" width="32" height="30"/>
33+
<state key="normal" title="right"/>
34+
<connections>
35+
<action selector="performRightAnimation" destination="vXZ-lx-hvc" eventType="touchUpInside" id="Al8-n6-05o"/>
36+
</connections>
37+
</button>
38+
</subviews>
39+
</stackView>
40+
</subviews>
2341
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
42+
<constraints>
43+
<constraint firstItem="i34-fY-1XS" firstAttribute="centerX" secondItem="kh9-bI-dsS" secondAttribute="centerX" id="heS-kE-gf2"/>
44+
<constraint firstItem="i34-fY-1XS" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" constant="20" id="qAT-79-JH1"/>
45+
</constraints>
2446
</view>
47+
<connections>
48+
<outlet property="stackView" destination="i34-fY-1XS" id="Sx3-GM-jVv"/>
49+
</connections>
2550
</viewController>
2651
<placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
2752
</objects>
53+
<point key="canvasLocation" x="138" y="134"/>
2854
</scene>
2955
</scenes>
3056
</document>

Example/CardsViewController/ViewController.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import CardsViewController
1111

1212
class ViewController: UIViewController {
1313

14+
@IBOutlet private weak var stackView: UIStackView!
1415
let cardsController = CardsViewController()
1516

1617
let colors: [UIColor] = [
@@ -43,6 +44,16 @@ class ViewController: UIViewController {
4344
cardsController.dataSource = self
4445
cardsController.delegate = self
4546
cardsController.reloadCards()
47+
48+
view.bringSubviewToFront(stackView)
49+
}
50+
51+
@IBAction private func performLeftAnimation() {
52+
cardsController.performCardSwipeAnimation(direction: .left)
53+
}
54+
55+
@IBAction private func performRightAnimation() {
56+
cardsController.performCardSwipeAnimation(direction: .right)
4657
}
4758
}
4859

0 commit comments

Comments
 (0)