🔥 A multi-directional card swiping library inspired by Tinder

Overview


Platform Swift Version Build Status Swift Package Manager CocoaPods Carthage Code Coverage LICENSE

Made with ❤️ by Mac Gallagher

Features

💡 Advanced swipe recognition based on velocity and card position

💡 Manual and programmatic actions

💡 Smooth card overlay view transitions

💡 Fluid and customizable animations

💡 Dynamic card loading using data source pattern

Example

To run the example project, clone the repo and run the ShuffleExample target.

Basic Usage

  1. Create your own card by either subclassing SwipeCard or setting its properties directly:

    func card(fromImage image: UIImage) -> SwipeCard {
      let card = SwipeCard()
      card.swipeDirections = [.left, .right]
      card.content = UIImageView(image: image)
      
      let leftOverlay = UIView()
      leftOverlay.backgroundColor = .green
      
      let rightOverlay = UIView()
      rightOverlay.backgroundColor = .red
      
      card.setOverlays([.left: leftOverlay, .right: rightOverlay])
      
      return card
    }

    The card returned from card(fromImage:) displays an image, can be swiped left or right, and has overlay views for both directions.

  2. Initialize your card data and place a SwipeCardStack on your view:

    class ViewController: UIViewController {
      let cardStack = SwipeCardStack()
      
      let cardImages = [
          UIImage(named: "cardImage1"),
          UIImage(named: "cardImage2"),
          UIImage(named: "cardImage3")
      ]
      
      override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(cardStack)
        cardStack.frame = view.safeAreaLayoutGuide.layoutFrame
      }
    }
  3. Conform your class to SwipeCardStackDataSource and set your card stack's dataSource:

    func cardStack(_ cardStack: SwipeCardStack, cardForIndexAt index: Int) -> SwipeCard {
      return card(fromImage: cardImages[index])
    }
    
    func numberOfCards(in cardStack: SwipeCardStack) -> Int {
      return cardImages.count
    }
    cardStack.dataSource = self
  4. Conform to SwipeCardStackDelegate to subscribe to any of the following events:

     func cardStack(_ cardStack: SwipeCardStack, didSelectCardAt index: Int)
     func cardStack(_ cardStack: SwipeCardStack, didSwipeCardAt index: Int, with direction: SwipeDirection)
     func cardStack(_ cardStack: SwipeCardStack, didUndoCardAt index: Int, from direction: SwipeDirection)
     func didSwipeAllCards(_ cardStack: SwipeCardStack)

    Note: didSwipeCardAt and didSwipeAllCards are called regardless if a card is swiped programmatically or by the user.

Card Stack Actions

The following methods are available on SwipeCardStack.

Swipe

Performs a swipe programmatically in the given direction.

func swipe(_ direction: SwipeDirection, animated: Bool)

Shift

Shifts the card stack's cards by the given distance. Any swiped cards are skipped over.

func shift(withDistance distance: Int = 1, animated: Bool)

Undo

Returns the most recently swiped card to the top of the card stack.

func undoLastSwipe(animated: Bool)

Card Layout

Each SwipeCard consists of three UI components: its content, footer, and overlay(s).

Content

The content is the card's primary view. You can include your own card template here.

var content: UIView? { get set }

Footer

The footer is an axuilliary view on the bottom of the card. It is laid out above the content in the view hierarchy if the footer is transparent. Otherwise, the footer is drawn just below the content.

var footer: UIView? { get set }
var footerHeight: CGFloat { get set }

Overlays

An overlay is a view whose alpha value reacts to the user's dragging. The overlays are laid out above the footer, regardless if the footer is transparent or not.

func overlay(forDirection direction: SwipeDirection) -> UIView?
func setOverlay(_ overlay: UIView?, forDirection direction: SwipeDirection)
func setOverlays(_ overlays: [SwipeDirection: UIView])

Advanced Usage

For more advanced usage, including

visit the document here.

Installation

CocoaPods

Shuffle is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod 'Shuffle-iOS'

The import statement in this case will be

import Shuffle_iOS

Carthage

Shuffle is available through Carthage. To install it, simply add the following line to your Cartfile:

github "mac-gallagher/Shuffle"

Swift Package Manager

Shuffle is available through Swift PM. To install it, simply add the package as a dependency in Package.swift:

dependencies: [
  .package(url: "https://github.com/mac-gallagher/Shuffle.git", from: "0.1.0"),
]

Manual

Download and drop the Sources directory into your project.

Requirements

  • iOS 9.0+
  • Xcode 10.2+
  • Swift 5.0+

Apps Using Shuffle

We love to hear about apps that use Shuffle - feel free to submit a pull request and share yours here!


Made with ❤️ by Mac Gallagher

Comments
  • Only the ImageView for the last card in the array shows but all other info is displayed correctly

    Only the ImageView for the last card in the array shows but all other info is displayed correctly

    Hey I'm trying to integrate the Spotify API into the shuffle library by fetching the song name, artist name, and music artwork and displaying it on the cards. For some reason it only shows the artwork for the last card in the array but shows the correct song name and artist name for all the other cards.

    func fetchAndConfigureSearch() {
            
            let randomOffset = Int.random(in: 0..<1000)
            
            let token = (UserDefaults.standard.string(forKey: "token"))
            
            api.call(request: .search(token: token!, q: getRandomSearch(), type: .track, market: "US", limit: 5, offset: randomOffset) { [self] result in
                
                let tracks = result as? Result<SearchTracks, Error>
                
                switch tracks {
                
                case .success(let something):
                    
                    for track in something.tracks.items {
                        
                        let newTrack = SimpleTrack(artistName: track.album.artists.first?.name,
                                                   id: track.id,
                                                   title: track.name,
                                                   previewURL: track.previewUrl,
                                                   images: track.album.images!,
                                                   albumName: track.album.name)
                        
                        let coverImageURL = newTrack.images[0].url
                        self.imageView.kf.setImage(with: coverImageURL)
                                            
                        let songModel = CardModel(songName: newTrack.title, artistName: newTrack.artistName, imageView: imageView)
                        cardModels.append(songModel)
                        let newIndices = Array(self.cardModels.count-1..<self.cardModels.count)
                        self.cardStack.appendCards(atIndices: newIndices)
                    }
                case .failure(let error):
                    print("search query failed bc... ", error)
                case .none:
                    print("not decoding correctly")
                }
            })   
        }
    

    https://user-images.githubusercontent.com/38578011/110167101-45519000-7dc3-11eb-901b-34dad5ba220e.mp4

    bug 
    opened by timmyvc123 23
  • Won't compile with SPM

    Won't compile with SPM

    Describe the bug I imported the latest Shuffle framework with the Swift Package Manager and I get several errors like "Use of undeclared type 'CGRect'" or "Use of undeclared type 'CGFloat'", which makes sense since CGStuff is in CoreGraphics, not in Foundation. Am I missing something with the SPM ? Thanks for your help ;-)

    To Reproduce Add the dependency for Shuffle and compile

    Expected behavior The code should compile

    Screenshots Uploading Capture d’écran 2020-03-25 à 15.15.01.png…

    Additional context Add any other context about the problem here.

    bug 
    opened by jerometonnelier 8
  • Make some extra space at the top

    Make some extra space at the top

    Hi all, first I want to thank you very much for that really great work! I am currently struggling with the layout of the card + footer. What I want to do is to add some additional buttons at the top of the view. But to do this I have to change the height of the picture to make some extra space available at the top. Unfortunately I am unable to find the right parameter to handle this without changing additional things like footer height, picture width.. Can anyone help me ? Thanks!

    help wanted 
    opened by LoopingLouieX 6
  • Using An Infinite Number of Cards

    Using An Infinite Number of Cards

    Is there a way to make an unlimited swipe stack, as Tinder does? Can the number of cards be updated dynamically?


    I've tried returning Int.max in numberOfCards, and for testing, I do a modulus on the cardForIndexAt parameter as such: return MyCard(image: cardImages[index % numImages])

    This doesn't work out as it seems to load all images in the stack, so the application just crashes when trying to load a practically infinite number of UIViews.

    enhancement 
    opened by CJxD 6
  • Set Up Read The Docs with Sphinx

    Set Up Read The Docs with Sphinx

    First step is to just set up Read The Docs using whatever documentation already exists in the project. Adding more coverage will come later!

    @purcellconsult is undertaking this project

    documentation enhancement 
    opened by mac-gallagher 6
  • Question

    Question

    Hi there! Thanks for making Shuffle!

    I'm having an issue related to https://github.com/mac-gallagher/Shuffle/issues/13. I'm trying to periodically add to content in the SwipeCardStack. I'm having to do some fancy stuff with updating the datasource.

    Anyways my problem is more specific. If I'm swiping a card and then call reloadData() the animations conflict and the cards get stuck in an isUserInteractionEnabled == false state. It looks like special care has been taken to disable isUserInteractionEnabled for every swipe. Were you experiencing specific issues when not specifically disabling isUserInteractionEnabled?

    func card(didSwipe card: SwipeCard, with direction: SwipeDirection, forced: Bool) {
            delegate?.cardStack?(self, didSwipeCardAt: topCardIndex, with: direction)
            ...
            isUserInteractionEnabled = false
            animator.swipe(self, topCard: card, direction: direction, forced: forced)
        }
    

    Please let me know! Thanks,

    • Nick
    opened by nservidio 5
  • Using Shuffle in UITableView/UICollectionView

    Using Shuffle in UITableView/UICollectionView

    Describe the bug TableView and CollectionView don't scroll vertically if the shuffle is in any cell.

    To Reproduce Steps to reproduce the behavior: Add SwipeCardStack inside TableViewCell/CollectionViewCell and scroll tableView vertically.

    Expected behavior If I chose to move horizontally, Shuffle don't be able to scroll vertically.

    bug 
    opened by andre991 4
  • deleteCards crashes with fatal error

    deleteCards crashes with fatal error

    App Crash with deleteCards function If you use the func deleteCards(atPositions positions: [Int]) an fatal error appears:

    Fatal error: Invalid update: invalid number of cards. The number of cards contained in the card stack after the update (7) must be equal to the number of cards contained in the card stack before the update (7), plus or minus the number of cards inserted or deleted (0 inserted, 1 deleted): file Shuffle-master/Sources/Shuffle/SwipeCardStack/SwipeCardStack.swift, line 430 2020-08-21 20:56:36.170087+0200 ShuffleExample[28610:461963] Fatal error: Invalid update: invalid number of cards. The number of cards contained in the card stack after the update (7) must be equal to the number of cards contained in the card stack before the update (7), plus or minus the number of cards inserted or deleted (0 inserted, 1 deleted): file Shuffle-master/Sources/Shuffle/SwipeCardStack/SwipeCardStack.swift, line 430 (lldb)

    Reproducing Steps

    1. Go to "TinderViewController.swift in the Demo App
    2. Add cardStack.deleteCards(atPositions: [3]) in a didTapButton action (i.e. swipe right)
    3. Crash!

    What I expected The cardModel at Position [3] will be deleted from the cardStack.

    bug 
    opened by LoopingLouieX 3
  • Header not precised in the README

    Header not precised in the README

    I downloaded the library. I tried to use it but Xcode did not recognized SwipeCard. It was not written on the README: please let the users know they need to import Shuffle_iOS in order to use the library

    Thank you

    enhancement 
    opened by Anybowdy 3
  • Do not dispose cards, instead shift on swipe

    Do not dispose cards, instead shift on swipe

    Is your feature request related to a problem? Please describe. I would like to do something like memorise the order of cards. Therefore it would be good to disable disposal and just send them to the back.

    Describe the solution you'd like An option to disable the disposal.

    Describe alternatives you've considered Re-adding the card when getting notified to is has been swiped out. But this will break the undo. and make the handling somehow wired.

    enhancement 
    opened by m42e 3
  • Stop embedding Swift standard libraries

    Stop embedding Swift standard libraries

    When i want to use Shuffle in my project whose dependency manager is Carthage, Transporter Error is occurred.

    [13:56:23]: [Transporter Error Output]: ERROR ITMS-90206: "Invalid Bundle. The bundle at 'MyProject.app/Frameworks/Shuffle.framework' contains disallowed file 'Frameworks'."
    [13:56:23]: Transporter transfer failed.
    [13:56:23]:
    [13:56:23]: ERROR ITMS-90206: "Invalid Bundle. The bundle at 'MyProject.app/Frameworks/Shuffle.framework' contains disallowed file 'Frameworks'."
    

    So I changed Always Embed Swift Standard Libraries option to No. Then problem solved!

    bug 
    opened by pilgwon 3
  • Getting this error

    Getting this error "Dependency "Shuffle" has no shared framework schemes" while carthage update.

    Skipped building Shuffle due to the error: Dependency "Shuffle" has no shared framework schemes. Carthage update/build is not able to generate xcframework.

    opened by vijendrakr 0
  • The top card is stuck when swiping the card quickly

    The top card is stuck when swiping the card quickly

    Describe the bug In the relatively fast process of swiping the card, if the network is returning data to add, there is a high probability that the current sliding card is stuck at the top level, and the remaining cards can still continue to slide.

    Another problem is that after the network data is added, click left, right, up, and the sliding card will be invalid. There is no response, that is, the following function is called:

     cardStack.swipe(.left, animated: true)
    
     cardStack.swipe(.right, animated: true)
    
     cardStack.swipe(.up, animated: true)
    

    https://user-images.githubusercontent.com/14361443/148019651-3d5e82a4-6702-4320-8e00-715ec6fd10c3.mp4

    bug 
    opened by Kejiasir 1
  • Is there any way to show background card little bit?

    Is there any way to show background card little bit?

    Is your feature request related to a problem? Please describe. A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

    Describe the solution you'd like A clear and concise description of what you want to happen.

    Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.

    Additional context Add any other context or screenshots about the feature request here.

    enhancement 
    opened by karthikeyan1425 0
  • Is there any way to generate or create deck shadows?

    Is there any way to generate or create deck shadows?

    hey Man,

    Thanks for building such a great lib, I am using it in my app and it works just amazing.

    I have a question about a shadow of the deck, is there any way to create a shadow-like it's attached on the screenshot with this lib? Screen Shot 2021-06-29 at 6 55 41 PM

    enhancement 
    opened by anurag1991 0
  • Image doesn't display

    Image doesn't display

    Hi!I am working with tmdb API and want to display some movies on a cards depending on a query(For now I try to display only one card in order to test it first).I followed the instructions on main page of this repo, and everything is fine description is displayed and I can swipe this View, except image doesn't display.Please help me if you can.Thank you! ` import UIKit import Shuffle_iOS

    class ResultViewController: UIViewController { let cardStack = SwipeCardStack()

    var cardImages = [UIImage](){didSet {
        DispatchQueue.main.async {
            self.cardStack.reloadData()
        }
    }}
    
    
    
    var name:String = ""
    var releaseDate:String = ""
    var popularity:Double = 0.0
    var voteAverage:Double = 0.0
    var budget:Int = 0
    var revenue:Int = 0
    var overview:String = ""
    
    var url:URL!
    var image:UIImage!
    
    
    
    
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var ratingLabel: UILabel!
    @IBOutlet weak var budgetLabel: UILabel!
    @IBOutlet weak var overviewLabel: UILabel!
    
    override func viewDidLoad() {
    
        super.viewDidLoad()
        view.addSubview(cardStack)
        cardStack.frame = view.safeAreaLayoutGuide.layoutFrame
        cardStack.dataSource = self
    }
    override func viewWillAppear(_ animated: Bool) {
        if url != nil{
            downloadImage(from: url)
            DispatchQueue.main.async {
                self.titleLabel.text = self.name + "(" + self.releaseDate + ")"
                self.ratingLabel.text = String(self.popularity) + "     " + String(self.voteAverage)
                self.budgetLabel.text = "Budget: " + String(self.budget) + "Revenue: " + String(self.revenue)
                self.overviewLabel.text = self.overview
            }
        }
    }
    

    } extension ResultViewController{ func getData(from url: URL, completion: @escaping (Data?, URLResponse?, Error?) -> ()) { URLSession.shared.dataTask(with: url, completionHandler: completion).resume() } func downloadImage(from url: URL) { getData(from: url) { data, response, error in guard let data = data, error == nil else { return } DispatchQueue.main.async() { let image = UIImage(data: data) if image != nil{ self.cardImages.append(image!) print(self.cardImages)

                }
            }
        }
    }
    

    } extension ResultViewController:SwipeCardStackDataSource{ func cardStack(_ cardStack: SwipeCardStack, cardForIndexAt index: Int) -> SwipeCard { return card(fromImage: cardImages[index]) }

    func numberOfCards(in cardStack: SwipeCardStack) -> Int {
        cardImages.count
    }
    func card(fromImage image: UIImage) -> SwipeCard {
      let card = SwipeCard()
      card.swipeDirections = [.left, .right]
      card.content = UIImageView(image: image)
      
      let leftOverlay = UIView()
      leftOverlay.backgroundColor = .green
      
      let rightOverlay = UIView()
      rightOverlay.backgroundColor = .red
      
      card.setOverlays([.left: leftOverlay, .right: rightOverlay])
      
      return card
    }
    

    } `

    opened by andreimarkoff 0
Owner
Mac Gallagher
📱 iOS Developer at @linkedin. Previously at @Ford
Mac Gallagher
🃏 Tinder like card interface

Features Swift 3 Custom views for the card & overlay Generic Dynamically add new cards on top or on the bottom Lazy view loading Setup pod 'DMSwipeCar

Dylan Marriott 250 Nov 15, 2022
IOS Card Game - A simple card game using SwiftUI

IOS_Card_Game A simple card game using Swift UI.

Md. Masum Musfique 1 Mar 25, 2022
A marriage between the Shazam Discover UI and Tinder, built with UICollectionView in Swift.

VerticalCardSwiper A marriage between the Shazam Discover UI and Tinder, built with UICollectionView in Swift. Project goal and information The goal o

Joni Van Roost 1.2k Dec 28, 2022
A easy-to-use SwiftUI view for Tinder like cards on iOS, macOS & watchOS.

?? CardStack A easy-to-use SwiftUI view for Tinder like cards on iOS, macOS & watchOS. Installation Xcode 11 & Swift Package Manager Use the package r

Deniz Adalar 285 Jan 3, 2023
Swipe to "like" or "dislike" any view, just like Tinder.app. Build a flashcard app, a photo viewer, and more, in minutes, not hours!

MDCSwipeToChoose Swipe to "like" or "dislike" any view, just like Tinder.app. Build a flashcard app, a photo viewer, and more, in minutes, not hours!

Brian Gesiak 2.6k Nov 29, 2022
KolodaView is a class designed to simplify the implementation of Tinder like cards on iOS.

KolodaView Check this article on our blog. Purpose KolodaView is a class designed to simplify the implementation of Tinder like cards on iOS. It adds

Yalantis 5.2k Jan 2, 2023
An iOS library to create beautiful card transitions.

CSCardTransition CSCardTransition is a small library allowing you to create wonderful push and pop transition animations like in the App Store. It wor

Creastel 5 Jan 14, 2022
A reactive, card-based UI framework built on UIKit for iOS developers.

CardParts - made with ❤️ by Intuit: Example Requirements Installation Communication & Contribution Overview Quick Start Architecture CardsViewControll

Intuit 2.5k Jan 4, 2023
A SwiftUI based custom sheet card to show information in iOS application.

A SwiftUI based custom sheet card to show any custom view inside the card in iOS application.

Mahmud Ahsan 4 Mar 28, 2022
A SwiftUI card view, made great for setup interactions.

SlideOverCard A SwiftUI card design, similar to the one used by Apple in HomeKit, AirPods, Apple Card and AirTag setup, NFC scanning, Wi-Fi password s

João Gabriel 716 Dec 29, 2022
This UI attempts to capture the Quibi Card Stack and the associated User Interaction.

RGStack This UI attempts to capture the Quibi Card Stack and the associated User Interaction. Required A View that conforms to the ConfigurableCard pr

RGeleta 96 Dec 18, 2022
Card-based view controller for apps that display content cards with accompanying maps, similar to Apple Maps.

TripGo Card View Controller This is a repo for providing the card-based design for TripGo as well as the TripKitUI SDK by SkedGo. Specs 1. Basic funct

SkedGo 6 Oct 15, 2022
Card flip animation by pan gesture.

CardAnimation Design from Dribble. 实现思路在这里。 Two Solutions At the begin, I didn't encapsulate code, @luxorules refactor code into class and improve it

null 1.2k Dec 14, 2022
Awesome looking Dial like card selection ViewController

KVCardSelectionVC Awesome looking Dial like card selection ViewController An updated Swift 3 working version of : https://github.com/atljeremy/JFCardS

Kunal Verma 23 Feb 1, 2021
Cusom CollectionView card layout

MMCardView Example To run the example project, clone the repo, and run pod install from the Example directory first. Demo 1.Card Requirements iOS 8.0+

Millman Yang 556 Dec 5, 2022
:star: Custom card-designed CollectionView layout

CardsLayout CardsLayout is a lightweight Collection Layout. Installation CocoaPods You can use CocoaPods to install CardsLayout by adding it to your P

Filipp Fediakov 798 Dec 28, 2022
SimpleCardView-SwiftUI is a very simple card view written with SwiftUI

SimpleCardView-SwiftUI is a very simple card view written with SwiftUI

Tomortec 3 May 19, 2022
GLScratchCard - Scratch card effect

I loved the way payments app's like Google pay and PhonePe used scratch card option to reward it's user. Hence with ?? cloned the same scratch card effect for you guys out there

Gokul 84 Dec 5, 2022
A PageView Swiping to the left will go to previous page and swiping to the right will go to next page

PageView This package creates a PageView. Swiping to the left will go to previous page and swiping to the right will go to next page. You can find how

null 0 Oct 20, 2021
Swipe able, customizable card stack view, Tinder like card stack view based on UICollectionView. Cards UI

Swipable, customizable card stack view, Tinder like card stack view based on UICollectionView. Cards UI Сocoapods installation Add in your Podfile: po

Indy 850 Nov 17, 2022