A easy-to-use SwiftUI view for Tinder like cards on iOS, macOS & watchOS.

Overview

πŸƒ CardStack

Swift Version License Platform

A easy-to-use SwiftUI view for Tinder like cards on iOS, macOS & watchOS.

Alt text

Installation

Xcode 11 & Swift Package Manager

Use the package repository URL in Xcode or SPM package.swift file: https://github.com/dadalar/SwiftUI-CardStackView.git

CocoaPods

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

pod "SwiftUICardStack"

Usage

The usage of this component is similar to SwiftUI's List. A basic implementation would be like this:

@State var cards: [Card] // This is the data to be shown in CardStack

CardStack(
  direction: LeftRight.direction, // See below for directions
  data: cards,
  onSwipe: { card, direction in // Closure to be called when a card is swiped.
    print("Swiped \(card) to \(direction)")
  },
  content: { card, direction, isOnTop in // View builder function
    CardView(card)
  }
)

Direction

CardStack needs to know which directions are available and how a swipe angle can be transformed into that direction. This is a conscious decision to make the component easily extendable while keeping type safety. The argument that needs to be passed to CardStack Initializer is a simple (Double) -> Direction? function. The Double input here is the angle in degrees where 0 points to up and 180 points to down. Direction is a generic type, that means users of this library can use their own types. Return nil from this function to indicate that that angle is not a valid direction (users won't be able to swipe to that direction).

There are the following predefined directions (LeftRight, FourDirections, EightDirections) and each of them define a direction(double:) function which can used in the CardStack Initializer. You can check the example project for a custom direction implementation.

Configuration

CardStack can be configured with SwiftUI's standard environment values. It can be directly set on the CardStack or an encapsulating view of it.

CardStack(
  // Initialize
)
.environment(\.cardStackConfiguration, CardStackConfiguration(
  maxVisibleCards: 3,
  swipeThreshold: 0.1,
  cardOffset: 40,
  cardScale: 0.2,
  animation: .linear
))

Use case: Appending items

It's really easy to load new data and append to the stack. Just make sure the data property is marked as @State and then you can append to the array. Please check the example project for a real case scenario.

struct AddingCards: View {
  @State var data: [Person] // Some initial data

  var body: some View {
    CardStack(
      direction: LeftRight.direction,
      data: data,
      onSwipe: { _, _ in },
      content: { person, _, _ in
        CardView(person: person)
      }
    )
    .navigationBarItems(trailing:
      Button(action: {
        self.data.append(contentsOf: [ /* some new data */ ])
      }) {
        Text("Append")
      }
    )
  }
}

Use case: Reload items

Since the component keeps an internal index of the current card, changing the order of the data or appending/removing items before the current item will break the component. If you want to replace the whole data, you need to force SwiftUI to reconstruct the component by changing the id of the component. Please check the example project for a real case scenario.

struct ReloadCards: View {
  @State var reloadToken = UUID()
  @State var data: [Person] = Person.mock.shuffled()

  var body: some View {
    CardStack(
      direction: LeftRight.direction,
      data: data,
      onSwipe: { _, _ in },
      content: { person, _, _ in
        CardView(person: person)
      }
    )
    .id(reloadToken)
    .navigationBarItems(trailing:
      Button(action: {
        self.reloadToken = UUID()
        self.data = Person.mock.shuffled()
      }) {
        Text("Reload")
      }
    )
  }
}

Author

Deniz Adalar, [email protected]

License

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

You might also like...
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

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

Presenting timelines as cards, single or bundled in scrollable feed!
Presenting timelines as cards, single or bundled in scrollable feed!

TimelineCards πŸƒ Autogenerated timelines presented as cards πŸƒ πŸƒ Single or bundled into feed πŸƒ Installation CocoaPods (wtf is that?) Add pod 'Timeli

πŸƒ Cardslider is a design UI controller that allows you to swipe through cards with pictures and accompanying descriptions.
πŸƒ 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

Presenting timelines as cards, single or bundled in scrollable feed!
Presenting timelines as cards, single or bundled in scrollable feed!

TimelineCards πŸƒ Autogenerated timelines presented as cards πŸƒ πŸƒ Single or bundled into feed πŸƒ Installation CocoaPods (wtf is that?) Add pod 'Timeli

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

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

Awesome looking Dial like card selection ViewController
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

A SwiftUI card view, made great for setup interactions.
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

A SwiftUI based custom sheet card to show information in iOS application.
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.

Comments
  • Allow simultaneous gesture

    Allow simultaneous gesture

    The current gesture modifier would get blocked when the child view has the same kind of gesture attached.

    https://developer.apple.com/documentation/swiftui/toggle/simultaneousgesture(_:including:)

    opened by ghost 4
  • Cards going to the left

    Cards going to the left

    Hey my cards are sticking to the left for some reason. How can I fix this? Example of issue

    (Looking at the Restaurant Stuff, ignore the media references)

    struct RestCardView: View {
        let restaurants: Restaurants
        @Environment(\.colorScheme) var colorScheme
        var body: some View {
            GeometryReader { geo in
                let card = (
                    VStack {
                        if #available(iOS 15, *) {
                            AsyncImage(url: restaurants.imageUrl) { image in
                                imag
    ![Simulator Screen Shot - iPhone 13 - 2022-03-09 at 11 48 24](https://user-images.githubusercontent.com/81453549/157522210-0892cbd6-6cc7-4d9f-9889-9a3ffdfe2ffe.png)
    e
                                    .resizable()
                                    .frame(idealWidth: geo.size.width, idealHeight: geo.size.height)//, maxHeight: 10)
                                    .aspectRatio(contentMode: .fit)
                            } placeholder: {
                                Image(systemName: "photo")
                                    .res
    ![Simulator Screen Shot - iPhone 13 - 2022-03-09 at 11 48 24](https://user-images.githubusercontent.com/81453549/157522269-6dee8139-720b-4384-9424-9105e3c5accb.png)
    izable()
    //                                .frame(height: geo.size.width)
                                    .aspectRatio(contentMode: .fit)
                                    .imageScale(.large)
                                    .foregroundColor(.gray)
                            }
                            .ignoresSafeArea()
    //                            switch image {
    //                            case .empty:
    //                                    Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            case .success(let image):
    //                                image
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            case .failure:
    //                                Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            @unknown default:
    //                                // Since the AsyncImagePhase enum isn't frozen,
    //                                // we need to add this currently unused fallback
    //                                // to handle any new cases that might be added
    //                                // in the future:
    //                                Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            }
    //                        }
                        }else {
                            URLImage(restaurants.imageUrl) { image in
                                image
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(idealHeight: geo.size.width)//, maxHeight: 50)
                                .clipped()
                            }
                        }
                        HStack{
                            Text(restaurants.name)
                            if restaurants.price != "Unknown" {
                                Text(restaurants.price)
                            }
                        }
                            .padding(.bottom)
                        if restaurants.category != [] {
                            Text("\(restaurants.category.joined(separator: ", "))")
                                .padding(.horizontal)
                        }
    //                    if !restaurants.isClosed {
    //                        Text("Open")
    //                            .foregroundColor(Color.green)
    //                    }else {
    //                        Text("Closed")
    //                            .foregroundColor(Color.red)
    //                    }
                            Button(action: {
                                print("opening site pressed")
                                if let url = URL(string: restaurants.url) {
                                    UIApplication.shared.open(url)
                                }
                            }) {
                                HStack {
                                    RestaurantReviews(reviews: restaurants.rating, forgroundColor: .yelpDefault, yelpIconSelectedSize: .large)
                                Text("\(restaurants.reviewCount)")
                                    .foregroundColor(.primary)
                                    .padding(.trailing)
                                Text("More Info")
                                Image("yelp logo")
                                    .resizable()
                                    .aspectRatio(contentMode: .fit)
                                    .frame(maxWidth: 30)
                                }
                        }
                            .padding(.horizontal)
                        Button(action: {
                            print("opening location pressed")
                            Utilities.encodeForGoogleMaps(url: restaurants.name) { loc in
                                print("loc = \(loc)")
                                if let url = URL(string: "https://www.google.com/maps/search/?api=1&query=\(loc)") {
                                    UIApplication.shared.open(url)
                                }
                            }
                        }, label: {
                            Text(restaurants.location)
                        })
                        .padding(.bottom)
                    })
                if colorScheme == .dark {
                    card
    //                .opacity(0.2)
                    .background(Color(red: 0.15, green: 0.15, blue: 0.15))
                    .cornerRadius(12)
                    .shadow(radius: 4)
                }else {//if colorScheme == .light {
                    card
                    .background(Color.white)
                    .cornerRadius(12)
                    .shadow(radius: 4)
                }
            }
        }
    }
    struct MediaCardView: View {
        let media: Media
        @Environment(\.colorScheme) var colorScheme
        var body: some View {
            GeometryReader { geo in
                let card = (
                    VStack {
                        if #available(iOS 15, *) {
                            AsyncImage(url: media.imageUrl) { image in
                                image
                                    .resizable()
                                    .frame(idealWidth: geo.size.width, idealHeight: geo.size.height)//, maxHeight: 10)
                                    .aspectRatio(contentMode: .fit)
                            } placeholder: {
                                Image(systemName: "photo")
                                    .resizable()
    //                                .frame(height: geo.size.width)
                                    .aspectRatio(contentMode: .fit)
                                    .imageScale(.large)
                                    .foregroundColor(.gray)
                            }
                            .ignoresSafeArea()
    //                            switch image {
    //                            case .empty:
    //                                    Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            case .success(let image):
    //                                image
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            case .failure:
    //                                Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            @unknown default:
    //                                // Since the AsyncImagePhase enum isn't frozen,
    //                                // we need to add this currently unused fallback
    //                                // to handle any new cases that might be added
    //                                // in the future:
    //                                Image(named: "No Image")
    //                                    .resizable()
    //                                    .aspectRatio(contentMode: .fit)
    //                                    .frame(height: geo.size.width)
    //                                    .clipped()
    //                            }
    //                        }
                        }else {
                            URLImage(media.imageUrl) { image in
                                image
                                .resizable()
                                .aspectRatio(contentMode: .fit)
                                .frame(idealHeight: geo.size.width)//, maxHeight: 50)
                                .clipped()
                            }
                        }
                        HStack{
                            Text(media.name)
                        }
    //                        .padding(.bottom)
                        let rating = "Avg. Rating: \(media.rating.rounded())/10 from \(media.ratingCount) reviews"
                        Text(rating)
                            .padding(.bottom)
                    Text(media.description)
                            .padding(.horizontal)
                    }
                        .padding(.bottom))
                    .onAppear {
                        print("Avg. Rating: \(media.rating)/10 from \(media.ratingCount) reviews")
                    }
                if colorScheme == .dark {
                    card
    //                .opacity(0.2)
                    .background(Color(red: 0.15, green: 0.15, blue: 0.15))
                    .cornerRadius(12)
                    .shadow(radius: 4)
                }else {//if colorScheme == .light {
                    card
                    .background(Color.white)
                    .cornerRadius(12)
                    .shadow(radius: 4)
                }
            }
        }
    }
    struct CardViewWithThumbs: View {
        let restaurants: Restaurants
        let media: Media
        let direction: LeftRight?
        static var yes = Array<Any>()
        
        var body: some View {
            ZStack(alignment: .topTrailing) {
                ZStack(alignment: .topLeading) {
                    if Utilities.picktType == .media {
                        MediaCardView(media: media)
                            .accessibilityIdentifier("Card")
                    }else {
                        RestCardView(restaurants: restaurants)
                            .accessibilityIdentifier("Card")
                    }
                    Image(systemName: "hand.thumbsup.fill")
                        .resizable()
                        .foregroundColor(Color.green)
                        .opacity(direction == .right ? 1 : 0)
                        .frame(width: 100, height: 100)
                        .padding()
                }
                Image(systemName: "hand.thumbsdown.fill")
                    .resizable()
                    .foregroundColor(Color.red)
                    .opacity(direction == .left ? 1 : 0)
                    .frame(width: 100, height: 100)
                    .padding()
            }
            .animation(.default)
        }
    }
    
    struct Thumbs: View {
    ...
        func showFinalRestView() {
            db.collection("parties").document(Utilities.code).addSnapshotListener { doc, error in
                print("Start data fetch...")
                if error == nil {
                    print("error = nil")
                    if doc != nil && doc!.exists {
                        print("Doc Exists")
                        if let num = doc!.get("devicesNum") as? Int {
                            devicesNum = num
                            if let dev = doc!.get("devices") as? Array<String> {
                                devices = dev
                                print("devices = \(dev)")
                                for name in dev {
                                    print("DeviceList = \(Devices.list)")
                                    if !Devices.list.contains(Devices(id: name, name: name)) {
                                        print("DeviceList Contains = \(name)")
                                        Devices.list.append(Devices(id: name, name: name))
                                    }
                                }
                                if let done = doc!.get("devicesDone") as? Array<String> {
                                    print("devicesDone = \(done), devices = \(devices)")
                                    devicesDone = done
                                    if done.sorted() == dev.sorted() && done.count == num {
                                        if var topController = UIApplication.shared.windows.first!.rootViewController {
                                            while let presentedViewController = topController.presentedViewController {
                                                topController = presentedViewController
                                            }
                                            self.fullScreenAd.showAd(root: topController, then: {
                                                //                                print("fetch data")
                                                //                                viewModels.fetchData {
                                                //                                        viewModels.fetchData { //yesRestaurant, AllRestaurants, yesMedia, allMedia, devices, devicesDone in
                                                //                                            vmRestAll = AllRestaurants
                                                //                                            vmRestYes = yesRestaurant
                                                //                                            vmDevices = devices
                                                //                                            vmMediaAll = allMedia
                                                //                                            vmMediaYes = yesMedia
                                                print("show final view")
                                                //                                    fullScreenAd.showAd {
                                                Utilities.showFinal = true
                                                DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
                                                    self.presentation.wrappedValue.dismiss()
                                                    results = false
                                                    topPick = false
                                                }
                                                print("yesRestaurants from restPicker = \(viewModels.yesRestaurants), allRestaurants = \(viewModels.allRestaurants)")
                                                print("done from completion handler")
                                                //                                    }
                                                //                                        }
                                            })
                                        }
                                    }
                                }else {
                                    print("error: \(error)")
                                }
                            }else {
                                print("error: \(error)")
                            }
                        }else {
                            print("error: \(error)")
                        }
                    }
                }
            }
        }
        func showfinalview() {
                print("show final view, yes.restlist = \(yes.restList), yes.medialist = \(yes.mediaList)")
                results = true
                topPick = true
        }
        func addRestData(card: Restaurants, direction: LeftRight) {
            if NetworkMonitor.shared.isConnected {
                if direction == .right {
                    itemCount += 1
                    print("Code = \(Utilities.code)")
                    print("Name = \(Utilities.name)")
                        yes.restList.append(Restaurants(name: card.name, imageUrl: card.imageUrl, id: card.id, rating: card.rating, url: card.url, location: card.location, category: card.category, tag: [], isClosed: card.isClosed, price: card.price, reviewCount: card.reviewCount))
                    print("yes, list = \(yes.restList)")
                }else {
                    print("no")
                    itemNo += 1
                    no.restList.append(Restaurants(name: card.name, imageUrl: card.imageUrl, id: card.id, rating: card.rating, url: card.url, location: card.location, category: card.category, tag: [], isClosed: card.isClosed, price: card.price, reviewCount: card.reviewCount))
                }
                if card.name == restData.last?.name {
                    showfinalview()
    //                            showFinal = true
                }
            }else {
                disabledAlert = true
            }
        }
    ...
        var body: some View {
            GeometryReader { geo in
    //            NavigationView {
                VStack(alignment: .center) {
                        Text(" ")
                            .padding(.bottom)
                        HStack {
                            Text("Party Code: \(Utilities.code)")
                                .padding(.trailing)
                            if Utilities.joinDisabled == false {
                                Button(action: {
                                    copy = true
                                    switch Utilities.shareMode {
                                    case .sheet:
    //                                    shareSheet.toggle()
                                        Utilities.presentShareSheet(activityItems: ["pickt://join/\(Utilities.code)"])
    //                                    let activityVC = UIActivityViewController(activityItems: ["pickt://join/\(Utilities.code)"], applicationActivities: nil)
    //                                    UIApplication.shared.windows.first?.rootViewController?.present(activityVC, animated: true, completion: nil)
                                    case .clipboard:
                                        let pasteboard = UIPasteboard.general
                                        pasteboard.string = "pickt://join/\(Utilities.code)"
                                    }
                                    DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
                                        copy = false
                                    }
                                }, label: {
                                    if !copy {
                                        Group {
                                            switch Utilities.shareMode {
                                            case .sheet:
                                                Image(systemName: "square.and.arrow.up")
                                            case .clipboard:
                                                Image(systemName: "doc.on.clipboard")
                                            }
                                        }
                                    }else {
                                        Image(systemName: "checkmark")
                                    }
                                })
                                    .foregroundColor(.primary)
                                    .frame(width: 35, height: 35)
                                    .overlay(
                                        RoundedRectangle(cornerRadius: 10)
                                        .stroke(Color.primary, lineWidth: 1)
                                    )
                            }
                        }
                        .padding(.top)
        //                Button("Stop Searching") {
        //                    if yes.list.count > 0 {
        //                       showfinalview()
        //                    }
        //                }
                    if restData == [] && Utilities.picktType == .restaurants {
                            Text("No Results Found For Query Terms:\n\nLocation: \(Utilities.location),\nRestaurant Filter: \(Utilities.restFilter),\nCuisine: \(Utilities.cuisine)")
                                .multilineTextAlignment(.center)
                                .padding(.top)
                        }else {
                            Group {
                                if Utilities.picktType == .media {
                                    if Utilities.mediaType == .TVShows {
                                        Text("Searching for TV Shows")
    //                                    Text("Everyone is seeing the same tv cards, please swipe right for yes and left for no.")
                                    }else if Utilities.mediaType == .Movies {
                                        Text("Searching for Movies")
    //                                    Text("Everyone is seeing the same movie cards, please swipe right for yes and left for no.")
                                    }
                                }else {
                                    Text("Searching \(Utilities.location) with \(Utilities.restFilter), \(Utilities.cuisine)")
    //                                    .padding(.horizontal)
    //                                Text("Everyone is seeing the same restaurant cards, please swipe right for yes and left for no.")
                                }
                            }
                            .padding()
                            .accessibility(identifier: "Searching")
                            .onTapGesture {
                                if UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT") {
                                    for item in Restaurants.viewModels {
                                        yes.restList.append(item)
                                    }
                                    self.showfinalview()
                                }
                            }
                            HStack {
                                Button(action: {
                                    var mediaList = Array<Media>()
                                    var restList = Array<Restaurants>()
                                    for item in no.mediaList {
                                        if !mediaList.contains(item) {
                                            mediaList.append(item)
                                        }
                                    }
                                    for item in no.restList {
                                        if !restList.contains(item) {
                                            restList.append(item)
                                        }
                                    }
                                    no.mediaList = mediaList
                                    no.restList = restList
                                    noList = true
                                }, label: {
                                    Image(systemName: "hand.thumbsdown")
                                    Text("\(itemNo)")
                                })
                                    .accessibility(identifier: "No List")
                                    .foregroundColor(.red)
                                Spacer()
                                    .frame(width: 40)
                                Button(action: {
                                    var mediaList = Array<Media>()
                                    var restList = Array<Restaurants>()
                                    for item in yes.mediaList {
                                        if !mediaList.contains(item) {
                                            mediaList.append(item)
                                        }
                                    }
                                    for item in yes.restList {
                                        if !restList.contains(item) {
                                            restList.append(item)
                                        }
                                    }
                                    yes.mediaList = mediaList
                                    yes.restList = restList
                                    yesList = true
                                }, label: {
                                    Image(systemName: "hand.thumbsup")
                                    Text("\(itemCount)")
                                })
                                    .accessibility(identifier: "Yes List")
                                .foregroundColor(.green)
                            }
                            .padding()
                        }
                        if results {
                            Group {
                                Text("\(waitingPhrases.randomElement()!)")
        //                        VStack {
        //                            Text("People Who Have Finished:\n")
        //                            ForEach(devicesDone.indices) {
        //                                Text(self.devicesDone[$0])
        //                            }
        //                        }
        //                        .padding()
                                Button("Override, Show Results") {
                                    overrideResults = true
                                }
                                Button {
                                    topPick = true
                                } label: {
                                    HStack {
                                        Image(systemName: "filemenu.and.selection")
                                        if pick != "None" {
                                            Text("Change your top pick")
                                        }else {
                                            Text("Choose your top pick")
                                        }
                                    }
                                }
    
                            }
                            .padding()
                        }
                    if !results && restData != [] && restData != [Restaurants(name: "nil", imageUrl: URL(string: Utilities.noImage)!, id: "abcdef", rating: 0, url: "https://google.com", location: "nil", category: ["nil"], tag: [], isClosed: false, price: "Unknown", reviewCount: 0)] && Utilities.picktType == .restaurants {
                                ZStack(alignment: Alignment(horizontal: .center, vertical: .center), content: {
                                    CardStack(
                                        direction: LeftRight.direction,
                                        data: restData,
                                        onSwipe: { card, direction in
                                            print("Swiped \(card.name) to \(direction)")
                                            addRestData(card: card, direction: direction)
                                        },
                                        content: { restaurants, direction, _ in
                                            CardViewWithThumbs(restaurants: restaurants, media: Media(name: "nil", imageUrl: URL(string: Utilities.noImage)!, id: "abcde", description: "abcde", ratingCount: 1, rating: 8, tag: []), direction: direction)
                                        }
                                    )
                                        .padding()
                                        .scaledToFit()
                                        .frame(alignment: .center)
    //                                .frame(maxWidth: screen.width)
                                    .environment(\.cardStackConfiguration, CardStackConfiguration(
                                      maxVisibleCards: 3
            //                          swipeThreshold: 0.1,
            //                            cardOffset: 10,
            //                            cardScale: 0.2,
            //                            animation: .linear
                                    ))
                                })
                    }else if Utilities.picktType == .media && mediaData != [] {
                        ZStack(alignment: Alignment(horizontal: .center, vertical: .center), content: {
                            CardStack(
                                direction: LeftRight.direction,
                                data: mediaData,
                                onSwipe: { card, direction in
                                    print("Swiped \(card.name) to \(direction)")
                                    addMediaData(card: card, direction: direction)
                                    if card.name == mediaData.last?.name {
                                        showfinalview()
            //                            showFinal = true
                                    }
                                },
                                content: { media, direction, _ in
                                    CardViewWithThumbs(restaurants: Restaurants(name: "nil", imageUrl: URL(string: Utilities.noImage)!, id: "abcdef", rating: 0, url: "https://google.com", location: "nil", category: ["nil"], tag: [], isClosed: false, price: "Unknown", reviewCount: 0), media: media, direction: direction)
                                }
                            )
                            .padding(.all)
    //                                .frame(height: screen.height)
    //                                .frame(minHeight: 0, idealHeight: screen.height, maxHeight: 450, alignment: .center)
                            .scaledToFit()
                            .frame(maxWidth: 700, maxHeight: 800)
    //                                .frame(maxWidth: screen.width)
                            .environment(\.cardStackConfiguration, CardStackConfiguration(
                              maxVisibleCards: 3
    //                          swipeThreshold: 0.1,
    //                            cardOffset: 10,
    //                            cardScale: 0.2,
    //                            animation: .linear
                            ))
                        })
                    }
                }
                ...
                .frame(maxWidth: .infinity, maxHeight: .infinity)
            }
        }
    }
    

    Thanks in advanced!!!

    opened by Mcrich23 0
  • The second card pass through the first one after swiping

    The second card pass through the first one after swiping

    Look closely, the second card pass through the first one after swiping, before it completely disappear. It looks / feels like the first card moved backwards and been drew out after the second card.

    20200915@105212@2x 20200915@105348@2x
    opened by ghost 3
  • Swipe left and right on button click

    Swipe left and right on button click

    Hi, Thanks for you help, How can I Swipe left and right on button click as when I click Like and Dislike button card should auto Swipe

    Your response will be helpful to me

    Thanks

    enhancement help wanted 
    opened by davy-devibharat 11
Releases(0.0.6)
Owner
Deniz Adalar
Deniz Adalar
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
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
πŸƒ 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 SwiftUI view that arranges its children in a whimsical interactive deck of cards, as seen in Big News

CardStack A SwiftUI view that arranges its children in a whimsical interactive deck of cards. CardStack mimics the behaviour of the photo stack in iMe

The Not So Big Company 74 Dec 13, 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 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 navigation controller that displays its view controllers as an interactive stack of cards.

CardNavigation The easiest way to turn a navigation controller into an interactive stack of cards. Highlights βœ… Fully interactive and interruptible βœ…

James Randolph 41 Sep 29, 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
Recreation of cards from Apple's AppStore written using SwiftUI.

App Store Cards Animation I tried to reproduce the look and the feeling of the cards from the AppStore. Please note that this repository is a work-in-

Roman 3 Mar 30, 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