├── DMSwipeCards ├── Assets │ └── .gitkeep └── Classes │ ├── .gitkeep │ ├── DMSwipeCardsView.swift │ └── DMSwipeCard.swift ├── _Pods.xcodeproj ├── Screenshots ├── 01.png ├── 02.png └── 03.png ├── screenshot_1482145004.png ├── Example ├── Podfile ├── DMSwipeCards.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── DMSwipeCards-Example.xcscheme │ └── project.pbxproj ├── DMSwipeCards.xcworkspace │ └── contents.xcworkspacedata ├── DMSwipeCards │ ├── AppDelegate.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.xib │ └── ViewController.swift └── Tests │ ├── Info.plist │ └── Tests.swift ├── DMSwipeCards.podspec ├── LICENSE ├── .gitignore └── README.md /DMSwipeCards/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /DMSwipeCards/Classes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /Screenshots/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-32/DMSwipeCards/HEAD/Screenshots/01.png -------------------------------------------------------------------------------- /Screenshots/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-32/DMSwipeCards/HEAD/Screenshots/02.png -------------------------------------------------------------------------------- /Screenshots/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-32/DMSwipeCards/HEAD/Screenshots/03.png -------------------------------------------------------------------------------- /screenshot_1482145004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-32/DMSwipeCards/HEAD/screenshot_1482145004.png -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'DMSwipeCards_Example' do 4 | pod 'DMSwipeCards', :path => '../' 5 | 6 | target 'DMSwipeCards_Tests' do 7 | inherit! :search_paths 8 | 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/DMSwipeCards.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/DMSwipeCards.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DMSwipeCards 4 | // 5 | // Created by Dylan Marriott on 12/18/2016. 6 | // Copyright (c) 2016 Dylan Marriott. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | return true 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/Tests.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import XCTest 3 | import DMSwipeCards 4 | 5 | class Tests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | XCTAssert(true, "Pass") 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure() { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /DMSwipeCards.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'DMSwipeCards' 3 | s.version = '1.0.3' 4 | s.summary = 'Tinder like card interface' 5 | 6 | s.description = <<-DESC 7 | Written in Swift 3, supports custom views for the card and the overlay. Views get loaded lazily, so there's no issues in loading a huge amount of cards. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/d-32/DMSwipeCards' 11 | s.screenshots = 'https://raw.githubusercontent.com/D-32/DMSwipeCards/master/Screenshots/01.png', 'https://raw.githubusercontent.com/D-32/DMSwipeCards/master/Screenshots/02.png', 'https://raw.githubusercontent.com/D-32/DMSwipeCards/master/Screenshots/03.png' 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | s.author = { 'Dylan Marriott' => 'info@d-32.com' } 14 | s.source = { :git => 'https://github.com/d-32/DMSwipeCards.git', :tag => s.version.to_s } 15 | s.social_media_url = 'https://twitter.com/dylan36032' 16 | 17 | s.ios.deployment_target = '8.0' 18 | 19 | s.source_files = 'DMSwipeCards/Classes/**/*' 20 | end 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Dylan Marriott 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | Pods/ 47 | Podfile.lock 48 | 49 | # Carthage 50 | # 51 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 52 | # Carthage/Checkouts 53 | 54 | Carthage/Build 55 | 56 | # fastlane 57 | # 58 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 59 | # screenshots whenever they are needed. 60 | # For more information about the recommended setup visit: 61 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 62 | 63 | fastlane/report.xml 64 | fastlane/Preview.html 65 | fastlane/screenshots 66 | fastlane/test_output 67 | 68 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Version](https://img.shields.io/cocoapods/v/DMSwipeCards.svg?style=flat)](http://cocoadocs.org/docsets/DMSwipeCards) 2 | ![License](https://img.shields.io/cocoapods/l/DMSwipeCards.svg?style=flat) 3 | [![twitter: @dylan36032](http://img.shields.io/badge/twitter-%40dylan36032-blue.svg?style=flat)](https://twitter.com/dylan36032) 4 | 5 | ![image](screenshot_1482145004.png) 6 | 7 | ## Features 8 | 9 | - Swift 3 10 | - Custom views for the card & overlay 11 | - Generic 12 | - Dynamically add new cards on top or on the bottom 13 | - Lazy view loading 14 | 15 | ## Setup 16 | ```ruby 17 | pod 'DMSwipeCards' 18 | ``` 19 | 20 | ## Usage 21 | 22 | First import the module: 23 | ```swift 24 | import DMSwipeCards 25 | ``` 26 | 27 | Next create an instance of a `DMSwipeCardsView`: 28 | (`Element` can be your custom model, or just `String`) 29 | ```swift 30 | let swipeView = DMSwipeCardsView(frame: frame, 31 | viewGenerator: viewGenerator, 32 | overlayGenerator: overlayGenerator) 33 | ``` 34 | Views get loaded lazy, so you have to provide `DMSwipeCardsView` with a ViewGenerator and optionally an OverlayGenerator. 35 | ```swift 36 | let viewGenerator: (String, CGRect) -> (UIView) = { (element: Element, frame: CGRect) -> (UIView) in 37 | // return a UIView here 38 | } 39 | 40 | let overlayGenerator: (SwipeMode, CGRect) -> (UIView) = { (mode: SwipeMode, frame: CGRect) -> (UIView) in 41 | // return a UIView here 42 | } 43 | ``` 44 | ### Adding cards 45 | 46 | To add new cards, just call the `addCards` method with an array of the previously defined `Element`: 47 | ```swift 48 | swipeView.addCards([Element], onTop: true) 49 | ``` 50 | ### Delegate 51 | 52 | `DMSwipeCardsView` has a delegate property so you can get informed when a card has been swipped. The delegate has to implement following methods: 53 | ```swift 54 | func swipedLeft(_ object: Any) 55 | func swipedRight(_ object: Any) 56 | func cardTapped(_ object: Any) 57 | func reachedEndOfStack() 58 | ``` 59 | The `object` parameter is guarenteed to have the type `Element`. Sadly generics don't work here. 60 | 61 | 62 | ## Example 63 | 64 | For a nice working demo sample, please take a look the [Example](https://github.com/D-32/DMSwipeCards/tree/master/Example) project. 65 | To run the example, first run `pod install` in the `Example` directory. 66 | 67 | ## Credits 68 | Loosly based on [TinderSimpleSwipeCards](https://github.com/cwRichardKim/TinderSimpleSwipeCards) 69 | 70 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/DMSwipeCards/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DMSwipeCards 4 | // 5 | // Created by Dylan Marriott on 12/18/2016. 6 | // Copyright (c) 2016 Dylan Marriott. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import DMSwipeCards 11 | 12 | class ViewController: UIViewController { 13 | 14 | private var swipeView: DMSwipeCardsView! 15 | private var count = 0 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | self.view.backgroundColor = UIColor(white: 0.95, alpha: 1.0) 21 | 22 | /* 23 | * In this example we're using `String` as a type. 24 | * You can use DMSwipeCardsView though with any custom class. 25 | */ 26 | 27 | let viewGenerator: (String, CGRect) -> (UIView) = { (element: String, frame: CGRect) -> (UIView) in 28 | let container = UIView(frame: CGRect(x: 30, y: 20, width: frame.width - 60, height: frame.height - 40)) 29 | let label = UILabel(frame: container.bounds) 30 | label.text = element 31 | label.textAlignment = .center 32 | label.backgroundColor = UIColor.white 33 | label.font = UIFont.systemFont(ofSize: 48, weight: UIFont.Weight.thin) 34 | label.clipsToBounds = true 35 | label.layer.cornerRadius = 16 36 | container.addSubview(label) 37 | 38 | container.layer.shadowRadius = 4 39 | container.layer.shadowOpacity = 1.0 40 | container.layer.shadowColor = UIColor(white: 0.9, alpha: 1.0).cgColor 41 | container.layer.shadowOffset = CGSize(width: 0, height: 0) 42 | container.layer.shouldRasterize = true 43 | container.layer.rasterizationScale = UIScreen.main.scale 44 | 45 | return container 46 | } 47 | 48 | let overlayGenerator: (SwipeMode, CGRect) -> (UIView) = { (mode: SwipeMode, frame: CGRect) -> (UIView) in 49 | let label = UILabel() 50 | label.frame.size = CGSize(width: 100, height: 100) 51 | label.center = CGPoint(x: frame.width / 2, y: frame.height / 2) 52 | label.layer.cornerRadius = label.frame.width / 2 53 | label.backgroundColor = mode == .left ? UIColor.red : UIColor.green 54 | label.clipsToBounds = true 55 | label.text = mode == .left ? "👍" : "👎" 56 | label.font = UIFont.systemFont(ofSize: 24) 57 | label.textAlignment = .center 58 | return label 59 | } 60 | 61 | let frame = CGRect(x: 0, y: 80, width: self.view.frame.width, height: self.view.frame.height - 160) 62 | swipeView = DMSwipeCardsView(frame: frame, 63 | viewGenerator: viewGenerator, 64 | overlayGenerator: overlayGenerator) 65 | swipeView.delegate = self 66 | self.view.addSubview(swipeView) 67 | 68 | 69 | 70 | let button = UIButton(frame: CGRect(x: 0, y: 40, width: self.view.frame.width, height: 40)) 71 | button.setTitle("Load cards", for: .normal) 72 | button.setTitleColor(UIColor.blue, for: .normal) 73 | button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) 74 | self.view.addSubview(button) 75 | } 76 | 77 | @objc func buttonTapped() { 78 | let ac = UIAlertController(title: "Load on top / on bottom?", message: nil, preferredStyle: .actionSheet) 79 | ac.addAction(UIAlertAction(title: "On Top", style: .default, handler: { (a: UIAlertAction) in 80 | self.swipeView.addCards((self.count...(self.count+3)).map({"\($0)"}), onTop: true) 81 | self.count = self.count + 4 82 | })) 83 | ac.addAction(UIAlertAction(title: "On Bottom", style: .default, handler: { (a: UIAlertAction) in 84 | self.swipeView.addCards((self.count...(self.count+3)).map({"\($0)"}), onTop: false) 85 | self.count = self.count + 4 86 | })) 87 | self.present(ac, animated: true, completion: nil) 88 | } 89 | } 90 | 91 | extension ViewController: DMSwipeCardsViewDelegate { 92 | func swipedLeft(_ object: Any) { 93 | print("Swiped left: \(object)") 94 | } 95 | 96 | func swipedRight(_ object: Any) { 97 | print("Swiped right: \(object)") 98 | } 99 | 100 | func cardTapped(_ object: Any) { 101 | print("Tapped on: \(object)") 102 | } 103 | 104 | func reachedEndOfStack() { 105 | print("Reached end of stack") 106 | } 107 | } 108 | 109 | -------------------------------------------------------------------------------- /DMSwipeCards/Classes/DMSwipeCardsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DMSwipeCardsView.swift 3 | // Pods 4 | // 5 | // Created by Dylan Marriott on 18/12/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | public enum SwipeMode { 13 | case left 14 | case right 15 | } 16 | 17 | public protocol DMSwipeCardsViewDelegate: class { 18 | func swipedLeft(_ object: Any) 19 | func swipedRight(_ object: Any) 20 | func cardTapped(_ object: Any) 21 | func reachedEndOfStack() 22 | } 23 | 24 | public class DMSwipeCardsView: UIView { 25 | 26 | public weak var delegate: DMSwipeCardsViewDelegate? 27 | public var bufferSize: Int = 2 28 | 29 | fileprivate let viewGenerator: ViewGenerator 30 | fileprivate let overlayGenerator: OverlayGenerator? 31 | fileprivate var allCards = [Element]() 32 | fileprivate var loadedCards = [DMSwipeCard]() 33 | 34 | public typealias ViewGenerator = (_ element: Element, _ frame: CGRect) -> (UIView) 35 | public typealias OverlayGenerator = (_ mode: SwipeMode, _ frame: CGRect) -> (UIView) 36 | public init(frame: CGRect, 37 | viewGenerator: @escaping ViewGenerator, 38 | overlayGenerator: OverlayGenerator? = nil) { 39 | self.overlayGenerator = overlayGenerator 40 | self.viewGenerator = viewGenerator 41 | super.init(frame: frame) 42 | self.isUserInteractionEnabled = false 43 | } 44 | 45 | override private init(frame: CGRect) { 46 | fatalError("Please use init(frame:,viewGenerator)") 47 | } 48 | 49 | required public init?(coder aDecoder: NSCoder) { 50 | fatalError("Please use init(frame:,viewGenerator)") 51 | } 52 | 53 | public func addCards(_ elements: [Element], onTop: Bool = false) { 54 | if elements.isEmpty { 55 | return 56 | } 57 | 58 | self.isUserInteractionEnabled = true 59 | 60 | if onTop { 61 | for element in elements.reversed() { 62 | allCards.insert(element, at: 0) 63 | } 64 | } else { 65 | for element in elements { 66 | allCards.append(element) 67 | } 68 | } 69 | 70 | if onTop && loadedCards.count > 0 { 71 | for cv in loadedCards { 72 | cv.removeFromSuperview() 73 | } 74 | loadedCards.removeAll() 75 | } 76 | 77 | for element in elements { 78 | if loadedCards.count < bufferSize { 79 | let cardView = self.createCardView(element: element) 80 | if loadedCards.isEmpty { 81 | self.addSubview(cardView) 82 | } else { 83 | self.insertSubview(cardView, belowSubview: loadedCards.last!) 84 | } 85 | self.loadedCards.append(cardView) 86 | } 87 | } 88 | } 89 | 90 | func swipeTopCardRight() { 91 | // TODO: not yet supported 92 | fatalError("Not yet supported") 93 | } 94 | 95 | func swipeTopCardLeft() { 96 | // TODO: not yet supported 97 | fatalError("Not yet supported") 98 | } 99 | } 100 | 101 | extension DMSwipeCardsView: DMSwipeCardDelegate { 102 | func cardSwipedLeft(_ card: DMSwipeCard) { 103 | self.handleSwipedCard(card) 104 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { 105 | self.delegate?.swipedLeft(card.obj) 106 | self.loadNextCard() 107 | } 108 | } 109 | 110 | func cardSwipedRight(_ card: DMSwipeCard) { 111 | self.handleSwipedCard(card) 112 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.001) { 113 | self.delegate?.swipedRight(card.obj) 114 | self.loadNextCard() 115 | } 116 | } 117 | 118 | func cardTapped(_ card: DMSwipeCard) { 119 | self.delegate?.cardTapped(card.obj) 120 | } 121 | } 122 | 123 | extension DMSwipeCardsView { 124 | fileprivate func handleSwipedCard(_ card: DMSwipeCard) { 125 | self.loadedCards.removeFirst() 126 | self.allCards.removeFirst() 127 | if self.allCards.isEmpty { 128 | self.isUserInteractionEnabled = false 129 | self.delegate?.reachedEndOfStack() 130 | } 131 | } 132 | 133 | fileprivate func loadNextCard() { 134 | if self.allCards.count - self.loadedCards.count > 0 { 135 | let next = self.allCards[loadedCards.count] 136 | let nextView = self.createCardView(element: next) 137 | let below = self.loadedCards.last! 138 | self.loadedCards.append(nextView) 139 | self.insertSubview(nextView, belowSubview: below) 140 | } 141 | } 142 | 143 | fileprivate func createCardView(element: Element) -> DMSwipeCard { 144 | let cardView = DMSwipeCard(frame: self.bounds) 145 | cardView.delegate = self 146 | cardView.obj = element 147 | let sv = self.viewGenerator(element, cardView.bounds) 148 | cardView.addSubview(sv) 149 | cardView.leftOverlay = self.overlayGenerator?(.left, cardView.bounds) 150 | cardView.rightOverlay = self.overlayGenerator?(.right, cardView.bounds) 151 | cardView.configureOverlays() 152 | return cardView 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /DMSwipeCards/Classes/DMSwipeCard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DMSwipeCard.swift 3 | // Pods 4 | // 5 | // Created by Dylan Marriott on 18/12/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | protocol DMSwipeCardDelegate: class { 13 | func cardSwipedLeft(_ card: DMSwipeCard) 14 | func cardSwipedRight(_ card: DMSwipeCard) 15 | func cardTapped(_ card: DMSwipeCard) 16 | } 17 | 18 | class DMSwipeCard: UIView { 19 | 20 | weak var delegate: DMSwipeCardDelegate? 21 | var obj: Any! 22 | var leftOverlay: UIView? 23 | var rightOverlay: UIView? 24 | 25 | private let actionMargin: CGFloat = 120.0 26 | private let rotationStrength: CGFloat = 320.0 27 | private let rotationAngle: CGFloat = CGFloat(Double.pi) / CGFloat(8.0) 28 | private let rotationMax: CGFloat = 1 29 | private let scaleStrength: CGFloat = -2 30 | private let scaleMax: CGFloat = 1.02 31 | 32 | private var xFromCenter: CGFloat = 0.0 33 | private var yFromCenter: CGFloat = 0.0 34 | private var originalPoint = CGPoint.zero 35 | 36 | override init(frame: CGRect) { 37 | super.init(frame: frame) 38 | 39 | let panGesture = UIPanGestureRecognizer(target: self, action: #selector(dragEvent(gesture:))) 40 | panGesture.delegate = self 41 | self.addGestureRecognizer(panGesture) 42 | 43 | let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapEvent(gesture:))) 44 | tapGesture.delegate = self 45 | self.addGestureRecognizer(tapGesture) 46 | } 47 | 48 | required init?(coder aDecoder: NSCoder) { 49 | fatalError("init(coder:) has not been implemented") 50 | } 51 | 52 | func configureOverlays() { 53 | self.configureOverlay(overlay: self.leftOverlay) 54 | self.configureOverlay(overlay: self.rightOverlay) 55 | } 56 | 57 | private func configureOverlay(overlay: UIView?) { 58 | if let o = overlay { 59 | self.addSubview(o) 60 | o.alpha = 0.0 61 | } 62 | } 63 | 64 | @objc func dragEvent(gesture: UIPanGestureRecognizer) { 65 | xFromCenter = gesture.translation(in: self).x 66 | yFromCenter = gesture.translation(in: self).y 67 | 68 | switch gesture.state { 69 | case .began: 70 | self.originalPoint = self.center 71 | break 72 | case .changed: 73 | let rStrength = min(xFromCenter / self.rotationStrength, rotationMax) 74 | let rAngle = self.rotationAngle * rStrength 75 | let scale = min(1 - fabs(rStrength) / self.scaleStrength, self.scaleMax) 76 | self.center = CGPoint(x: self.originalPoint.x + xFromCenter, y: self.originalPoint.y + yFromCenter) 77 | let transform = CGAffineTransform(rotationAngle: rAngle) 78 | let scaleTransform = transform.scaledBy(x: scale, y: scale) 79 | self.transform = scaleTransform 80 | self.updateOverlay(xFromCenter) 81 | break 82 | case .ended: 83 | self.afterSwipeAction() 84 | break 85 | default: 86 | break 87 | } 88 | } 89 | 90 | @objc func tapEvent(gesture: UITapGestureRecognizer) { 91 | self.delegate?.cardTapped(self) 92 | } 93 | 94 | private func afterSwipeAction() { 95 | if xFromCenter > actionMargin { 96 | self.rightAction() 97 | } else if xFromCenter < -actionMargin { 98 | self.leftAction() 99 | } else { 100 | UIView.animate(withDuration: 0.3) { 101 | self.center = self.originalPoint 102 | self.transform = CGAffineTransform.identity 103 | self.leftOverlay?.alpha = 0.0 104 | self.rightOverlay?.alpha = 0.0 105 | } 106 | } 107 | } 108 | 109 | private func updateOverlay(_ distance: CGFloat) { 110 | var activeOverlay: UIView? 111 | if (distance > 0) { 112 | self.leftOverlay?.alpha = 0.0 113 | activeOverlay = self.rightOverlay 114 | } else { 115 | self.rightOverlay?.alpha = 0.0 116 | activeOverlay = self.leftOverlay 117 | } 118 | 119 | activeOverlay?.alpha = min(fabs(distance)/100, 1.0) 120 | } 121 | 122 | private func rightAction() { 123 | let finishPoint = CGPoint(x: 500, y: 2 * yFromCenter + self.originalPoint.y) 124 | UIView.animate(withDuration: 0.3, animations: { 125 | self.center = finishPoint 126 | }) { _ in 127 | self.removeFromSuperview() 128 | } 129 | self.delegate?.cardSwipedRight(self) 130 | } 131 | 132 | private func leftAction() { 133 | let finishPoint = CGPoint(x: -500, y: 2 * yFromCenter + self.originalPoint.y) 134 | UIView.animate(withDuration: 0.3, animations: { 135 | self.center = finishPoint 136 | }) { _ in 137 | self.removeFromSuperview() 138 | } 139 | self.delegate?.cardSwipedLeft(self) 140 | } 141 | } 142 | 143 | extension DMSwipeCard: UIGestureRecognizerDelegate { 144 | func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { 145 | return true 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Example/DMSwipeCards.xcodeproj/xcshareddata/xcschemes/DMSwipeCards-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 80 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /Example/DMSwipeCards.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 11 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 12 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 13 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 14 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 15 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 16 | B9551E26ADF407AE3BB533E3 /* Pods_DMSwipeCards_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6141AE14A14A9F5D1AC3FA4 /* Pods_DMSwipeCards_Example.framework */; }; 17 | F05AED7EF5EAE4550F9249E3 /* Pods_DMSwipeCards_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C879166BF64B71F80EAE783B /* Pods_DMSwipeCards_Tests.framework */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 24 | proxyType = 1; 25 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 26 | remoteInfo = DMSwipeCards; 27 | }; 28 | /* End PBXContainerItemProxy section */ 29 | 30 | /* Begin PBXFileReference section */ 31 | 607FACD01AFB9204008FA782 /* DMSwipeCards_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DMSwipeCards_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 34 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 35 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 36 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 37 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 38 | 607FACE51AFB9204008FA782 /* DMSwipeCards_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DMSwipeCards_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 41 | 6B1B7896F726F29C923F295F /* Pods-DMSwipeCards_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DMSwipeCards_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-DMSwipeCards_Tests/Pods-DMSwipeCards_Tests.release.xcconfig"; sourceTree = ""; }; 42 | 79D49D4CA52D2D5FAE0A4FB4 /* Pods-DMSwipeCards_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DMSwipeCards_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-DMSwipeCards_Example/Pods-DMSwipeCards_Example.release.xcconfig"; sourceTree = ""; }; 43 | 7E72897F699D50996F044DB5 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 44 | 8D1421C8DEBC15509002FEE1 /* Pods-DMSwipeCards_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DMSwipeCards_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DMSwipeCards_Example/Pods-DMSwipeCards_Example.debug.xcconfig"; sourceTree = ""; }; 45 | A2EE6D2C56338D5D1DFA615C /* DMSwipeCards.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = DMSwipeCards.podspec; path = ../DMSwipeCards.podspec; sourceTree = ""; }; 46 | A6141AE14A14A9F5D1AC3FA4 /* Pods_DMSwipeCards_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DMSwipeCards_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | C4D2A18B3C40A0ECA3C6D57A /* Pods-DMSwipeCards_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-DMSwipeCards_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-DMSwipeCards_Tests/Pods-DMSwipeCards_Tests.debug.xcconfig"; sourceTree = ""; }; 48 | C879166BF64B71F80EAE783B /* Pods_DMSwipeCards_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_DMSwipeCards_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 49 | E249F1E20D62FBD95928AFD5 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | B9551E26ADF407AE3BB533E3 /* Pods_DMSwipeCards_Example.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | F05AED7EF5EAE4550F9249E3 /* Pods_DMSwipeCards_Tests.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | /* End PBXFrameworksBuildPhase section */ 70 | 71 | /* Begin PBXGroup section */ 72 | 32BA4B9EE20D09F413AC040A /* Frameworks */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | A6141AE14A14A9F5D1AC3FA4 /* Pods_DMSwipeCards_Example.framework */, 76 | C879166BF64B71F80EAE783B /* Pods_DMSwipeCards_Tests.framework */, 77 | ); 78 | name = Frameworks; 79 | sourceTree = ""; 80 | }; 81 | 607FACC71AFB9204008FA782 = { 82 | isa = PBXGroup; 83 | children = ( 84 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 85 | 607FACD21AFB9204008FA782 /* Example for DMSwipeCards */, 86 | 607FACE81AFB9204008FA782 /* Tests */, 87 | 607FACD11AFB9204008FA782 /* Products */, 88 | EA37DEB6265882EFAA40D84C /* Pods */, 89 | 32BA4B9EE20D09F413AC040A /* Frameworks */, 90 | ); 91 | sourceTree = ""; 92 | }; 93 | 607FACD11AFB9204008FA782 /* Products */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 607FACD01AFB9204008FA782 /* DMSwipeCards_Example.app */, 97 | 607FACE51AFB9204008FA782 /* DMSwipeCards_Tests.xctest */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 607FACD21AFB9204008FA782 /* Example for DMSwipeCards */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 106 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 107 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 108 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 109 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 110 | 607FACD31AFB9204008FA782 /* Supporting Files */, 111 | ); 112 | name = "Example for DMSwipeCards"; 113 | path = DMSwipeCards; 114 | sourceTree = ""; 115 | }; 116 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 117 | isa = PBXGroup; 118 | children = ( 119 | 607FACD41AFB9204008FA782 /* Info.plist */, 120 | ); 121 | name = "Supporting Files"; 122 | sourceTree = ""; 123 | }; 124 | 607FACE81AFB9204008FA782 /* Tests */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | 607FACEB1AFB9204008FA782 /* Tests.swift */, 128 | 607FACE91AFB9204008FA782 /* Supporting Files */, 129 | ); 130 | path = Tests; 131 | sourceTree = ""; 132 | }; 133 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 607FACEA1AFB9204008FA782 /* Info.plist */, 137 | ); 138 | name = "Supporting Files"; 139 | sourceTree = ""; 140 | }; 141 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | A2EE6D2C56338D5D1DFA615C /* DMSwipeCards.podspec */, 145 | E249F1E20D62FBD95928AFD5 /* README.md */, 146 | 7E72897F699D50996F044DB5 /* LICENSE */, 147 | ); 148 | name = "Podspec Metadata"; 149 | sourceTree = ""; 150 | }; 151 | EA37DEB6265882EFAA40D84C /* Pods */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | 8D1421C8DEBC15509002FEE1 /* Pods-DMSwipeCards_Example.debug.xcconfig */, 155 | 79D49D4CA52D2D5FAE0A4FB4 /* Pods-DMSwipeCards_Example.release.xcconfig */, 156 | C4D2A18B3C40A0ECA3C6D57A /* Pods-DMSwipeCards_Tests.debug.xcconfig */, 157 | 6B1B7896F726F29C923F295F /* Pods-DMSwipeCards_Tests.release.xcconfig */, 158 | ); 159 | name = Pods; 160 | sourceTree = ""; 161 | }; 162 | /* End PBXGroup section */ 163 | 164 | /* Begin PBXNativeTarget section */ 165 | 607FACCF1AFB9204008FA782 /* DMSwipeCards_Example */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DMSwipeCards_Example" */; 168 | buildPhases = ( 169 | 5A24F9515C27B91824A9A6C6 /* [CP] Check Pods Manifest.lock */, 170 | 607FACCC1AFB9204008FA782 /* Sources */, 171 | 607FACCD1AFB9204008FA782 /* Frameworks */, 172 | 607FACCE1AFB9204008FA782 /* Resources */, 173 | 679E24DDE95367712F71CFC5 /* [CP] Embed Pods Frameworks */, 174 | FDE2D9D4B061700E7C3A8627 /* [CP] Copy Pods Resources */, 175 | ); 176 | buildRules = ( 177 | ); 178 | dependencies = ( 179 | ); 180 | name = DMSwipeCards_Example; 181 | productName = DMSwipeCards; 182 | productReference = 607FACD01AFB9204008FA782 /* DMSwipeCards_Example.app */; 183 | productType = "com.apple.product-type.application"; 184 | }; 185 | 607FACE41AFB9204008FA782 /* DMSwipeCards_Tests */ = { 186 | isa = PBXNativeTarget; 187 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DMSwipeCards_Tests" */; 188 | buildPhases = ( 189 | 9D48BBB0E8673685D521D64B /* [CP] Check Pods Manifest.lock */, 190 | 607FACE11AFB9204008FA782 /* Sources */, 191 | 607FACE21AFB9204008FA782 /* Frameworks */, 192 | 607FACE31AFB9204008FA782 /* Resources */, 193 | CE573A2FF0A2EAE8295B207A /* [CP] Embed Pods Frameworks */, 194 | BC2B06B9A501516B6782B00E /* [CP] Copy Pods Resources */, 195 | ); 196 | buildRules = ( 197 | ); 198 | dependencies = ( 199 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 200 | ); 201 | name = DMSwipeCards_Tests; 202 | productName = Tests; 203 | productReference = 607FACE51AFB9204008FA782 /* DMSwipeCards_Tests.xctest */; 204 | productType = "com.apple.product-type.bundle.unit-test"; 205 | }; 206 | /* End PBXNativeTarget section */ 207 | 208 | /* Begin PBXProject section */ 209 | 607FACC81AFB9204008FA782 /* Project object */ = { 210 | isa = PBXProject; 211 | attributes = { 212 | LastSwiftUpdateCheck = 0720; 213 | LastUpgradeCheck = 0900; 214 | ORGANIZATIONNAME = CocoaPods; 215 | TargetAttributes = { 216 | 607FACCF1AFB9204008FA782 = { 217 | CreatedOnToolsVersion = 6.3.1; 218 | DevelopmentTeam = U69N6KJ99T; 219 | LastSwiftMigration = 0900; 220 | }; 221 | 607FACE41AFB9204008FA782 = { 222 | CreatedOnToolsVersion = 6.3.1; 223 | DevelopmentTeam = U69N6KJ99T; 224 | LastSwiftMigration = 0900; 225 | TestTargetID = 607FACCF1AFB9204008FA782; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DMSwipeCards" */; 230 | compatibilityVersion = "Xcode 3.2"; 231 | developmentRegion = English; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | Base, 236 | ); 237 | mainGroup = 607FACC71AFB9204008FA782; 238 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 239 | projectDirPath = ""; 240 | projectRoot = ""; 241 | targets = ( 242 | 607FACCF1AFB9204008FA782 /* DMSwipeCards_Example */, 243 | 607FACE41AFB9204008FA782 /* DMSwipeCards_Tests */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 607FACCE1AFB9204008FA782 /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 254 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 255 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | 607FACE31AFB9204008FA782 /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXResourcesBuildPhase section */ 267 | 268 | /* Begin PBXShellScriptBuildPhase section */ 269 | 5A24F9515C27B91824A9A6C6 /* [CP] Check Pods Manifest.lock */ = { 270 | isa = PBXShellScriptBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | ); 274 | inputPaths = ( 275 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 276 | "${PODS_ROOT}/Manifest.lock", 277 | ); 278 | name = "[CP] Check Pods Manifest.lock"; 279 | outputPaths = ( 280 | "$(DERIVED_FILE_DIR)/Pods-DMSwipeCards_Example-checkManifestLockResult.txt", 281 | ); 282 | runOnlyForDeploymentPostprocessing = 0; 283 | shellPath = /bin/sh; 284 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 285 | showEnvVarsInLog = 0; 286 | }; 287 | 679E24DDE95367712F71CFC5 /* [CP] Embed Pods Frameworks */ = { 288 | isa = PBXShellScriptBuildPhase; 289 | buildActionMask = 2147483647; 290 | files = ( 291 | ); 292 | inputPaths = ( 293 | "${SRCROOT}/Pods/Target Support Files/Pods-DMSwipeCards_Example/Pods-DMSwipeCards_Example-frameworks.sh", 294 | "${BUILT_PRODUCTS_DIR}/DMSwipeCards/DMSwipeCards.framework", 295 | ); 296 | name = "[CP] Embed Pods Frameworks"; 297 | outputPaths = ( 298 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DMSwipeCards.framework", 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | shellPath = /bin/sh; 302 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DMSwipeCards_Example/Pods-DMSwipeCards_Example-frameworks.sh\"\n"; 303 | showEnvVarsInLog = 0; 304 | }; 305 | 9D48BBB0E8673685D521D64B /* [CP] Check Pods Manifest.lock */ = { 306 | isa = PBXShellScriptBuildPhase; 307 | buildActionMask = 2147483647; 308 | files = ( 309 | ); 310 | inputPaths = ( 311 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 312 | "${PODS_ROOT}/Manifest.lock", 313 | ); 314 | name = "[CP] Check Pods Manifest.lock"; 315 | outputPaths = ( 316 | "$(DERIVED_FILE_DIR)/Pods-DMSwipeCards_Tests-checkManifestLockResult.txt", 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | shellPath = /bin/sh; 320 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 321 | showEnvVarsInLog = 0; 322 | }; 323 | BC2B06B9A501516B6782B00E /* [CP] Copy Pods Resources */ = { 324 | isa = PBXShellScriptBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | ); 328 | inputPaths = ( 329 | ); 330 | name = "[CP] Copy Pods Resources"; 331 | outputPaths = ( 332 | ); 333 | runOnlyForDeploymentPostprocessing = 0; 334 | shellPath = /bin/sh; 335 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DMSwipeCards_Tests/Pods-DMSwipeCards_Tests-resources.sh\"\n"; 336 | showEnvVarsInLog = 0; 337 | }; 338 | CE573A2FF0A2EAE8295B207A /* [CP] Embed Pods Frameworks */ = { 339 | isa = PBXShellScriptBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | ); 343 | inputPaths = ( 344 | ); 345 | name = "[CP] Embed Pods Frameworks"; 346 | outputPaths = ( 347 | ); 348 | runOnlyForDeploymentPostprocessing = 0; 349 | shellPath = /bin/sh; 350 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DMSwipeCards_Tests/Pods-DMSwipeCards_Tests-frameworks.sh\"\n"; 351 | showEnvVarsInLog = 0; 352 | }; 353 | FDE2D9D4B061700E7C3A8627 /* [CP] Copy Pods Resources */ = { 354 | isa = PBXShellScriptBuildPhase; 355 | buildActionMask = 2147483647; 356 | files = ( 357 | ); 358 | inputPaths = ( 359 | ); 360 | name = "[CP] Copy Pods Resources"; 361 | outputPaths = ( 362 | ); 363 | runOnlyForDeploymentPostprocessing = 0; 364 | shellPath = /bin/sh; 365 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-DMSwipeCards_Example/Pods-DMSwipeCards_Example-resources.sh\"\n"; 366 | showEnvVarsInLog = 0; 367 | }; 368 | /* End PBXShellScriptBuildPhase section */ 369 | 370 | /* Begin PBXSourcesBuildPhase section */ 371 | 607FACCC1AFB9204008FA782 /* Sources */ = { 372 | isa = PBXSourcesBuildPhase; 373 | buildActionMask = 2147483647; 374 | files = ( 375 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 376 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 377 | ); 378 | runOnlyForDeploymentPostprocessing = 0; 379 | }; 380 | 607FACE11AFB9204008FA782 /* Sources */ = { 381 | isa = PBXSourcesBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */, 385 | ); 386 | runOnlyForDeploymentPostprocessing = 0; 387 | }; 388 | /* End PBXSourcesBuildPhase section */ 389 | 390 | /* Begin PBXTargetDependency section */ 391 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 392 | isa = PBXTargetDependency; 393 | target = 607FACCF1AFB9204008FA782 /* DMSwipeCards_Example */; 394 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 395 | }; 396 | /* End PBXTargetDependency section */ 397 | 398 | /* Begin PBXVariantGroup section */ 399 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 400 | isa = PBXVariantGroup; 401 | children = ( 402 | 607FACDA1AFB9204008FA782 /* Base */, 403 | ); 404 | name = Main.storyboard; 405 | sourceTree = ""; 406 | }; 407 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 408 | isa = PBXVariantGroup; 409 | children = ( 410 | 607FACDF1AFB9204008FA782 /* Base */, 411 | ); 412 | name = LaunchScreen.xib; 413 | sourceTree = ""; 414 | }; 415 | /* End PBXVariantGroup section */ 416 | 417 | /* Begin XCBuildConfiguration section */ 418 | 607FACED1AFB9204008FA782 /* Debug */ = { 419 | isa = XCBuildConfiguration; 420 | buildSettings = { 421 | ALWAYS_SEARCH_USER_PATHS = NO; 422 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 423 | CLANG_CXX_LIBRARY = "libc++"; 424 | CLANG_ENABLE_MODULES = YES; 425 | CLANG_ENABLE_OBJC_ARC = YES; 426 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 427 | CLANG_WARN_BOOL_CONVERSION = YES; 428 | CLANG_WARN_COMMA = YES; 429 | CLANG_WARN_CONSTANT_CONVERSION = YES; 430 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 431 | CLANG_WARN_EMPTY_BODY = YES; 432 | CLANG_WARN_ENUM_CONVERSION = YES; 433 | CLANG_WARN_INFINITE_RECURSION = YES; 434 | CLANG_WARN_INT_CONVERSION = YES; 435 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 436 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 439 | CLANG_WARN_STRICT_PROTOTYPES = YES; 440 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 441 | CLANG_WARN_UNREACHABLE_CODE = YES; 442 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 443 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 444 | COPY_PHASE_STRIP = NO; 445 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 446 | ENABLE_STRICT_OBJC_MSGSEND = YES; 447 | ENABLE_TESTABILITY = YES; 448 | GCC_C_LANGUAGE_STANDARD = gnu99; 449 | GCC_DYNAMIC_NO_PIC = NO; 450 | GCC_NO_COMMON_BLOCKS = YES; 451 | GCC_OPTIMIZATION_LEVEL = 0; 452 | GCC_PREPROCESSOR_DEFINITIONS = ( 453 | "DEBUG=1", 454 | "$(inherited)", 455 | ); 456 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 457 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 458 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 459 | GCC_WARN_UNDECLARED_SELECTOR = YES; 460 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 461 | GCC_WARN_UNUSED_FUNCTION = YES; 462 | GCC_WARN_UNUSED_VARIABLE = YES; 463 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 464 | MTL_ENABLE_DEBUG_INFO = YES; 465 | ONLY_ACTIVE_ARCH = YES; 466 | SDKROOT = iphoneos; 467 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 468 | }; 469 | name = Debug; 470 | }; 471 | 607FACEE1AFB9204008FA782 /* Release */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | ALWAYS_SEARCH_USER_PATHS = NO; 475 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 476 | CLANG_CXX_LIBRARY = "libc++"; 477 | CLANG_ENABLE_MODULES = YES; 478 | CLANG_ENABLE_OBJC_ARC = YES; 479 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 480 | CLANG_WARN_BOOL_CONVERSION = YES; 481 | CLANG_WARN_COMMA = YES; 482 | CLANG_WARN_CONSTANT_CONVERSION = YES; 483 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 484 | CLANG_WARN_EMPTY_BODY = YES; 485 | CLANG_WARN_ENUM_CONVERSION = YES; 486 | CLANG_WARN_INFINITE_RECURSION = YES; 487 | CLANG_WARN_INT_CONVERSION = YES; 488 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 489 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 490 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 491 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 492 | CLANG_WARN_STRICT_PROTOTYPES = YES; 493 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 494 | CLANG_WARN_UNREACHABLE_CODE = YES; 495 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 496 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 497 | COPY_PHASE_STRIP = NO; 498 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 499 | ENABLE_NS_ASSERTIONS = NO; 500 | ENABLE_STRICT_OBJC_MSGSEND = YES; 501 | GCC_C_LANGUAGE_STANDARD = gnu99; 502 | GCC_NO_COMMON_BLOCKS = YES; 503 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 504 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 505 | GCC_WARN_UNDECLARED_SELECTOR = YES; 506 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 507 | GCC_WARN_UNUSED_FUNCTION = YES; 508 | GCC_WARN_UNUSED_VARIABLE = YES; 509 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 510 | MTL_ENABLE_DEBUG_INFO = NO; 511 | SDKROOT = iphoneos; 512 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 513 | VALIDATE_PRODUCT = YES; 514 | }; 515 | name = Release; 516 | }; 517 | 607FACF01AFB9204008FA782 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | baseConfigurationReference = 8D1421C8DEBC15509002FEE1 /* Pods-DMSwipeCards_Example.debug.xcconfig */; 520 | buildSettings = { 521 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 522 | DEVELOPMENT_TEAM = U69N6KJ99T; 523 | INFOPLIST_FILE = DMSwipeCards/Info.plist; 524 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 525 | MODULE_NAME = ExampleApp; 526 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 527 | PRODUCT_NAME = "$(TARGET_NAME)"; 528 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 529 | SWIFT_VERSION = 4.0; 530 | }; 531 | name = Debug; 532 | }; 533 | 607FACF11AFB9204008FA782 /* Release */ = { 534 | isa = XCBuildConfiguration; 535 | baseConfigurationReference = 79D49D4CA52D2D5FAE0A4FB4 /* Pods-DMSwipeCards_Example.release.xcconfig */; 536 | buildSettings = { 537 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 538 | DEVELOPMENT_TEAM = U69N6KJ99T; 539 | INFOPLIST_FILE = DMSwipeCards/Info.plist; 540 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 541 | MODULE_NAME = ExampleApp; 542 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 545 | SWIFT_VERSION = 4.0; 546 | }; 547 | name = Release; 548 | }; 549 | 607FACF31AFB9204008FA782 /* Debug */ = { 550 | isa = XCBuildConfiguration; 551 | baseConfigurationReference = C4D2A18B3C40A0ECA3C6D57A /* Pods-DMSwipeCards_Tests.debug.xcconfig */; 552 | buildSettings = { 553 | DEVELOPMENT_TEAM = U69N6KJ99T; 554 | FRAMEWORK_SEARCH_PATHS = ( 555 | "$(SDKROOT)/Developer/Library/Frameworks", 556 | "$(inherited)", 557 | ); 558 | GCC_PREPROCESSOR_DEFINITIONS = ( 559 | "DEBUG=1", 560 | "$(inherited)", 561 | ); 562 | INFOPLIST_FILE = Tests/Info.plist; 563 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 564 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 565 | PRODUCT_NAME = "$(TARGET_NAME)"; 566 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 567 | SWIFT_VERSION = 4.0; 568 | }; 569 | name = Debug; 570 | }; 571 | 607FACF41AFB9204008FA782 /* Release */ = { 572 | isa = XCBuildConfiguration; 573 | baseConfigurationReference = 6B1B7896F726F29C923F295F /* Pods-DMSwipeCards_Tests.release.xcconfig */; 574 | buildSettings = { 575 | DEVELOPMENT_TEAM = U69N6KJ99T; 576 | FRAMEWORK_SEARCH_PATHS = ( 577 | "$(SDKROOT)/Developer/Library/Frameworks", 578 | "$(inherited)", 579 | ); 580 | INFOPLIST_FILE = Tests/Info.plist; 581 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 582 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 583 | PRODUCT_NAME = "$(TARGET_NAME)"; 584 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 585 | SWIFT_VERSION = 4.0; 586 | }; 587 | name = Release; 588 | }; 589 | /* End XCBuildConfiguration section */ 590 | 591 | /* Begin XCConfigurationList section */ 592 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "DMSwipeCards" */ = { 593 | isa = XCConfigurationList; 594 | buildConfigurations = ( 595 | 607FACED1AFB9204008FA782 /* Debug */, 596 | 607FACEE1AFB9204008FA782 /* Release */, 597 | ); 598 | defaultConfigurationIsVisible = 0; 599 | defaultConfigurationName = Release; 600 | }; 601 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DMSwipeCards_Example" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | 607FACF01AFB9204008FA782 /* Debug */, 605 | 607FACF11AFB9204008FA782 /* Release */, 606 | ); 607 | defaultConfigurationIsVisible = 0; 608 | defaultConfigurationName = Release; 609 | }; 610 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "DMSwipeCards_Tests" */ = { 611 | isa = XCConfigurationList; 612 | buildConfigurations = ( 613 | 607FACF31AFB9204008FA782 /* Debug */, 614 | 607FACF41AFB9204008FA782 /* Release */, 615 | ); 616 | defaultConfigurationIsVisible = 0; 617 | defaultConfigurationName = Release; 618 | }; 619 | /* End XCConfigurationList section */ 620 | }; 621 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 622 | } 623 | --------------------------------------------------------------------------------