├── .gitignore ├── .travis.yml ├── AddCustomLayout.png ├── ExampleProject ├── Cartfile ├── Cartfile.resolved ├── ExampleProject │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── DeleteX.imageset │ │ │ ├── Contents.json │ │ │ ├── DeleteX.png │ │ │ └── DeleteX@2x.png │ │ ├── Disliked.imageset │ │ │ ├── Contents.json │ │ │ ├── Disliked.png │ │ │ └── Disliked@2x.png │ │ ├── Liked.imageset │ │ │ ├── Contents.json │ │ │ ├── Liked.png │ │ │ └── Liked@2x.png │ │ ├── Profile1.imageset │ │ │ ├── Contents.json │ │ │ └── Profile1.png │ │ ├── Profile2.imageset │ │ │ ├── Contents.json │ │ │ └── Profile2.png │ │ ├── Profile3.imageset │ │ │ ├── Contents.json │ │ │ └── Profile3.png │ │ ├── Profile4.imageset │ │ │ ├── Contents.json │ │ │ └── Profile4.png │ │ ├── Profile5.imageset │ │ │ ├── Contents.json │ │ │ └── Profile5.png │ │ ├── Profile6.imageset │ │ │ ├── Contents.json │ │ │ └── Profile6.png │ │ ├── Profile7.imageset │ │ │ ├── Contents.json │ │ │ └── Profile7.png │ │ ├── ProfileMe.imageset │ │ │ ├── Contents.json │ │ │ ├── ProfileMe.png │ │ │ └── ProfileMe@2x.png │ │ └── Tick.imageset │ │ │ ├── Contents.json │ │ │ ├── Tick.png │ │ │ └── Tick@2x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Card.swift │ ├── CardCollectionViewCell.swift │ ├── CardCollectionViewCell.xib │ ├── CardViewController.swift │ ├── Cards.plist │ ├── ColorExtensions.swift │ └── Info.plist └── SwipingCards.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ └── contents.xcworkspacedata ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── SwipingCarousel │ ├── Info.plist │ ├── Swiping Carousel.h │ ├── SwipingCarouselCellManager.swift │ ├── SwipingCarouselCollectionViewCell.swift │ └── SwipingCarouselFlowLayout.swift ├── Swiping-Carousel-Demo.gif ├── SwipingCarousel.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ └── Swiping Carousel.xcscheme └── Tests └── SwipingCarouselTests ├── Info.plist └── SwipingCarouselTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | .DS_Store 5 | /.build 6 | 7 | ## Other 8 | *.xcuserstate 9 | xcuserdata/ 10 | 11 | # Swift Package Manager 12 | # 13 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 14 | # Packages/ 15 | /*.xcodeproj 16 | .swiftpm 17 | 18 | # Carthage 19 | # 20 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 21 | # Carthage/Checkouts 22 | 23 | ExampleProject/Carthage/ 24 | Carthage/ 25 | /*.framework.zip 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode11.2 3 | script: 4 | - xcodebuild clean test -project "SwipingCarousel.xcodeproj" -scheme "Swiping Carousel" -destination "platform=iOS Simulator,name=iPhone 11,OS=13.2.2" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ONLY_ACTIVE_ARCH=NO -quiet 5 | -------------------------------------------------------------------------------- /AddCustomLayout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/AddCustomLayout.png -------------------------------------------------------------------------------- /ExampleProject/Cartfile: -------------------------------------------------------------------------------- 1 | github "ppacie/SwipingCarousel" 2 | -------------------------------------------------------------------------------- /ExampleProject/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "ppacie/SwipingCarousel" "1.5" 2 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ExampleProject 4 | // 5 | // Created by Pablo Paciello on 10/27/16. 6 | // Copyright © 2016 PPacie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | UIApplication.shared.setStatusBarStyle(UIStatusBarStyle.lightContent, animated: true) 18 | return true 19 | } 20 | 21 | func applicationWillResignActive(_ application: UIApplication) { 22 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 23 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 24 | } 25 | 26 | func applicationDidEnterBackground(_ application: UIApplication) { 27 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 28 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 29 | } 30 | 31 | func applicationWillEnterForeground(_ application: UIApplication) { 32 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 33 | } 34 | 35 | func applicationDidBecomeActive(_ application: UIApplication) { 36 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 37 | } 38 | 39 | func applicationWillTerminate(_ application: UIApplication) { 40 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/DeleteX.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "DeleteX.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "DeleteX@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/DeleteX.imageset/DeleteX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/DeleteX.imageset/DeleteX.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/DeleteX.imageset/DeleteX@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/DeleteX.imageset/DeleteX@2x.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Disliked.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Disliked.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Disliked@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Disliked.imageset/Disliked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Disliked.imageset/Disliked.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Disliked.imageset/Disliked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Disliked.imageset/Disliked@2x.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Liked.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Liked.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Liked@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Liked.imageset/Liked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Liked.imageset/Liked.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Liked.imageset/Liked@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Liked.imageset/Liked@2x.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile1.imageset/Profile1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile1.imageset/Profile1.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile2.imageset/Profile2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile2.imageset/Profile2.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile3.imageset/Profile3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile3.imageset/Profile3.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile4.imageset/Profile4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile4.imageset/Profile4.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile5.imageset/Profile5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile5.imageset/Profile5.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile6.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile6.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile6.imageset/Profile6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile6.imageset/Profile6.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile7.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Profile7.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Profile7.imageset/Profile7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Profile7.imageset/Profile7.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/ProfileMe.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "ProfileMe.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "ProfileMe@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/ProfileMe.imageset/ProfileMe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/ProfileMe.imageset/ProfileMe.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/ProfileMe.imageset/ProfileMe@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/ProfileMe.imageset/ProfileMe@2x.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Tick.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Tick.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Tick@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Tick.imageset/Tick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Tick.imageset/Tick.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Assets.xcassets/Tick.imageset/Tick@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/ExampleProject/ExampleProject/Assets.xcassets/Tick.imageset/Tick@2x.png -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Base.lproj/LaunchScreen.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 | 27 | 28 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/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 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Card.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Card.swift 3 | // Swiping Cards 4 | // 5 | // Created by Pablo Paciello on 8/20/15. 6 | // Copyright (c) 2015 Pablo Paciello. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Card { 12 | let image: UIImage 13 | let name: String 14 | let profession: String 15 | let mainDescription: String 16 | let activity: String 17 | let backgroundColor: UIColor 18 | var likedCard: Bool 19 | 20 | init(dictionary: NSDictionary) { 21 | self.image = UIImage(named: (dictionary["Image"] as! String))! 22 | self.name = dictionary["Name"] as! String 23 | self.profession = dictionary["Profession"] as! String 24 | self.mainDescription = dictionary["Description"] as! String 25 | self.activity = dictionary["Activity"] as! String 26 | self.backgroundColor = UIColor.random 27 | self.likedCard = dictionary["Liked"] as! Bool 28 | } 29 | 30 | //Load some demo information into the [savedCards] Array. 31 | static func loadCards() -> [Card] { 32 | var savedCards = [Card]() 33 | if let URL = Bundle.main.url(forResource: "Cards", withExtension: "plist") { 34 | if let cardsFromPlist = NSArray(contentsOf: URL) { 35 | for card in cardsFromPlist{ 36 | let newCard = Card(dictionary: card as! NSDictionary) 37 | savedCards.append(newCard) 38 | } 39 | } 40 | } 41 | return savedCards 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/CardCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardCollectionViewCell.swift 3 | // ExampleProject 4 | // 5 | // Created by Pablo Paciello on 10/27/16. 6 | // Copyright © 2016 PPacie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwipingCarousel 11 | 12 | class CardCollectionViewCell: SwipingCarouselCollectionViewCell { 13 | @IBOutlet weak var profileImage: UIImageView! 14 | @IBOutlet weak var nameLabel: UILabel! 15 | @IBOutlet weak var professionLabel: UILabel! 16 | @IBOutlet weak var mainDescriptionLabel: UILabel! 17 | @IBOutlet weak var activityLabel: UILabel! 18 | @IBOutlet weak var likeImage: UIImageView! 19 | 20 | static let reuseIdentifier = "CardCollectionViewCell" 21 | static var nib: UINib { 22 | get { 23 | return UINib(nibName: "CardCollectionViewCell", bundle: nil) 24 | } 25 | } 26 | 27 | override func awakeFromNib() { 28 | super.awakeFromNib() 29 | // Cell Corner and Shadows 30 | layer.masksToBounds = false 31 | layer.cornerRadius = 10 32 | layer.shadowRadius = 5 33 | layer.shadowOpacity = 0.6 34 | // Emphasize the shadow on the bottom and right sides of the cell 35 | layer.shadowOffset = CGSize(width: 4, height: 4) 36 | } 37 | 38 | func populateWith(card: Card) { 39 | profileImage.image = card.image 40 | nameLabel.text = card.name 41 | professionLabel.text = card.profession 42 | mainDescriptionLabel.text = card.mainDescription 43 | activityLabel.text = card.activity 44 | backgroundColor = card.backgroundColor 45 | likeImage.image = card.likedCard ? UIImage(named: "Liked") : UIImage(named:"Disliked") 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/CardCollectionViewCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 32 | 33 | 34 | 35 | 36 | 37 | 43 | 51 | 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 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/CardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CardViewController.swift 3 | // ExampleProject 4 | // 5 | // Created by Pablo Paciello on 10/27/16. 6 | // Copyright © 2016 PPacie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwipingCarousel 11 | 12 | class CardViewController: UIViewController { 13 | @IBOutlet weak var collectionView: UICollectionView! { didSet { 14 | collectionView.register(CardCollectionViewCell.nib, forCellWithReuseIdentifier: CardCollectionViewCell.reuseIdentifier) 15 | } 16 | } 17 | 18 | // Load allTheCards from SavedCards Class. 19 | private var allTheCards = Card.loadCards() 20 | private let segueIdentifier = "OpenChat" 21 | } 22 | // MARK: UICollectionView DataSource 23 | extension CardViewController: UICollectionViewDataSource, UICollectionViewDelegate { 24 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 25 | //Return the number of items in the section 26 | return allTheCards.count 27 | } 28 | 29 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 30 | 31 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CardCollectionViewCell.reuseIdentifier, for: indexPath) as! CardCollectionViewCell 32 | 33 | // Configure the cell 34 | cell.populateWith(card: allTheCards[(indexPath as NSIndexPath).row]) 35 | cell.delegate = self 36 | cell.deleteOnSwipeDown = true 37 | return cell 38 | } 39 | 40 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 41 | if let cell = collectionView.cellForItem(at: indexPath) { 42 | //We check if the selected Card is the one in the middle to open the chat. If it's not, we scroll to the selected side card. 43 | if cell.frame.size.height > cell.bounds.size.height { 44 | performSegue(withIdentifier: segueIdentifier, sender: Any?.self) 45 | } else { 46 | collectionView.scrollToItem(at: indexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: true) 47 | } 48 | } 49 | } 50 | } 51 | // MARK: Conform to the SwipingCarousel Delegate 52 | extension CardViewController: SwipingCarouselDelegate { 53 | func cellSwipedUp(_ cell: UICollectionViewCell) { 54 | guard let cell = cell as? CardCollectionViewCell else { return } 55 | print("Swiped Up - Card to Like/Dislike: \(cell.nameLabel.text!)") 56 | //Get the IndexPath from Cell being passed (swiped up). 57 | if let indexPath = collectionView?.indexPath(for: cell) { 58 | //Change the Like status to Like/Dislike. 59 | allTheCards[(indexPath as NSIndexPath).row].likedCard = !allTheCards[(indexPath as NSIndexPath).row].likedCard 60 | // Update the Like Image 61 | cell.likeImage.image = allTheCards[(indexPath as NSIndexPath).row].likedCard ? UIImage(named: "Liked") : UIImage(named:"Disliked") 62 | //We are going to Scroll to the next item or to the previous one after Liking/Disliking a card. 63 | //So, we check if we ara at the end of the Array to know if we can scroll to the next item. 64 | if (indexPath as NSIndexPath).row+1 < allTheCards.count { 65 | let nextIndexPath = IndexPath(row: (indexPath as NSIndexPath).row + 1, section: 0) 66 | collectionView?.scrollToItem(at: nextIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: true) 67 | } else { //Otherwise, we scroll back to the previous one. 68 | let previousIndexPath = IndexPath(row: (indexPath as NSIndexPath).row - 1, section: 0) 69 | collectionView?.scrollToItem(at: previousIndexPath, at: UICollectionView.ScrollPosition.centeredHorizontally, animated: true) 70 | } 71 | } 72 | } 73 | 74 | func cellSwipedDown(_ cell: UICollectionViewCell) { 75 | guard let cell = cell as? CardCollectionViewCell else { return } 76 | print("Swiped Down - Card to Delete: \(cell.nameLabel.text!)") 77 | //Get the IndexPath from Cell being passed (swiped down). 78 | if let indexPath = collectionView?.indexPath(for: cell) { 79 | //Delete the swiped card from the Model. 80 | allTheCards.remove(at: (indexPath as NSIndexPath).row) 81 | //Delete the swiped card from CollectionView. 82 | collectionView?.deleteItems(at: [indexPath]) 83 | //Delete cell from View. 84 | cell.removeFromSuperview() 85 | } 86 | } 87 | } 88 | // MARK: Set the size of the cards 89 | extension CardViewController: UICollectionViewDelegateFlowLayout { 90 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 91 | return CGSize(width: 230.0, height: 300.0) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/Cards.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Name 7 | Nelson 8 | Image 9 | Profile2 10 | Profession 11 | Bogan 12 | Description 13 | Shoplifting is a victimless crime. Like punching someone in the dark. 14 | Activity 15 | 25m @Bart 16 | Liked 17 | 18 | 19 | 20 | Name 21 | Bart 22 | Image 23 | Profile1 24 | Profession 25 | Troublemaker 26 | Description 27 | Aren't we forgetting the true meaning of Christmas: the birth of Santa. I didn't do it. Ay caramba. 28 | Activity 29 | 25m @Nelson 30 | Liked 31 | 32 | 33 | 34 | Name 35 | Sideshow Bob 36 | Image 37 | Profile3 38 | Profession 39 | PhD 40 | Description 41 | I did once try to kill the world's greatest lover. But then I realized there are laws against suicide. 42 | Activity 43 | 42m @Edna 44 | Liked 45 | 46 | 47 | 48 | Name 49 | Homer J. 50 | Image 51 | Profile4 52 | Profession 53 | Beer Drinker 54 | Description 55 | Now, son, you don't want to drink beer. That's for daddies, and kids with fake IDs. 56 | Activity 57 | 15m @Moe 58 | Liked 59 | 60 | 61 | 62 | Name 63 | Steve 64 | Image 65 | Profile5 66 | Profession 67 | Apple Founder 68 | Description 69 | Your time is limited, so don't waste it living someone else's life. Stay hungry, stay foolish. 70 | Activity 71 | 2m @Tim 72 | Liked 73 | 74 | 75 | 76 | Name 77 | Krusty 78 | Image 79 | Profile6 80 | Profession 81 | Clown 82 | Description 83 | Ahh, there's nothing better than a cigarette... unless it's a cigarette lit with a hundred-dollar bill. 84 | Activity 85 | 17m @Monkey 86 | Liked 87 | 88 | 89 | 90 | Name 91 | Diego 92 | Image 93 | Profile7 94 | Profession 95 | Hand of God 96 | Description 97 | La pelota no se mancha. Se le escapó la torguga y LTA. 98 | Activity 99 | 10m @Guillote 100 | Liked 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/ColorExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ColorExtensions.swift 3 | // Swiping Cards 4 | // 5 | // Created by Pablo Paciello on 10/26/16. 6 | // Copyright © 2016 Pablo Paciello. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //Function to create a color from RGB and Computed property to get a Random UIColor based on this UIColorFromRGB method. 12 | extension UIColor { 13 | class func colorFromRGB(_ r: Int, g: Int, b: Int) -> UIColor { 14 | return UIColor(red: CGFloat(Float(r) / 255), green: CGFloat(Float(g) / 255), blue: CGFloat(Float(b) / 255), alpha: 1) 15 | } 16 | 17 | class var random: UIColor { 18 | switch arc4random() % 13 { 19 | case 0: return UIColor.colorFromRGB(85, g: 0, b: 255) 20 | case 1: return UIColor.colorFromRGB(170, g: 0, b: 170) 21 | case 2: return UIColor.colorFromRGB(85, g: 170, b: 85) 22 | case 3: return UIColor.colorFromRGB(0, g: 85, b: 0) 23 | case 4: return UIColor.colorFromRGB(255, g: 170, b: 0) 24 | case 5: return UIColor.colorFromRGB(255, g: 85, b: 0) 25 | case 6: return UIColor.colorFromRGB(0, g: 85, b: 85) 26 | case 7: return UIColor.colorFromRGB(0, g: 85, b: 255) 27 | case 8: return UIColor.colorFromRGB(170, g: 170, b: 255) 28 | case 9: return UIColor.colorFromRGB(85, g: 0, b: 0) 29 | case 10: return UIColor.colorFromRGB(170, g: 85, b: 85) 30 | case 11: return UIColor.colorFromRGB(85, g: 170, b: 255) 31 | case 12: return UIColor.colorFromRGB(0, g: 170, b: 170) 32 | default : return UIColor.black //Not going to be called 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ExampleProject/ExampleProject/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 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ExampleProject/SwipingCards.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 670501EA1DC2CA080057DC00 /* SwipingCarousel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 670501E91DC2CA080057DC00 /* SwipingCarousel.framework */; }; 11 | 672AF5B823929F540004468D /* ColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 672AF5B723929F540004468D /* ColorExtensions.swift */; }; 12 | 67B016A71DC21CEC006EF53E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016A61DC21CEC006EF53E /* AppDelegate.swift */; }; 13 | 67B016A91DC21CEC006EF53E /* CardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016A81DC21CEC006EF53E /* CardViewController.swift */; }; 14 | 67B016AC1DC21CEC006EF53E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B016AA1DC21CEC006EF53E /* Main.storyboard */; }; 15 | 67B016AE1DC21CEC006EF53E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 67B016AD1DC21CEC006EF53E /* Assets.xcassets */; }; 16 | 67B016B11DC21CEC006EF53E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 67B016AF1DC21CEC006EF53E /* LaunchScreen.storyboard */; }; 17 | 67B016CE1DC2434C006EF53E /* Card.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016CD1DC2434C006EF53E /* Card.swift */; }; 18 | 67B016D31DC243FA006EF53E /* CardCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016D11DC243FA006EF53E /* CardCollectionViewCell.swift */; }; 19 | 67B016D41DC243FA006EF53E /* CardCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 67B016D21DC243FA006EF53E /* CardCollectionViewCell.xib */; }; 20 | 67B016D81DC24419006EF53E /* Cards.plist in Resources */ = {isa = PBXBuildFile; fileRef = 67B016D71DC24419006EF53E /* Cards.plist */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 670501E91DC2CA080057DC00 /* SwipingCarousel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwipingCarousel.framework; path = Carthage/Build/iOS/SwipingCarousel.framework; sourceTree = ""; }; 25 | 672AF5B723929F540004468D /* ColorExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorExtensions.swift; sourceTree = ""; }; 26 | 67B016A31DC21CEC006EF53E /* SwipingCards.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwipingCards.app; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | 67B016A61DC21CEC006EF53E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 28 | 67B016A81DC21CEC006EF53E /* CardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardViewController.swift; sourceTree = ""; }; 29 | 67B016AB1DC21CEC006EF53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 30 | 67B016AD1DC21CEC006EF53E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 67B016B01DC21CEC006EF53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 32 | 67B016B21DC21CEC006EF53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 67B016CD1DC2434C006EF53E /* Card.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Card.swift; sourceTree = ""; }; 34 | 67B016D11DC243FA006EF53E /* CardCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CardCollectionViewCell.swift; sourceTree = ""; }; 35 | 67B016D21DC243FA006EF53E /* CardCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CardCollectionViewCell.xib; sourceTree = ""; }; 36 | 67B016D71DC24419006EF53E /* Cards.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Cards.plist; sourceTree = ""; }; 37 | /* End PBXFileReference section */ 38 | 39 | /* Begin PBXFrameworksBuildPhase section */ 40 | 67B016A01DC21CEC006EF53E /* Frameworks */ = { 41 | isa = PBXFrameworksBuildPhase; 42 | buildActionMask = 2147483647; 43 | files = ( 44 | 670501EA1DC2CA080057DC00 /* SwipingCarousel.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | 67B0169A1DC21CEC006EF53E = { 52 | isa = PBXGroup; 53 | children = ( 54 | 67B016A51DC21CEC006EF53E /* SwipingCards */, 55 | 67B016A41DC21CEC006EF53E /* Products */, 56 | 67B016C61DC2420D006EF53E /* Frameworks */, 57 | ); 58 | sourceTree = ""; 59 | }; 60 | 67B016A41DC21CEC006EF53E /* Products */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 67B016A31DC21CEC006EF53E /* SwipingCards.app */, 64 | ); 65 | name = Products; 66 | sourceTree = ""; 67 | }; 68 | 67B016A51DC21CEC006EF53E /* SwipingCards */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 67B016CA1DC24315006EF53E /* Model */, 72 | 67B016CB1DC2431E006EF53E /* Views */, 73 | 67B016CC1DC24335006EF53E /* Controllers */, 74 | 67B016CF1DC2435C006EF53E /* Support Files */, 75 | 67B016D01DC24372006EF53E /* Storyboards */, 76 | ); 77 | name = SwipingCards; 78 | path = ExampleProject; 79 | sourceTree = ""; 80 | }; 81 | 67B016C61DC2420D006EF53E /* Frameworks */ = { 82 | isa = PBXGroup; 83 | children = ( 84 | 670501E91DC2CA080057DC00 /* SwipingCarousel.framework */, 85 | ); 86 | name = Frameworks; 87 | sourceTree = ""; 88 | }; 89 | 67B016CA1DC24315006EF53E /* Model */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | 67B016CD1DC2434C006EF53E /* Card.swift */, 93 | ); 94 | name = Model; 95 | sourceTree = ""; 96 | }; 97 | 67B016CB1DC2431E006EF53E /* Views */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 67B016D11DC243FA006EF53E /* CardCollectionViewCell.swift */, 101 | 67B016D21DC243FA006EF53E /* CardCollectionViewCell.xib */, 102 | ); 103 | name = Views; 104 | sourceTree = ""; 105 | }; 106 | 67B016CC1DC24335006EF53E /* Controllers */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 67B016A81DC21CEC006EF53E /* CardViewController.swift */, 110 | ); 111 | name = Controllers; 112 | sourceTree = ""; 113 | }; 114 | 67B016CF1DC2435C006EF53E /* Support Files */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 67B016A61DC21CEC006EF53E /* AppDelegate.swift */, 118 | 672AF5B723929F540004468D /* ColorExtensions.swift */, 119 | 67B016D71DC24419006EF53E /* Cards.plist */, 120 | 67B016AD1DC21CEC006EF53E /* Assets.xcassets */, 121 | 67B016B21DC21CEC006EF53E /* Info.plist */, 122 | ); 123 | name = "Support Files"; 124 | sourceTree = ""; 125 | }; 126 | 67B016D01DC24372006EF53E /* Storyboards */ = { 127 | isa = PBXGroup; 128 | children = ( 129 | 67B016AA1DC21CEC006EF53E /* Main.storyboard */, 130 | 67B016AF1DC21CEC006EF53E /* LaunchScreen.storyboard */, 131 | ); 132 | name = Storyboards; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 67B016A21DC21CEC006EF53E /* SwipingCards */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 67B016C01DC21CEC006EF53E /* Build configuration list for PBXNativeTarget "SwipingCards" */; 141 | buildPhases = ( 142 | 67B0169F1DC21CEC006EF53E /* Sources */, 143 | 67B016A01DC21CEC006EF53E /* Frameworks */, 144 | 67B016A11DC21CEC006EF53E /* Resources */, 145 | 67B016C91DC2422E006EF53E /* ShellScript */, 146 | ); 147 | buildRules = ( 148 | ); 149 | dependencies = ( 150 | ); 151 | name = SwipingCards; 152 | productName = ExampleProject; 153 | productReference = 67B016A31DC21CEC006EF53E /* SwipingCards.app */; 154 | productType = "com.apple.product-type.application"; 155 | }; 156 | /* End PBXNativeTarget section */ 157 | 158 | /* Begin PBXProject section */ 159 | 67B0169B1DC21CEC006EF53E /* Project object */ = { 160 | isa = PBXProject; 161 | attributes = { 162 | LastSwiftUpdateCheck = 0800; 163 | LastUpgradeCheck = 1120; 164 | ORGANIZATIONNAME = PPacie; 165 | TargetAttributes = { 166 | 67B016A21DC21CEC006EF53E = { 167 | CreatedOnToolsVersion = 8.0; 168 | DevelopmentTeam = 534ZL84PZG; 169 | LastSwiftMigration = 1120; 170 | ProvisioningStyle = Automatic; 171 | }; 172 | }; 173 | }; 174 | buildConfigurationList = 67B0169E1DC21CEC006EF53E /* Build configuration list for PBXProject "SwipingCards" */; 175 | compatibilityVersion = "Xcode 3.2"; 176 | developmentRegion = en; 177 | hasScannedForEncodings = 0; 178 | knownRegions = ( 179 | en, 180 | Base, 181 | ); 182 | mainGroup = 67B0169A1DC21CEC006EF53E; 183 | productRefGroup = 67B016A41DC21CEC006EF53E /* Products */; 184 | projectDirPath = ""; 185 | projectRoot = ""; 186 | targets = ( 187 | 67B016A21DC21CEC006EF53E /* SwipingCards */, 188 | ); 189 | }; 190 | /* End PBXProject section */ 191 | 192 | /* Begin PBXResourcesBuildPhase section */ 193 | 67B016A11DC21CEC006EF53E /* Resources */ = { 194 | isa = PBXResourcesBuildPhase; 195 | buildActionMask = 2147483647; 196 | files = ( 197 | 67B016D81DC24419006EF53E /* Cards.plist in Resources */, 198 | 67B016D41DC243FA006EF53E /* CardCollectionViewCell.xib in Resources */, 199 | 67B016B11DC21CEC006EF53E /* LaunchScreen.storyboard in Resources */, 200 | 67B016AE1DC21CEC006EF53E /* Assets.xcassets in Resources */, 201 | 67B016AC1DC21CEC006EF53E /* Main.storyboard in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXResourcesBuildPhase section */ 206 | 207 | /* Begin PBXShellScriptBuildPhase section */ 208 | 67B016C91DC2422E006EF53E /* ShellScript */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | "$(SRCROOT)/Carthage/Build/iOS/SwipingCarousel.framework", 215 | ); 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "/usr/local/bin/carthage copy-frameworks"; 221 | }; 222 | /* End PBXShellScriptBuildPhase section */ 223 | 224 | /* Begin PBXSourcesBuildPhase section */ 225 | 67B0169F1DC21CEC006EF53E /* Sources */ = { 226 | isa = PBXSourcesBuildPhase; 227 | buildActionMask = 2147483647; 228 | files = ( 229 | 67B016D31DC243FA006EF53E /* CardCollectionViewCell.swift in Sources */, 230 | 672AF5B823929F540004468D /* ColorExtensions.swift in Sources */, 231 | 67B016A91DC21CEC006EF53E /* CardViewController.swift in Sources */, 232 | 67B016CE1DC2434C006EF53E /* Card.swift in Sources */, 233 | 67B016A71DC21CEC006EF53E /* AppDelegate.swift in Sources */, 234 | ); 235 | runOnlyForDeploymentPostprocessing = 0; 236 | }; 237 | /* End PBXSourcesBuildPhase section */ 238 | 239 | /* Begin PBXVariantGroup section */ 240 | 67B016AA1DC21CEC006EF53E /* Main.storyboard */ = { 241 | isa = PBXVariantGroup; 242 | children = ( 243 | 67B016AB1DC21CEC006EF53E /* Base */, 244 | ); 245 | name = Main.storyboard; 246 | sourceTree = ""; 247 | }; 248 | 67B016AF1DC21CEC006EF53E /* LaunchScreen.storyboard */ = { 249 | isa = PBXVariantGroup; 250 | children = ( 251 | 67B016B01DC21CEC006EF53E /* Base */, 252 | ); 253 | name = LaunchScreen.storyboard; 254 | sourceTree = ""; 255 | }; 256 | /* End PBXVariantGroup section */ 257 | 258 | /* Begin XCBuildConfiguration section */ 259 | 67B016BE1DC21CEC006EF53E /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | ALWAYS_SEARCH_USER_PATHS = NO; 263 | CLANG_ANALYZER_NONNULL = YES; 264 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 265 | CLANG_CXX_LIBRARY = "libc++"; 266 | CLANG_ENABLE_MODULES = YES; 267 | CLANG_ENABLE_OBJC_ARC = YES; 268 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 269 | CLANG_WARN_BOOL_CONVERSION = YES; 270 | CLANG_WARN_COMMA = YES; 271 | CLANG_WARN_CONSTANT_CONVERSION = YES; 272 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 281 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 282 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 283 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 284 | CLANG_WARN_STRICT_PROTOTYPES = YES; 285 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 286 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = dwarf; 292 | ENABLE_STRICT_OBJC_MSGSEND = YES; 293 | ENABLE_TESTABILITY = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu99; 295 | GCC_DYNAMIC_NO_PIC = NO; 296 | GCC_NO_COMMON_BLOCKS = YES; 297 | GCC_OPTIMIZATION_LEVEL = 0; 298 | GCC_PREPROCESSOR_DEFINITIONS = ( 299 | "DEBUG=1", 300 | "$(inherited)", 301 | ); 302 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 303 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 304 | GCC_WARN_UNDECLARED_SELECTOR = YES; 305 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 306 | GCC_WARN_UNUSED_FUNCTION = YES; 307 | GCC_WARN_UNUSED_VARIABLE = YES; 308 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 309 | MTL_ENABLE_DEBUG_INFO = YES; 310 | ONLY_ACTIVE_ARCH = YES; 311 | SDKROOT = iphoneos; 312 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 313 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 314 | SWIFT_VERSION = ""; 315 | TARGETED_DEVICE_FAMILY = "1,2"; 316 | }; 317 | name = Debug; 318 | }; 319 | 67B016BF1DC21CEC006EF53E /* Release */ = { 320 | isa = XCBuildConfiguration; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_NONNULL = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 333 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 334 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 335 | CLANG_WARN_EMPTY_BODY = YES; 336 | CLANG_WARN_ENUM_CONVERSION = YES; 337 | CLANG_WARN_INFINITE_RECURSION = YES; 338 | CLANG_WARN_INT_CONVERSION = YES; 339 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 341 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 343 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 344 | CLANG_WARN_STRICT_PROTOTYPES = YES; 345 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 346 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 347 | CLANG_WARN_UNREACHABLE_CODE = YES; 348 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 349 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 350 | COPY_PHASE_STRIP = NO; 351 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 352 | ENABLE_NS_ASSERTIONS = NO; 353 | ENABLE_STRICT_OBJC_MSGSEND = YES; 354 | GCC_C_LANGUAGE_STANDARD = gnu99; 355 | GCC_NO_COMMON_BLOCKS = YES; 356 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 357 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 358 | GCC_WARN_UNDECLARED_SELECTOR = YES; 359 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 360 | GCC_WARN_UNUSED_FUNCTION = YES; 361 | GCC_WARN_UNUSED_VARIABLE = YES; 362 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 363 | MTL_ENABLE_DEBUG_INFO = NO; 364 | SDKROOT = iphoneos; 365 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 366 | SWIFT_VERSION = ""; 367 | TARGETED_DEVICE_FAMILY = "1,2"; 368 | VALIDATE_PRODUCT = YES; 369 | }; 370 | name = Release; 371 | }; 372 | 67B016C11DC21CEC006EF53E /* Debug */ = { 373 | isa = XCBuildConfiguration; 374 | buildSettings = { 375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 376 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 377 | DEVELOPMENT_TEAM = 534ZL84PZG; 378 | FRAMEWORK_SEARCH_PATHS = ( 379 | "$(inherited)", 380 | "$(PROJECT_DIR)/Carthage/Build/iOS", 381 | ); 382 | INFOPLIST_FILE = ExampleProject/Info.plist; 383 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 384 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.ExampleProject; 385 | PRODUCT_NAME = "$(TARGET_NAME)"; 386 | PROVISIONING_PROFILE = ""; 387 | PROVISIONING_PROFILE_SPECIFIER = ""; 388 | SWIFT_VERSION = 5.0; 389 | }; 390 | name = Debug; 391 | }; 392 | 67B016C21DC21CEC006EF53E /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 396 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 397 | DEVELOPMENT_TEAM = 534ZL84PZG; 398 | FRAMEWORK_SEARCH_PATHS = ( 399 | "$(inherited)", 400 | "$(PROJECT_DIR)/Carthage/Build/iOS", 401 | ); 402 | INFOPLIST_FILE = ExampleProject/Info.plist; 403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 404 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.ExampleProject; 405 | PRODUCT_NAME = "$(TARGET_NAME)"; 406 | PROVISIONING_PROFILE = ""; 407 | PROVISIONING_PROFILE_SPECIFIER = ""; 408 | SWIFT_VERSION = 5.0; 409 | }; 410 | name = Release; 411 | }; 412 | /* End XCBuildConfiguration section */ 413 | 414 | /* Begin XCConfigurationList section */ 415 | 67B0169E1DC21CEC006EF53E /* Build configuration list for PBXProject "SwipingCards" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 67B016BE1DC21CEC006EF53E /* Debug */, 419 | 67B016BF1DC21CEC006EF53E /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | 67B016C01DC21CEC006EF53E /* Build configuration list for PBXNativeTarget "SwipingCards" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 67B016C11DC21CEC006EF53E /* Debug */, 428 | 67B016C21DC21CEC006EF53E /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | }; 435 | rootObject = 67B0169B1DC21CEC006EF53E /* Project object */; 436 | } 437 | -------------------------------------------------------------------------------- /ExampleProject/SwipingCards.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Pablo Paciello 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let package = Package( 5 | name: "Swiping Carousel", 6 | platforms: [ 7 | .iOS(.v9), 8 | ], 9 | products: [ 10 | .library( 11 | name: "Swiping Carousel", 12 | targets: ["SwipingCarousel"]), 13 | ], 14 | dependencies: [ 15 | // no dependencies 16 | ], 17 | targets: [ 18 | .target( 19 | name: "SwipingCarousel", 20 | dependencies: []), 21 | .testTarget( 22 | name: "SwipingCarouselTests", 23 | dependencies: ["SwipingCarousel"]), 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwipingCarousel 2 | ![](https://img.shields.io/badge/platforms-ios-brightgreen.svg?style=flat) 3 | [![](https://img.shields.io/badge/swiftpm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | ![Swift 5.1](https://img.shields.io/badge/Swift-5.1-orange.svg) 6 | ![](https://api.travis-ci.org/PPacie/SwipingCarousel.svg?branch=master) 7 | [![](https://img.shields.io/badge/twitter-@ppacie-blue.svg?style=flat)](https://twitter.com/ppacie) 8 | 9 | ![alt tag](https://github.com/PPacie/Swiping-Carousel/blob/master/Swiping-Carousel-Demo.gif) 10 | 11 | ## What does it do? 12 | * When scrolling, the cards magnify when they get to the center. 13 | * You can tap and hold on a card, swipe up to **'Like'** it. Right after, it will scroll to next one. 14 | * You can tap and hold on a card, swipe down to **'Dismiss'** it. After, it will scroll to next card. 15 | * When tapping on the centered card, it will be opened a chat room with the user the card represents. 16 | * When tapping on the side cards, it will scroll to them. 17 | * When swiping up or down on the side cards, they won't be liked or dismissed. Just moved. 18 | 19 | ## Installation 20 | #### Carthage 21 | ~~~ 22 | github "ppacie/SwipingCarousel" 23 | ~~~ 24 | 25 | ## How to set it up 26 | 27 | 1. Import `SwipingCarousel` module to your `ViewController` and `CollectionViewCell` classes. 28 | 29 | ```swift 30 | import SwipingCarousel 31 | ``` 32 | 2. Make your `CollectionViewCell` subclass of `SwipingCarouselCollectionViewCell`. 33 | 34 | ```swift 35 | class MyCollectionViewCell: SwipingCarouselCollectionViewCell { 36 | } 37 | ``` 38 | 3. Conform to `SwipingCarouselDelegate` protocol methods `func cellSwipedDown()` and `func cellSwipedUp()`. 39 | e.g. 40 | ```swift 41 | extension MyCollectionViewController: SwipingCarouselDelegate { 42 | 43 | func cellSwipedUp(_ cell: UICollectionViewCell) { 44 | ... 45 | } 46 | 47 | func cellSwipedDown(_ cell: UICollectionViewCell) { 48 | ... 49 | } 50 | } 51 | ``` 52 | 4. Finally, you will need to set it as your CollectionView's custom layout: You can do it either in the Interface Builder **OR** programmatically. 53 | 54 | * Interface Builder: Go to your Storyboard file and select the Controller where you have the CollectionView. Later select the CollectionView in the Document Outline and set the `SwipingCarouselFlowLayout` as the Custom Layout in the Attributes Inspector and set the Module to `SwipingCarousel`. 55 | 56 | ![alt tag](https://github.com/PPacie/Swiping-Carousel/blob/master/AddCustomLayout.png) 57 | 58 | * Programmaticaly: 59 | Add the following line in the `viewDidLoad()` of your `CollectionViewController` (the ViewController that contains your CollectionView): 60 | 61 | ```swift 62 | collectionView.setCollectionViewLayout(SwipingCarouselFlowLayout(), animated: false) 63 | ``` 64 | ## SwipingCarouselCollectionViewCell Properties 65 | 66 | ```swift 67 | weak public var delegate: SwipingCarouselDelegate? 68 | ``` 69 | An object that supports the SwipingCarouselDelegate protocol and can respond to swiping events. 70 | ```swift 71 | public var deleteOnSwipeDown = false 72 | ``` 73 | Indicates weather to remove the SwipingCarouselCell cell from view or to get it back to the original position when swiping down. Default behavior is to get back to original position. 74 | ```swift 75 | public var deleteOnSwipeUp = false 76 | ``` 77 | Indicates weather to remove the SwipingCarouselCell cell from view or to get it back to the original position when swiping up. Default behavior is to get back to original position. 78 | 79 | ## Example 80 | Check the *ExampleProject* folder to see how it works. 81 | 82 | ## Can I customize the Layout? 83 | Sure, you are able to customize the layout by overriding the `UICollectionViewDelegateFlowLayout` methods you desire. 84 | e.g. 85 | ```swift 86 | extension MyCollectionViewController: UICollectionViewDelegateFlowLayout { 87 | 88 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 89 | return UIEdgeInsets(top: 200, left: 120, bottom: 200, right: 120) 90 | } 91 | 92 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 93 | return CGSize(width: 150.0, height: 230.0) 94 | } 95 | 96 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 97 | return 50.0 98 | } 99 | } 100 | ``` 101 | Also if you set the layout programmatically you will be allowed to change the ```activeDistance``` value. This property will get you access to modify the layoutAttributes of the collectionView. It represents the distance from the center where the cell/item will start zooming in and out. Default value is 200.0 102 | 103 | ```swift 104 | let layout = SwipingCarouselFlowLayout() 105 | layout.activeDistance = 50 106 | collectionView.setCollectionViewLayout(layout, animated: false) 107 | ``` 108 | -------------------------------------------------------------------------------- /Sources/SwipingCarousel/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 | FMWK 17 | CFBundleShortVersionString 18 | 1.5 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sources/SwipingCarousel/Swiping Carousel.h: -------------------------------------------------------------------------------- 1 | // 2 | // Swiping Carousel.h 3 | // Swiping Carousel 4 | // 5 | // Created by Pablo Paciello on 10/27/16. 6 | // Copyright © 2016 PPacie. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for Swiping Carousel. 12 | FOUNDATION_EXPORT double Swiping_CarouselVersionNumber; 13 | 14 | //! Project version string for Swiping Carousel. 15 | FOUNDATION_EXPORT const unsigned char Swiping_CarouselVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Sources/SwipingCarousel/SwipingCarouselCellManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipingCarouselCellManager.swift 3 | // SwipingCarousel 4 | // 5 | // Created by Pablo Paciello on 8/1/17. 6 | // Copyright © 2017 PPacie. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | final class SwpingCarouselCellManager { 12 | private var cell: SwipingCarouselCollectionViewCell! 13 | lazy var swipeDistanceOnY = CGFloat() //Distance of the swipe over "y" axis. 14 | lazy var originalPoint = CGPoint() 15 | 16 | init(withCell cell: SwipingCarouselCollectionViewCell) { 17 | self.cell = cell 18 | } 19 | 20 | //MARK: - Gestures Handling 21 | private enum Constants { 22 | static let SwipeDistanceToTakeAction: CGFloat = UIScreen.main.bounds.size.height / 5 //Distance required for the cell to go off the screen. 23 | static let SwipeImageAnimationDuration: TimeInterval = 0.30 //Duration of the Animation when Swiping Up/Down. 24 | static let CenterImageAnimationDuration: TimeInterval = 0.20 //Duration of the Animation when image gets back to original postion. 25 | } 26 | 27 | func handlePanGesture(_ sender: UIPanGestureRecognizer) { 28 | //Get the distance of the Swipe on "y" axis. 29 | swipeDistanceOnY = sender.translation(in: self.cell).y 30 | 31 | switch sender.state { 32 | case .began: 33 | originalPoint = cell.center //Get the center of the Cell. 34 | case .changed: 35 | //Move the cell to the Y point while gesturing. 36 | cell.center = CGPoint(x: originalPoint.x, y: originalPoint.y + swipeDistanceOnY) 37 | case .ended: 38 | //Take action after the Swipe gesture ends. 39 | afterSwipeAction() 40 | default: 41 | break 42 | } 43 | } 44 | 45 | func afterSwipeAction() { 46 | //First, we check if the swiped cell is the one in the middle of screen by cheking its size. If the cell is one of the sides, we send it back to its original position. 47 | if (cell.frame.size.height > cell.bounds.size.height) { 48 | //If the cell is the one at the center (biggest one), we proceed to check wheather or not the distance of the gesture is enough to move the cell off the screen (up or down). 49 | if swipeDistanceOnY > Constants.SwipeDistanceToTakeAction { 50 | downAction() 51 | } else if swipeDistanceOnY < -Constants.SwipeDistanceToTakeAction { 52 | upAction() 53 | } else { 54 | UIView.animate(withDuration: Constants.CenterImageAnimationDuration, animations: { 55 | self.cell?.center = self.originalPoint 56 | }) 57 | } 58 | } else { 59 | UIView.animate(withDuration: Constants.CenterImageAnimationDuration, animations: { 60 | self.cell?.center = self.originalPoint 61 | }) 62 | } 63 | } 64 | 65 | func upAction() { 66 | /* The maxUpperPoint will depend on deleteOnSwipeUp variable. 67 | Under default behavior, 'false', the cell will go back to the original position. 68 | If it's set to 'true' the cell will go down off the screen to be able to delete it throught its delegate. */ 69 | let maxUpperPoint: CGPoint = cell.deleteOnSwipeUp ? CGPoint(x: originalPoint.x, y: 2 * cell.frame.minY) : originalPoint 70 | UIView.animate(withDuration: Constants.SwipeImageAnimationDuration, animations: { () -> Void in 71 | self.cell?.center = maxUpperPoint //Move the cell to the maxUpperPoint. 72 | self.cell?.superview?.isUserInteractionEnabled = false //Deactivate the user interaction in the Superview (In this case will be in the collection view). To avoid scrolling during the animation. 73 | }, completion: { (completion) -> Void in 74 | self.cell?.superview?.isUserInteractionEnabled = true // Re-activate the user interaction. 75 | self.cell?.delegate?.cellSwipedUp(self.cell!) //Delegate the SwipeUp action and send the view with it. 76 | }) 77 | } 78 | 79 | func downAction() { 80 | /* The maxDownPoint will depend on deleteOnSwipeDown variable. 81 | Under default behavior, 'false', the cell will go back to the original position. 82 | If it's set to 'true' the cell will go down off the screen to be able to delete it throught its delegate. */ 83 | let maxDownPoint: CGPoint = cell.deleteOnSwipeDown ? CGPoint(x: originalPoint.x, y: 2 * cell.frame.maxY) : originalPoint 84 | UIView.animate(withDuration: Constants.SwipeImageAnimationDuration, animations: { () -> Void in 85 | self.cell?.center = maxDownPoint //Move the cell to the maxDownPoint. 86 | self.cell?.superview?.isUserInteractionEnabled = false //Deactivate the user interaction in the Superview (In this case will be in the collection view). To avoid scrolling during the animation. 87 | }, completion: { (completion) -> Void in 88 | self.cell?.superview?.isUserInteractionEnabled = true // Re-activate the user interaction. 89 | self.cell?.delegate?.cellSwipedDown(self.cell!) //Delegate the SwipeDown action and send the view with it. 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/SwipingCarousel/SwipingCarouselCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipingCarouselCollectionViewCell.swift 3 | // Swiping Cards 4 | // 5 | // Created by Pablo Paciello on 10/26/16. 6 | // Copyright © 2016 Pablo Paciello. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | //Protocol to inform that cell is being swiped up or down. 12 | public protocol SwipingCarouselDelegate : class { 13 | func cellSwipedUp(_ cell: UICollectionViewCell) 14 | func cellSwipedDown(_ cell: UICollectionViewCell) 15 | } 16 | 17 | open class SwipingCarouselCollectionViewCell: UICollectionViewCell { 18 | public weak var delegate: SwipingCarouselDelegate? 19 | public var deleteOnSwipeUp = false 20 | public var deleteOnSwipeDown = false 21 | private var cellManager: SwpingCarouselCellManager! 22 | 23 | open override func awakeFromNib() { 24 | super.awakeFromNib() 25 | //Init the Cell Manager 26 | cellManager = SwpingCarouselCellManager(withCell: self) 27 | // Add Gesture to Cell 28 | addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:)))) 29 | } 30 | 31 | @objc private func handlePanGesture(_ sender: UIPanGestureRecognizer) { 32 | cellManager.handlePanGesture(sender) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/SwipingCarousel/SwipingCarouselFlowLayout.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipingCarouselFlowLayout.swift 3 | // Swiping Cards 4 | // 5 | // Created by Pablo Paciello on 8/20/15. 6 | // Copyright (c) 2015 Pablo Paciello. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | public class SwipingCarouselFlowLayout: UICollectionViewFlowLayout { 12 | 13 | // MARK: - Constants 14 | private enum Constants { 15 | static let zoomFactor: CGFloat = 0.3 16 | static let itemWidth: CGFloat = 230 //Width of the Cell. 17 | static let itemHeight: CGFloat = 300 //Height of the Cell. 18 | static let minLineSpacing: CGFloat = 50.0 19 | } 20 | 21 | public var activeDistance: CGFloat = 200 22 | 23 | override public func prepare() { 24 | super.prepare() 25 | 26 | itemSize = CGSize(width: Constants.itemWidth, height: Constants.itemHeight) 27 | scrollDirection = .horizontal 28 | minimumLineSpacing = Constants.minLineSpacing 29 | //These numbers will depend on the size of your cards you have set in the CardsViewFlowConstants. 30 | //60 - will let the first and last card of the CollectionView to be centered. 31 | //100 - will avoid the double rows in the CollectionView 32 | sectionInset = UIEdgeInsets.init(top: 100.0, left: 60.0, bottom: 100, right: 60.0) 33 | } 34 | 35 | // Here is where the magic happens 36 | // Add zooming to the Layout Attributes. 37 | override public func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? { 38 | 39 | let array = super.layoutAttributesForElements(in: rect) 40 | var attributesCopy = [UICollectionViewLayoutAttributes]() 41 | 42 | var visibleRect = CGRect() 43 | visibleRect.origin = collectionView!.contentOffset 44 | visibleRect.size = collectionView!.bounds.size 45 | 46 | for itemAttributes in array! { 47 | //let newAttributes: UICollectionViewLayoutAttributes = itemAttributes 48 | let itemAttributesCopy = itemAttributes.copy() as! UICollectionViewLayoutAttributes 49 | if itemAttributesCopy.frame.intersects(rect) { 50 | let distance = visibleRect.midX - itemAttributes.center.x 51 | let normalizedDistance = distance / activeDistance 52 | if (abs(distance)) < activeDistance { 53 | let zoom = 1 + Constants.zoomFactor*(1 - abs(normalizedDistance)) 54 | itemAttributesCopy.transform3D = CATransform3DMakeScale(zoom, zoom, 1.0) 55 | itemAttributesCopy.zIndex = 1 56 | } 57 | } 58 | attributesCopy.append(itemAttributesCopy) 59 | } 60 | return attributesCopy 61 | } 62 | 63 | //Focus the zoom in the middle Card. 64 | override public func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { 65 | 66 | var offsetAdjustment:CGFloat = CGFloat(MAXFLOAT) 67 | let horizontalCenter = proposedContentOffset.x + (collectionView!.bounds.width / 2.0) 68 | 69 | let targetRect = CGRect(x: proposedContentOffset.x, y: 0.0, width: collectionView!.bounds.size.width, height: collectionView!.bounds.size.height) 70 | 71 | if let array = super.layoutAttributesForElements(in: targetRect) { 72 | for layoutAttributes in array { 73 | let itemHorizontalCenter: CGFloat = layoutAttributes.center.x 74 | if (abs(itemHorizontalCenter - horizontalCenter) < abs(offsetAdjustment)) { 75 | offsetAdjustment = itemHorizontalCenter - horizontalCenter 76 | } 77 | } 78 | } 79 | return CGPoint(x: proposedContentOffset.x + offsetAdjustment, y: proposedContentOffset.y) 80 | } 81 | 82 | // Invalidate the Layout when the user is scrolling 83 | override public func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool { 84 | return true 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Swiping-Carousel-Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PPacie/SwipingCarousel/4247840a2ae7a799d0710495ae3d3ff49590036b/Swiping-Carousel-Demo.gif -------------------------------------------------------------------------------- /SwipingCarousel.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 67895D8A1F3095FD0004DB9E /* SwipingCarouselTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67895D891F3095FD0004DB9E /* SwipingCarouselTests.swift */; }; 11 | 67895D8C1F3095FD0004DB9E /* SwipingCarousel.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 67B0166E1DC2041F006EF53E /* SwipingCarousel.framework */; }; 12 | 67B0167F1DC2041F006EF53E /* Swiping Carousel.h in Headers */ = {isa = PBXBuildFile; fileRef = 67B016711DC2041F006EF53E /* Swiping Carousel.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 67B0168A1DC20B1D006EF53E /* SwipingCarouselFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016881DC20B1D006EF53E /* SwipingCarouselFlowLayout.swift */; }; 14 | 67B0168B1DC20B1D006EF53E /* SwipingCarouselCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67B016891DC20B1D006EF53E /* SwipingCarouselCollectionViewCell.swift */; }; 15 | 67CBE1721F3089FC00F35988 /* SwipingCarouselCellManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67CBE1711F3089FC00F35988 /* SwipingCarouselCellManager.swift */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXContainerItemProxy section */ 19 | 67895D8D1F3095FD0004DB9E /* PBXContainerItemProxy */ = { 20 | isa = PBXContainerItemProxy; 21 | containerPortal = 67B016651DC2041F006EF53E /* Project object */; 22 | proxyType = 1; 23 | remoteGlobalIDString = 67B0166D1DC2041F006EF53E; 24 | remoteInfo = SwipingCarousel; 25 | }; 26 | /* End PBXContainerItemProxy section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | 6759C10E23A24C2600B7F506 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 30 | 67895D871F3095FD0004DB9E /* SwipingCarouselTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwipingCarouselTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 67895D891F3095FD0004DB9E /* SwipingCarouselTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipingCarouselTests.swift; sourceTree = ""; }; 32 | 67895D8B1F3095FD0004DB9E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 67B0166E1DC2041F006EF53E /* SwipingCarousel.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwipingCarousel.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 67B016711DC2041F006EF53E /* Swiping Carousel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Swiping Carousel.h"; sourceTree = ""; }; 35 | 67B016721DC2041F006EF53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 36 | 67B016881DC20B1D006EF53E /* SwipingCarouselFlowLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwipingCarouselFlowLayout.swift; sourceTree = ""; }; 37 | 67B016891DC20B1D006EF53E /* SwipingCarouselCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwipingCarouselCollectionViewCell.swift; sourceTree = ""; }; 38 | 67CBE1711F3089FC00F35988 /* SwipingCarouselCellManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwipingCarouselCellManager.swift; sourceTree = ""; }; 39 | /* End PBXFileReference section */ 40 | 41 | /* Begin PBXFrameworksBuildPhase section */ 42 | 67895D841F3095FD0004DB9E /* Frameworks */ = { 43 | isa = PBXFrameworksBuildPhase; 44 | buildActionMask = 2147483647; 45 | files = ( 46 | 67895D8C1F3095FD0004DB9E /* SwipingCarousel.framework in Frameworks */, 47 | ); 48 | runOnlyForDeploymentPostprocessing = 0; 49 | }; 50 | 67B0166A1DC2041F006EF53E /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | /* End PBXFrameworksBuildPhase section */ 58 | 59 | /* Begin PBXGroup section */ 60 | 6759C10C23A24ADF00B7F506 /* Sources */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 67B016701DC2041F006EF53E /* SwipingCarousel */, 64 | ); 65 | path = Sources; 66 | sourceTree = ""; 67 | }; 68 | 6759C10D23A24B4700B7F506 /* Tests */ = { 69 | isa = PBXGroup; 70 | children = ( 71 | 67895D881F3095FD0004DB9E /* SwipingCarouselTests */, 72 | ); 73 | path = Tests; 74 | sourceTree = ""; 75 | }; 76 | 67895D881F3095FD0004DB9E /* SwipingCarouselTests */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 67895D891F3095FD0004DB9E /* SwipingCarouselTests.swift */, 80 | 67895D8B1F3095FD0004DB9E /* Info.plist */, 81 | ); 82 | path = SwipingCarouselTests; 83 | sourceTree = ""; 84 | }; 85 | 67B016641DC2041F006EF53E = { 86 | isa = PBXGroup; 87 | children = ( 88 | 6759C10E23A24C2600B7F506 /* Package.swift */, 89 | 6759C10C23A24ADF00B7F506 /* Sources */, 90 | 6759C10D23A24B4700B7F506 /* Tests */, 91 | 67B0166F1DC2041F006EF53E /* Products */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | 67B0166F1DC2041F006EF53E /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 67B0166E1DC2041F006EF53E /* SwipingCarousel.framework */, 99 | 67895D871F3095FD0004DB9E /* SwipingCarouselTests.xctest */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 67B016701DC2041F006EF53E /* SwipingCarousel */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 67B016881DC20B1D006EF53E /* SwipingCarouselFlowLayout.swift */, 108 | 67B016891DC20B1D006EF53E /* SwipingCarouselCollectionViewCell.swift */, 109 | 67CBE1711F3089FC00F35988 /* SwipingCarouselCellManager.swift */, 110 | 67B016711DC2041F006EF53E /* Swiping Carousel.h */, 111 | 67B016721DC2041F006EF53E /* Info.plist */, 112 | ); 113 | path = SwipingCarousel; 114 | sourceTree = ""; 115 | }; 116 | /* End PBXGroup section */ 117 | 118 | /* Begin PBXHeadersBuildPhase section */ 119 | 67B0166B1DC2041F006EF53E /* Headers */ = { 120 | isa = PBXHeadersBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | 67B0167F1DC2041F006EF53E /* Swiping Carousel.h in Headers */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXHeadersBuildPhase section */ 128 | 129 | /* Begin PBXNativeTarget section */ 130 | 67895D861F3095FD0004DB9E /* SwipingCarouselTests */ = { 131 | isa = PBXNativeTarget; 132 | buildConfigurationList = 67895D8F1F3095FD0004DB9E /* Build configuration list for PBXNativeTarget "SwipingCarouselTests" */; 133 | buildPhases = ( 134 | 67895D831F3095FD0004DB9E /* Sources */, 135 | 67895D841F3095FD0004DB9E /* Frameworks */, 136 | 67895D851F3095FD0004DB9E /* Resources */, 137 | ); 138 | buildRules = ( 139 | ); 140 | dependencies = ( 141 | 67895D8E1F3095FD0004DB9E /* PBXTargetDependency */, 142 | ); 143 | name = SwipingCarouselTests; 144 | productName = SwipingCarouselTests; 145 | productReference = 67895D871F3095FD0004DB9E /* SwipingCarouselTests.xctest */; 146 | productType = "com.apple.product-type.bundle.unit-test"; 147 | }; 148 | 67B0166D1DC2041F006EF53E /* SwipingCarousel */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 67B016821DC2041F006EF53E /* Build configuration list for PBXNativeTarget "SwipingCarousel" */; 151 | buildPhases = ( 152 | 67B016691DC2041F006EF53E /* Sources */, 153 | 67B0166A1DC2041F006EF53E /* Frameworks */, 154 | 67B0166B1DC2041F006EF53E /* Headers */, 155 | 67B0166C1DC2041F006EF53E /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = SwipingCarousel; 162 | productName = "Swiping Carousel"; 163 | productReference = 67B0166E1DC2041F006EF53E /* SwipingCarousel.framework */; 164 | productType = "com.apple.product-type.framework"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 67B016651DC2041F006EF53E /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | LastSwiftUpdateCheck = 0830; 173 | LastUpgradeCheck = 1120; 174 | ORGANIZATIONNAME = PPacie; 175 | TargetAttributes = { 176 | 67895D861F3095FD0004DB9E = { 177 | CreatedOnToolsVersion = 8.3.3; 178 | DevelopmentTeam = 534ZL84PZG; 179 | LastSwiftMigration = 1120; 180 | ProvisioningStyle = Automatic; 181 | }; 182 | 67B0166D1DC2041F006EF53E = { 183 | CreatedOnToolsVersion = 8.0; 184 | DevelopmentTeam = 534ZL84PZG; 185 | LastSwiftMigration = 1120; 186 | ProvisioningStyle = Automatic; 187 | }; 188 | }; 189 | }; 190 | buildConfigurationList = 67B016681DC2041F006EF53E /* Build configuration list for PBXProject "SwipingCarousel" */; 191 | compatibilityVersion = "Xcode 3.2"; 192 | developmentRegion = en; 193 | hasScannedForEncodings = 0; 194 | knownRegions = ( 195 | en, 196 | Base, 197 | ); 198 | mainGroup = 67B016641DC2041F006EF53E; 199 | productRefGroup = 67B0166F1DC2041F006EF53E /* Products */; 200 | projectDirPath = ""; 201 | projectRoot = ""; 202 | targets = ( 203 | 67B0166D1DC2041F006EF53E /* SwipingCarousel */, 204 | 67895D861F3095FD0004DB9E /* SwipingCarouselTests */, 205 | ); 206 | }; 207 | /* End PBXProject section */ 208 | 209 | /* Begin PBXResourcesBuildPhase section */ 210 | 67895D851F3095FD0004DB9E /* Resources */ = { 211 | isa = PBXResourcesBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | ); 215 | runOnlyForDeploymentPostprocessing = 0; 216 | }; 217 | 67B0166C1DC2041F006EF53E /* Resources */ = { 218 | isa = PBXResourcesBuildPhase; 219 | buildActionMask = 2147483647; 220 | files = ( 221 | ); 222 | runOnlyForDeploymentPostprocessing = 0; 223 | }; 224 | /* End PBXResourcesBuildPhase section */ 225 | 226 | /* Begin PBXSourcesBuildPhase section */ 227 | 67895D831F3095FD0004DB9E /* Sources */ = { 228 | isa = PBXSourcesBuildPhase; 229 | buildActionMask = 2147483647; 230 | files = ( 231 | 67895D8A1F3095FD0004DB9E /* SwipingCarouselTests.swift in Sources */, 232 | ); 233 | runOnlyForDeploymentPostprocessing = 0; 234 | }; 235 | 67B016691DC2041F006EF53E /* Sources */ = { 236 | isa = PBXSourcesBuildPhase; 237 | buildActionMask = 2147483647; 238 | files = ( 239 | 67CBE1721F3089FC00F35988 /* SwipingCarouselCellManager.swift in Sources */, 240 | 67B0168B1DC20B1D006EF53E /* SwipingCarouselCollectionViewCell.swift in Sources */, 241 | 67B0168A1DC20B1D006EF53E /* SwipingCarouselFlowLayout.swift in Sources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXSourcesBuildPhase section */ 246 | 247 | /* Begin PBXTargetDependency section */ 248 | 67895D8E1F3095FD0004DB9E /* PBXTargetDependency */ = { 249 | isa = PBXTargetDependency; 250 | target = 67B0166D1DC2041F006EF53E /* SwipingCarousel */; 251 | targetProxy = 67895D8D1F3095FD0004DB9E /* PBXContainerItemProxy */; 252 | }; 253 | /* End PBXTargetDependency section */ 254 | 255 | /* Begin XCBuildConfiguration section */ 256 | 67895D901F3095FD0004DB9E /* Debug */ = { 257 | isa = XCBuildConfiguration; 258 | buildSettings = { 259 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 260 | CODE_SIGN_IDENTITY = "iPhone Developer"; 261 | CODE_SIGN_STYLE = Automatic; 262 | DEVELOPMENT_TEAM = 534ZL84PZG; 263 | INFOPLIST_FILE = Tests/SwipingCarouselTests/Info.plist; 264 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 265 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 266 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.SwipingCarouselTests; 267 | PRODUCT_NAME = "$(TARGET_NAME)"; 268 | PROVISIONING_PROFILE_SPECIFIER = ""; 269 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 270 | SWIFT_VERSION = 5.0; 271 | }; 272 | name = Debug; 273 | }; 274 | 67895D911F3095FD0004DB9E /* Release */ = { 275 | isa = XCBuildConfiguration; 276 | buildSettings = { 277 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 278 | CODE_SIGN_IDENTITY = "iPhone Developer"; 279 | CODE_SIGN_STYLE = Automatic; 280 | DEVELOPMENT_TEAM = 534ZL84PZG; 281 | INFOPLIST_FILE = Tests/SwipingCarouselTests/Info.plist; 282 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 283 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 284 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.SwipingCarouselTests; 285 | PRODUCT_NAME = "$(TARGET_NAME)"; 286 | PROVISIONING_PROFILE_SPECIFIER = ""; 287 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 288 | SWIFT_VERSION = 5.0; 289 | }; 290 | name = Release; 291 | }; 292 | 67B016801DC2041F006EF53E /* Debug */ = { 293 | isa = XCBuildConfiguration; 294 | buildSettings = { 295 | ALWAYS_SEARCH_USER_PATHS = NO; 296 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 297 | CLANG_ANALYZER_NONNULL = YES; 298 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 299 | CLANG_CXX_LIBRARY = "libc++"; 300 | CLANG_ENABLE_MODULES = YES; 301 | CLANG_ENABLE_OBJC_ARC = YES; 302 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 303 | CLANG_WARN_BOOL_CONVERSION = YES; 304 | CLANG_WARN_COMMA = YES; 305 | CLANG_WARN_CONSTANT_CONVERSION = YES; 306 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 308 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 309 | CLANG_WARN_EMPTY_BODY = YES; 310 | CLANG_WARN_ENUM_CONVERSION = YES; 311 | CLANG_WARN_INFINITE_RECURSION = YES; 312 | CLANG_WARN_INT_CONVERSION = YES; 313 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 314 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 315 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 317 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 318 | CLANG_WARN_STRICT_PROTOTYPES = YES; 319 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 320 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | CURRENT_PROJECT_VERSION = 1; 326 | DEBUG_INFORMATION_FORMAT = dwarf; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | ENABLE_TESTABILITY = YES; 329 | GCC_C_LANGUAGE_STANDARD = gnu99; 330 | GCC_DYNAMIC_NO_PIC = NO; 331 | GCC_NO_COMMON_BLOCKS = YES; 332 | GCC_OPTIMIZATION_LEVEL = 0; 333 | GCC_PREPROCESSOR_DEFINITIONS = ( 334 | "DEBUG=1", 335 | "$(inherited)", 336 | ); 337 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 338 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 339 | GCC_WARN_UNDECLARED_SELECTOR = YES; 340 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 341 | GCC_WARN_UNUSED_FUNCTION = YES; 342 | GCC_WARN_UNUSED_VARIABLE = YES; 343 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 344 | MTL_ENABLE_DEBUG_INFO = YES; 345 | ONLY_ACTIVE_ARCH = YES; 346 | SDKROOT = iphoneos; 347 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 348 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 349 | TARGETED_DEVICE_FAMILY = "1,2"; 350 | VERSIONING_SYSTEM = "apple-generic"; 351 | VERSION_INFO_PREFIX = ""; 352 | }; 353 | name = Debug; 354 | }; 355 | 67B016811DC2041F006EF53E /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ALWAYS_SEARCH_USER_PATHS = NO; 359 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 360 | CLANG_ANALYZER_NONNULL = YES; 361 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 362 | CLANG_CXX_LIBRARY = "libc++"; 363 | CLANG_ENABLE_MODULES = YES; 364 | CLANG_ENABLE_OBJC_ARC = YES; 365 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 366 | CLANG_WARN_BOOL_CONVERSION = YES; 367 | CLANG_WARN_COMMA = YES; 368 | CLANG_WARN_CONSTANT_CONVERSION = YES; 369 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 370 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 371 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 384 | CLANG_WARN_UNREACHABLE_CODE = YES; 385 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 386 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 387 | COPY_PHASE_STRIP = NO; 388 | CURRENT_PROJECT_VERSION = 1; 389 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 390 | ENABLE_NS_ASSERTIONS = NO; 391 | ENABLE_STRICT_OBJC_MSGSEND = YES; 392 | GCC_C_LANGUAGE_STANDARD = gnu99; 393 | GCC_NO_COMMON_BLOCKS = YES; 394 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 395 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 396 | GCC_WARN_UNDECLARED_SELECTOR = YES; 397 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 398 | GCC_WARN_UNUSED_FUNCTION = YES; 399 | GCC_WARN_UNUSED_VARIABLE = YES; 400 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 401 | MTL_ENABLE_DEBUG_INFO = NO; 402 | SDKROOT = iphoneos; 403 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 404 | TARGETED_DEVICE_FAMILY = "1,2"; 405 | VALIDATE_PRODUCT = YES; 406 | VERSIONING_SYSTEM = "apple-generic"; 407 | VERSION_INFO_PREFIX = ""; 408 | }; 409 | name = Release; 410 | }; 411 | 67B016831DC2041F006EF53E /* Debug */ = { 412 | isa = XCBuildConfiguration; 413 | buildSettings = { 414 | CLANG_ENABLE_MODULES = YES; 415 | CODE_SIGN_IDENTITY = ""; 416 | CODE_SIGN_STYLE = Automatic; 417 | DEFINES_MODULE = YES; 418 | DEVELOPMENT_TEAM = 534ZL84PZG; 419 | DYLIB_COMPATIBILITY_VERSION = 1; 420 | DYLIB_CURRENT_VERSION = 1; 421 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 422 | INFOPLIST_FILE = Sources/SwipingCarousel/Info.plist; 423 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 424 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 425 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 426 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.SwipingCarousel; 427 | PRODUCT_NAME = "$(TARGET_NAME)"; 428 | PROVISIONING_PROFILE_SPECIFIER = ""; 429 | SKIP_INSTALL = YES; 430 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 431 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 432 | SWIFT_VERSION = 5.0; 433 | }; 434 | name = Debug; 435 | }; 436 | 67B016841DC2041F006EF53E /* Release */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | CLANG_ENABLE_MODULES = YES; 440 | CODE_SIGN_IDENTITY = ""; 441 | CODE_SIGN_STYLE = Automatic; 442 | DEFINES_MODULE = YES; 443 | DEVELOPMENT_TEAM = 534ZL84PZG; 444 | DYLIB_COMPATIBILITY_VERSION = 1; 445 | DYLIB_CURRENT_VERSION = 1; 446 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 447 | INFOPLIST_FILE = Sources/SwipingCarousel/Info.plist; 448 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 449 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 450 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 451 | PRODUCT_BUNDLE_IDENTIFIER = com.ppacie.SwipingCarousel; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | PROVISIONING_PROFILE_SPECIFIER = ""; 454 | SKIP_INSTALL = YES; 455 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 456 | SWIFT_VERSION = 5.0; 457 | }; 458 | name = Release; 459 | }; 460 | /* End XCBuildConfiguration section */ 461 | 462 | /* Begin XCConfigurationList section */ 463 | 67895D8F1F3095FD0004DB9E /* Build configuration list for PBXNativeTarget "SwipingCarouselTests" */ = { 464 | isa = XCConfigurationList; 465 | buildConfigurations = ( 466 | 67895D901F3095FD0004DB9E /* Debug */, 467 | 67895D911F3095FD0004DB9E /* Release */, 468 | ); 469 | defaultConfigurationIsVisible = 0; 470 | defaultConfigurationName = Release; 471 | }; 472 | 67B016681DC2041F006EF53E /* Build configuration list for PBXProject "SwipingCarousel" */ = { 473 | isa = XCConfigurationList; 474 | buildConfigurations = ( 475 | 67B016801DC2041F006EF53E /* Debug */, 476 | 67B016811DC2041F006EF53E /* Release */, 477 | ); 478 | defaultConfigurationIsVisible = 0; 479 | defaultConfigurationName = Release; 480 | }; 481 | 67B016821DC2041F006EF53E /* Build configuration list for PBXNativeTarget "SwipingCarousel" */ = { 482 | isa = XCConfigurationList; 483 | buildConfigurations = ( 484 | 67B016831DC2041F006EF53E /* Debug */, 485 | 67B016841DC2041F006EF53E /* Release */, 486 | ); 487 | defaultConfigurationIsVisible = 0; 488 | defaultConfigurationName = Release; 489 | }; 490 | /* End XCConfigurationList section */ 491 | }; 492 | rootObject = 67B016651DC2041F006EF53E /* Project object */; 493 | } 494 | -------------------------------------------------------------------------------- /SwipingCarousel.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwipingCarousel.xcodeproj/xcshareddata/xcschemes/Swiping Carousel.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Tests/SwipingCarouselTests/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 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/SwipingCarouselTests/SwipingCarouselTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwipingCarouselTests.swift 3 | // SwipingCarouselTests 4 | // 5 | // Created by Pablo Paciello on 8/1/17. 6 | // Copyright © 2017 PPacie. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwipingCarousel 11 | 12 | class SwipingCarouselTests: XCTestCase { 13 | 14 | var cellManager: SwpingCarouselCellManager! 15 | var cell: SwipingCarouselCollectionViewCell! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | // Put setup code here. This method is called before the invocation of each test method in the class. 20 | cell = SwipingCarouselCollectionViewCell() 21 | let initialSize = CGSize(width: 210, height: 278) 22 | cell.frame.size = initialSize 23 | cell.bounds.size = initialSize 24 | cellManager = SwpingCarouselCellManager(withCell: cell) 25 | cellManager.originalPoint = CGPoint(x: 165.0, y: 301.5) 26 | } 27 | 28 | override func tearDown() { 29 | cell = nil 30 | cellManager = nil 31 | // Put teardown code here. This method is called after the invocation of each test method in the class. 32 | super.tearDown() 33 | } 34 | 35 | func testSwipingSideCells() { 36 | //After swiping up or down over a cell which is not the one in the middle of the screen, the cell should go back to its original position. 37 | cellManager.swipeDistanceOnY = 200 38 | cell.frame.size.height = 200 39 | cellManager.afterSwipeAction() 40 | 41 | XCTAssertEqual(cell.center, cellManager.originalPoint, "Cell Should go back to CenterY position") 42 | } 43 | 44 | 45 | func testSwipingDownNoDeletion() { 46 | //After swiping down over a cell which has deleteOnSwipeDown disabled, the cell should go back to its original position. 47 | cellManager.swipeDistanceOnY = 300 48 | cell.deleteOnSwipeDown = false 49 | cellManager.downAction() 50 | 51 | XCTAssertEqual(cell.center, cellManager.originalPoint, "Cell Should go back to CenterY if Delete is disabled") 52 | } 53 | 54 | func testSwipingDownAndDeletion() { 55 | //After swiping down over a cell which has deleteOnSwipeDown enabled, the cell should go to the maxY possible, disappearing from Screen. 56 | cellManager.swipeDistanceOnY = 300 57 | cell.deleteOnSwipeDown = true 58 | cellManager.downAction() 59 | 60 | XCTAssertNotEqual(cell.center, cellManager.originalPoint, "Cell Should go off screen") 61 | } 62 | 63 | func testSwipingUpNoDeletion() { 64 | //After swiping up over a cell which has deleteOnSwipeUp disabled, the cell should go back to its original position. 65 | cellManager.swipeDistanceOnY = -300 66 | cell.deleteOnSwipeUp = false 67 | cellManager.upAction() 68 | 69 | XCTAssertEqual(cell.center, cellManager.originalPoint, "Cell Should go back to CenterY if Delete is disabled") 70 | } 71 | 72 | func testSwipingUpAndDeletion() { 73 | //After swiping up over a cell which has deleteOnSwipeUp enabled, the cell should go to the maxY possible, disappearing from Screen. 74 | cellManager.swipeDistanceOnY = -300 75 | cell.deleteOnSwipeUp = true 76 | cellManager.upAction() 77 | 78 | XCTAssertNotEqual(cell.center, cellManager.originalPoint, "Cell Should go off screen") 79 | } 80 | 81 | func testPerformanceExample() { 82 | // This is an example of a performance test case. 83 | self.measure { 84 | // Put the code you want to measure the time of here. 85 | } 86 | } 87 | 88 | } 89 | --------------------------------------------------------------------------------