dadalar / SwiftUI-CardStackView

A easy-to-use SwiftUI view for Tinder like cards on iOS, macOS & watchOS.
MIT License
488 stars 53 forks source link

Cards going to the left #7

Open Mcrich23 opened 2 years ago

Mcrich23 commented 2 years ago

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!!!