A marriage between the Shazam Discover UI and Tinder, built with UICollectionView in Swift.

Overview

VerticalCardSwiper

A marriage between the Shazam Discover UI and Tinder, built with UICollectionView in Swift.


example

Project goal and information

The goal of this project is to recreate the Discover UI in Shazam (which I think is a great, fun way to display content) in combination with a Tinder style of swiping cards to the left/right. The idea behind this is that in some cases, you don't want to swipe away cards, but keep them available for later on. This implementation allows for that. And it's a fun way to interact with content.

It's built with a UICollectionView and a custom flowLayout.

Requirements

  • iOS 9.0+
  • Swift 5

Installation

CocoaPods

To install with CocoaPods, simply add the following line to your Podfile:

pod 'VerticalCardSwiper'

Carthage

To install with Carthage, simply add the following line to your Podfile:

github "JoniVR/VerticalCardSwiper"

Example

To try out VerticalCardSwiper

pod try VerticalCardSwiper

or open the project and run the Example.

Usage

VerticalCardSwiper behaves a lot like a standard UICollectionView. To use it inside your UIViewController:

import VerticalCardSwiper

class ExampleViewController: UIViewController, VerticalCardSwiperDatasource {
    
    private var cardSwiper: VerticalCardSwiper!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        cardSwiper = VerticalCardSwiper(frame: self.view.bounds)
        view.addSubview(cardSwiper)
        
        cardSwiper.datasource = self
        
        // register cardcell for storyboard use
        cardSwiper.register(nib: UINib(nibName: "ExampleCell", bundle: nil), forCellWithReuseIdentifier: "ExampleCell")
    }
    
    func cardForItemAt(verticalCardSwiperView: VerticalCardSwiperView, cardForItemAt index: Int) -> CardCell {
        
        if let cardCell = verticalCardSwiperView.dequeueReusableCell(withReuseIdentifier: "ExampleCell", for: index) as? ExampleCardCell {
            return cardCell
        }
        return CardCell()
    }
    
    func numberOfCards(verticalCardSwiperView: VerticalCardSwiperView) -> Int {
        return 100
    }
}

Properties

/// Indicates if side swiping on cards is enabled. Set to false if you don't want side swiping. Default is `true`.
@IBInspectable public var isSideSwipingEnabled: Bool = true
/// Allows you to enable/disable the stacking effect. Default is `true` (enabled).
@IBInspectable public var isStackingEnabled: Bool = true
/// The transform animation that is shown on the top card when scrolling through the cards. Default is 0.05.
@IBInspectable public var firstItemTransform: CGFloat = 0.05
/// The inset (spacing) at the top for the cards. Default is 40.
@IBInspectable public var topInset: CGFloat = 40
/// The inset (spacing) at each side of the cards. Default is 20.
@IBInspectable public var sideInset: CGFloat = 20
/// Sets how much of the next card should be visible. Default is 50.
@IBInspectable public var visibleNextCardHeight: CGFloat = 50
/// Vertical spacing between the focussed card and the bottom (next) card. Default is 40.
@IBInspectable public var cardSpacing: CGFloat = 40
/// Allows you to set the view to Stack at the Top or at the Bottom. Default is true.
@IBInspectable public var isStackOnBottom: Bool = true
/// Sets how many cards of the stack are visible in the background
@IBInspectable public var stackedCardsCount: Int = 1
/** 
 Returns an array of indexes (as Int) that are currently visible in the `VerticalCardSwiperView`.
 This includes cards that are stacked (behind the focussed card).
*/
public var indexesForVisibleCards: [Int]

Other

Just like with a regular UICollectionView, you can reload the data by calling
cardSwiper.reloadData()
Get the current focussed card index
cardSwiper.focussedCardIndex
Scroll to a specifc card by calling
cardSwiper.scrollToCard(at: Int, animated: Bool) -> Bool
Get a card at a specified index
cardSwiper.cardForItem(at: Int) -> CardCell?
Swipe a card away programatically
cardSwiper.swipeCardAwayProgrammatically(at: Int, to: SwipeDirection, withDuration: TimeInterval = 0.3) -> Bool
Moving/Deleting/Inserting cards at runtime

Make sure to update your datasource first, otherwise an error will occur.

cardSwiper.moveCard(at: Int, to: Int)
cardSwiper.deleteCards(at: [Int])
cardSwiper.insertCards(at: [Int])

Delegation

To handle swipe gestures, implement the VerticalCardSwiperDelegate.

class ViewController: UIViewController, VerticalCardSwiperDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        cardSwiper.delegate = self
    }
    
    func willSwipeCardAway(card: CardCell, index: Int, swipeDirection: SwipeDirection) {
    
        // called right before the card animates off the screen (optional).
    }

    func didSwipeCardAway(card: CardCell, index: Int, swipeDirection: SwipeDirection) {

        // handle swipe gestures (optional).
    }
    
    func didCancelSwipe(card: CardCell, index: Int) {
        
        // Called when a card swipe is cancelled (when the threshold wasn't reached)
    }
    
    func sizeForItem(verticalCardSwiperView: VerticalCardSwiperView, index: Int) -> CGSize {
    
        // Allows you to return custom card sizes (optional).
        return CGSize(width: verticalCardSwiperView.frame.width * 0.75, height: verticalCardSwiperView.frame.height * 0.75)
    }
    
    func didScroll(verticalCardSwiperView: VerticalCardSwiperView) {
    
        // Tells the delegate when the user scrolls through the cards (optional).
    }
    
    func didEndScroll(verticalCardSwiperView: VerticalCardSwiperView) {
    
        // Tells the delegate when scrolling through the cards came to an end (optional).
    }
    
    func didDragCard(card: CardCell, index: Int, swipeDirection: SwipeDirection) {
    
        // Called when the user starts dragging a card to the side (optional).
    }
    
    func didTapCard(verticalCardSwiperView: VerticalCardSwiperView, index: Int) {
    
        // Tells the delegate when the user taps a card (optional).
    }
    
    func didHoldCard(verticalCardSwiperView: VerticalCardSwiperView, index: Int, state: UIGestureRecognizer.State) {
    
        // Tells the delegate when the user holds a card (optional).
    }
}

Customization

Subclass the CardCell to customize the cards.

class ExampleCardCell: CardCell {

}

Key Features

  • Shazam Discover UI with paging
  • Tinder-style swiping
  • Option to disable side swiping
  • Set custom number of stacked cards
  • Code documentation in README.md file
  • Cocoapods support
  • Carthage support
  • SPM support
  • Diff support

Author

Joni Van Roost, [email protected]

License

VerticalCardSwiper is available under the MIT license. See the LICENSE file for more info.

More

Feel free to submit a pull request, open an issue or fork this project. Any help is always appreciated. A big thank you to all the contributors!

Comments
  • Shazam effect

    Shazam effect

    I cloned the project in the attempt to include the stack effect for the vertical collection as in Shazam. I managed to do so except the fact that sometimes the stack displays two cards behind, sometimes three. Did you came across this problem when trying to implement this effect ? I think the problem is the third cell (which should be displayed in the background always when the indexPath is greater or equal to 2) sometimes gets deallocated, sometimes it doesn't. Do you have any guess on this ?

    Thank you.

    bug enhancement 
    opened by VladIacobIonut 12
  • Feature/visible background cells

    Feature/visible background cells

    Checklist

    • [x] I've tested my changes.
    • [x] I've read the Contribution Guidelines.
    • [x] I've updated the documentation if necessary.
    • [x] I've added additional Tests where/if it makes sense.

    Motivation and Context

    Needed this feature for a own project an saw the help wanted on this feature in the issue tab

    closes #40

    tried it with different values from 1 to 10 cards on every possible device size

    Description

    removed this part to enable top stacking cards. if cvMinY > cardMinY + cardHeight + minimumLineSpacing + collectionView.contentInset.top { cvMinY = 0 }

    • added bool to set if the stacking should be on top or at the bottom

    • added int for the amout of cards shown in the background.

    • defining an newRect so cards in the background won't be removed from the collectionView

    let y = collectionView.bounds.minY - cellHeight * CGFloat(topStackCount) let newRect = CGRect(x: 0, y: y < 0 ? 0 : y, width: collectionView.bounds.maxX, height: collectionView.bounds.maxY) let items = NSArray(array: super.layoutAttributesForElements(in: newRect)!, copyItems: true)

    i am very open for improvements because this is kind of a quick shot.

    enhancement 
    opened by stfnhdr 9
  • IndexPath.row

    IndexPath.row "3" Issues

    Hello, thanks for sharing this library. Exactly what I was looking for. I found one problem which I can't explain its behaviour and where is it coming from. I need to use the index of the visible cell to save it as a progress, so next time the user loads the collection view with the specific theme, it can scroll to index where it was left last time.

    It's straight forward implementation, but the issue is, for whatever reason, the count always ignores index "3" and it goes like this: 1, 2, 2, 4, 5, .... even swiping backwards ...5, 4, 2, 2, 1

    Home.swift:

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
            let visibleIndexPath = collectionView.indexPathsForVisibleItems
            let lastCardIndex = visibleIndexPath[0].row + 1
            print("visible index: \(lastCardIndex)")
        }
    

    Where do I look within the code to tackle this?

    Also, the very last card has an offset on Y axe. I couldn't figure out where to disable it. I'd like to animate the last card the same way and keep it in centre of the view as the other cards. The idea is to:

    • display "Done" statement
    • assign same BG colour as a view, so it looks it disappeared into it after the scroll animation finishes
    • disable user interaction with bool

    Thank you for your suggestions

    Kind regards

    A.

    bug 
    opened by alessign 8
  • Hiding previous card

    Hiding previous card

    Is there a way to hide a previous card after scrolling up. I have tried to implement isPreviousCardVisible: Bool = false both in code and in IB, but previous card is always visible

    Thanks!

    question 
    opened by VisvaldasG 7
  •  I have problems adding the library to my podfile

    I have problems adding the library to my podfile

    I have problems adding the library to my podfile

    Issue Description

    captura de pantalla 2018-11-18 a las 18 54 36

    I enter pod 'VerticalCardSwiper', '0.1.0-beta6' in my podfile and it gives me an error [!] Unable to find a specification for VerticalCardSwiper (= 0.1.0-beta6)

    Environment

    • iOS Version: [12.1]
    • Device(s): [mac mini]
    • Xcode Version: [10.1]
    opened by STAMFORD313 6
  • [Bug] `isStackOnBottom` and `stackedCardsCount` not working on 2.3.0

    [Bug] `isStackOnBottom` and `stackedCardsCount` not working on 2.3.0

    Bottom stacking won't work anymore with 2.3.0. I had to downgrade after wasting 3 hours not knowing what is going on. If someone else encounters this issue it is because of the new version. stackedCardsCount and isStackOnBottom have no effect whatsoever.

    bug 
    opened by IDontEatMeat 5
  • [Feature Request] Ability to set custom Swipe Percentage Margin

    [Feature Request] Ability to set custom Swipe Percentage Margin

    Now when I swipe 20-30% then card goes to left and right. But I want ratio of 5%. When I just swipe 5% then card should be goes to left and right. Where I need to change ?????

    enhancement discussion 
    opened by AmandeepTMSFalcon 5
  • [Question] How to check properly if CardCell subview is visible?

    [Question] How to check properly if CardCell subview is visible?

    Hi, I have a AVPlayerLayer in CardCell, and when I check different approaches like if the AVPlayerLayer is visible, I get true even if the CardCell is not on top of stack, but behind another card (ex. ImageCardCell).

    I would like to pause the video when CardCell goes back when scrolling, but I always get value true or a full height when I check in didEndScroll if the AVPlayerLayer is visible. Is there a way to check this?

    I hope my question was clear.

    Thanks for your help.

    invalid question 
    opened by sevgjan 5
  • [Question]  How to stop backwards scrolling?

    [Question] How to stop backwards scrolling?

    New Issue Checklist

    Question

    Is there a method for only scrolling down rather than allowing scrolling both ways? Disabling the last card just swiped to be visible again? Cheers

    question 
    opened by Harryjeffs 5
  • Reverse approach of cards stack?

    Reverse approach of cards stack?

    Is there a possibility to reverse the approach, like have a stack of cards and then either swipe left/right or just scroll card to bottom, and swipe up if you want to bring back the scrolled card?

    question 
    opened by sevgjan 5
  • CardCell animation glitch when changing alpha

    CardCell animation glitch when changing alpha

    I'm trying to animate the cardCell with a basic flip animation, with alpha change to show/hide the other view. This works perfectly with UICollectionView.

    https://github.com/hemo87/ExampleAnimation

    IMG_0989

    But the same code with this library just won't work, it refuses to do the alpha animation, as shown here.

    https://github.com/hemo87/ExampleWithVeticalCardSwiper

    IMG_0990

    Im assuming this is a bug as I'd expect it to still behave like UICollectionView?

    bug 
    opened by hemo87 5
  • How to scrollToCard horizontally?

    How to scrollToCard horizontally?

    Before opening the issue, have you...?

    • [X] Searched Google for a solution to my issue if applicable.

    Question

    Is it possible to make the scrolling horizontal when calling scrollToCard?

    question 
    opened by davidchea 0
  • [Question] Current and Next cell with different size.

    [Question] Current and Next cell with different size.

    New Issue Checklist

    Question

    So the whole view will always have 2 cell, one is the current focused one and another is next one. I want to current one have the screen_width. The next one have screen_width-30. And once I scroll the next one, it become the current one and become the screen_width. Is there a way to fullfill this?

    question 
    opened by o1xhack 0
  • [Bug] Losing cards from stack when device orientation is rotated

    [Bug] Losing cards from stack when device orientation is rotated

    New Issue Checklist

    Bug Description

    Changing device orientation to landscape removes some cards from view

    To Reproduce

    Steps to reproduce the behavior:

    1. Create more than one card for testing (eg 8), using normal methods.
    2. Use numbers in sequential labelling visible on cards
    3. Allow view to rotate to landscape orientation
    4. Notice that only even numbered labels are visible

    Expected behavior

    Not expecting views to resize to landscape view (another issue, another day), but expecting to see all available cards in stack. Looking at card No 1 reverts to card No 2 when rotated to landscape. Looking at card No 3 reverts to No 6 when rotated.

    Environment

    • iOS Version: [13.7]
    • Device(s): [iPhone X]
    • Xcode Version: [11.7]
    bug 
    opened by Proudspark 7
  • [Bug] Adding multiple card sizes doesn't snap card correctly

    [Bug] Adding multiple card sizes doesn't snap card correctly

    Bug Description

    Card Swiper doesn't snap card swipe correctly when we provide different sizes for cards using

    func sizeForItem(verticalCardSwiperView: VerticalCardSwiperView, index: Int)
    

    To Reproduce

    Steps to reproduce the behavior: Following steps are for ExampleViewController in project

    1. Update contactsDataSource to add size for each item e.g.
    private var contactsDemoData: [Contact] = [
            Contact(name: "John Doe", age: 33, size: 500),
            Contact(name: "Chuck Norris", age: 78, size: 500),
            Contact(name: "Bill Gates", age: 62, size: 500),
            Contact(name: "Steve Jobs", age: 56, size: 300),
            Contact(name: "Barack Obama", age: 56, size: 300),
            Contact(name: "Mila Kunis", age: 34, size: 300),
            Contact(name: "Pamela Anderson", age: 50, size: 300),
            Contact(name: "Christina Anguilera", age: 37, size: 300),
            Contact(name: "Ed Sheeran", age: 23, size: 300),
            Contact(name: "Jennifer Lopez", age: 45, size: 300),
            Contact(name: "Nicki Minaj", age: 31, size: 300),
            Contact(name: "Tim Cook", age: 57, size: 300),
            Contact(name: "Satya Nadella", age: 50, size: 300)
        ]
    
    1. Add custom size for cells using delegate method
    func sizeForItem(verticalCardSwiperView: VerticalCardSwiperView, index: Int) -> CGSize {
            let contact = contactsDemoData[index]
            return CGSize(width: self.view.bounds.size.width, height: CGFloat(contact.size))
    }
    
    1. Run project and notice, it takes size as expected, but swipe offset doesn't update based on cell size and snaps to wrong position on swipe up

    Expected behavior

    • Swipe offset should consider dynamic cell size and update position accordingly

    Video

    https://jmp.sh/KbBOXeR

    Environment

    iOS 13.6 - iPhone 11

    enhancement 
    opened by ankit01217 3
  • Expose `cellForItem` API

    Expose `cellForItem` API

    Currently I am using this repo for show and play video player in Cell and video is automatically play. But problem is that when I am going to another tab video is still player on ExampleCell.I want to stop video player so how can I access cell outside CardForItemAt method and handle my issue ?

    Thanks

    enhancement 
    opened by kishancm 4
Owner
Joni Van Roost
Software Engineer, Tech enthusiast. Details matter.
Joni Van Roost
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
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
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
🃏 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
🔥 A multi-directional card swiping library inspired by Tinder

Made with ❤️ by Mac Gallagher Features ?? Advanced swipe recognition based on velocity and card position ?? Manual and programmatic actions ?? Smooth

Mac Gallagher 754 Dec 28, 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
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
🃏 Cardslider is a design UI controller that allows you to swipe through cards with pictures and accompanying descriptions.

CARD SLIDER UI controller that allows you to swipe through cards with pictures. We specialize in the designing and coding of custom UI for Mobile Apps

Ramotion 1.2k Dec 19, 2022
A swift SDK to help you scan debit/credit cards.

SKCardReader A swift SDK to help you scan debit/credit cards. Requirements To use the SDK the following requirements must be met: Xcode 11.0 or newer

Syed Kashan 4 Jul 29, 2022
Swift Package to download Transactions for LunchOnUs Cards

LunchOnUs Downloader What This is a small library to download transaction and balance data for LunchOnUs Cards (Giftcards by Eigen Development). How C

Steffen Kötte 0 Jan 15, 2022
Awesome iOS 11 appstore cards in swift 5.

Cards brings to Xcode the card views seen in the new iOS XI Appstore. Getting Started Storyboard Go to main.storyboard and add a blank UIView Open the

Paolo Cuscela 4.1k Dec 14, 2022
Alejandro Piguave 24 Dec 30, 2022
A simple Shazam Clone using ShazamKit, built with SwiftUI.

ShazamClone A simple Shazam clone using ShazamKit, built with SwiftUI. Requirements iOS 15 Xcode 13 Mac OS 11.3 or higher Tools ShazamKit: Lets you en

Emmanuel Kehinde 6 Dec 8, 2022
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
Shazam, out of your way

SLAM SLAM: an app I made in 24 hours that’s like Shazam, but out of your way. I used it as an excuse to play with ShazamKit, the new framework made by

Linus Skucas 8 Jan 24, 2022
Cool wave like transition between two or more UICollectionView

CKWaveCollectionViewTransition This is a cool custom transition between two or more UICollectionViewControllers with wave-like cell animation. Could b

Cezary Kopacz 1.9k Oct 4, 2022
Enhanced transitioning between UICollectionView layouts in iOS.

TLLayoutTransitioning Enhanced transitioning between UICollectionView layouts in iOS. ##Overview TLLayoutTransitioning provides a TLLayoutTransition t

SwiftKick Mobile 359 Oct 30, 2022
Discover recent and popular movies on iOS and Android

PopularMovies Description This application help users discover popular and recent movies using TMDb API. Android Installation Obtain an TMDb API Key.

Ivan Magda 15 Feb 10, 2022
Publish and discover services using Bonjour

Ciao Lib to publish and find services using mDNS Requirements Installation Usage License Requirements iOS 8.0+ / Mac OS X 10.10+ / tvOS 9.0+ Xcode 9.0

Alexandre Mantovani Tavares 55 Dec 14, 2022
A very simple library to discover and retrieve data from nearby devices (even if the peer app works at background).

Discovery: A simple library to discover and retrieve data from nearby devices. Discovery is a very simple but useful library for discovering nearby de

Ömer Faruk Gül 412 Dec 19, 2022