From 7680aa0022e721d150e38cfeef89295c695f0e71 Mon Sep 17 00:00:00 2001 From: Badarinath Venkatnarayansetty Date: Wed, 31 Jan 2024 21:11:05 -0800 Subject: [PATCH 1/2] Passing ViewModel as part of the constructor --- Example/StackCardView/StackCardDemoView.swift | 43 +++++++++++++++++-- Sources/StackCardView.swift | 42 +++++++++++------- .../ViewModel/StackCardButtonViewModel.swift | 31 ++++++++----- 3 files changed, 87 insertions(+), 29 deletions(-) diff --git a/Example/StackCardView/StackCardDemoView.swift b/Example/StackCardView/StackCardDemoView.swift index 0f41a05..ddc972e 100644 --- a/Example/StackCardView/StackCardDemoView.swift +++ b/Example/StackCardView/StackCardDemoView.swift @@ -21,14 +21,15 @@ struct StackCardModel: StackCardModelProtocol, Identifiable { struct StackCardDemoView: View { - @State private var stackCardModels:[StackCardModel] = [] + @StateObject private var viewModel = StackCardViewModel() + @StateObject private var stackCardButtonPublisher = StackCardButtonPublisher() var body: some View { VStack { /// iterate over the list of cards ForEach(stackCardModels.reversed(), id: \.id) { card in - StackCard(model: card) { + StackCard(model: card, viewModel: viewModel, stackCardButtonPublisher: stackCardButtonPublisher) { // content Image(card.image) .resizable() @@ -42,12 +43,18 @@ struct StackCardDemoView: View { .onLeftSwipe { print("left Swipe \(card.id)") } + .onLeftButtonTap { + print("Left Button Tap \(card.id)") + } + .onRightButtonTap { + print("Right Button Tap \(card.id)") + } } .embedInZStack() .bind(model: stackCardModels) //empty State View - if stackCardModels.isEmpty { + if viewModel.displayingCards?.isEmpty ?? false { Text("Come back later we can find more matches for you!") .font(.caption) .foregroundColor(.gray) @@ -64,5 +71,35 @@ struct StackCardDemoView: View { StackCardModel(id: UUID().uuidString,name: "Park4", image: "park4") ] } + + + HStack { + Button(action: { + stackCardButtonPublisher.setDirection(direction: StackCardDirection.left) + }, label: { + Text("Keep Unread") + .foregroundColor(.white) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.black) + .cornerRadius(8) + + }) + + Spacer() + + Button { + stackCardButtonPublisher.setDirection(direction: StackCardDirection.right) + } label: { + Text("Mark As read") + .foregroundColor(.black) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.gray.opacity(0.2)) + .cornerRadius(8) + } + } + .padding(.horizontal, 40) + .padding(.top) } } diff --git a/Sources/StackCardView.swift b/Sources/StackCardView.swift index 2a3b21e..ad236d2 100644 --- a/Sources/StackCardView.swift +++ b/Sources/StackCardView.swift @@ -12,7 +12,7 @@ public struct StackCard private var content: () -> Content private var model: T - @StateObject private var viewModel = StackCardViewModel() + @ObservedObject private var viewModel: StackCardViewModel @State var offset:CGFloat = 0.0 @State var endSwipe: Bool = false @@ -33,11 +33,14 @@ public struct StackCard @GestureState private var dragState: DragState = DragState.inactive - @ObservedObject private var stackCardButtonPublisher: StackCardButtonProperties + @ObservedObject private var stackCardButtonPublisher: StackCardButtonPublisher - public init(model: T, stackCardButtonPublisher: StackCardButtonProperties? = nil, @ViewBuilder content: @escaping () -> Content) { + public init(model: T, viewModel: StackCardViewModel, + stackCardButtonPublisher: StackCardButtonPublisher? = nil, + @ViewBuilder content: @escaping () -> Content) { self.model = model - self.stackCardButtonPublisher = stackCardButtonPublisher ?? StackCardButtonProperties() + self.viewModel = viewModel + self.stackCardButtonPublisher = stackCardButtonPublisher ?? StackCardButtonPublisher() self.content = content } @@ -56,20 +59,27 @@ public struct StackCard .rotationEffect(.init(degrees: getRotationAngle())) //controls the rotation angle. .contentShape(Circle().trim(from: 0, to: endSwipe ? 0 : 1)) //Users can enable second card too, so we are setting the shape. .gesture(dragGesture) - .onReceive(stackCardButtonPublisher.$buttons) { buttons in + .onReceive(stackCardButtonPublisher.$direction) { direction in let width = UIScreen.main.bounds.width - Utils.defaultValue - switch true { - case isIdEqual(to: buttons.left): - withAnimation { - offset = -(width * 2) - endSwipeActions() - onLeftButtonTap?() + guard let first = viewModel.displayingCards?.first else { return } + switch direction { + case .left: + if isIdEqual(to: first.id) { + withAnimation { + offset = -(width * 2) + endSwipeActions() + viewModel.removeCard() + onLeftButtonTap?() + } } - case isIdEqual(to: buttons.right): - withAnimation { - offset = width * 2 - endSwipeActions() - onRightButtonTap?() + case .right: + if isIdEqual(to: first.id) { + withAnimation { + offset = width * 2 + endSwipeActions() + viewModel.removeCard() + onRightButtonTap?() + } } default: break diff --git a/Sources/ViewModel/StackCardButtonViewModel.swift b/Sources/ViewModel/StackCardButtonViewModel.swift index 03ff648..43b39df 100644 --- a/Sources/ViewModel/StackCardButtonViewModel.swift +++ b/Sources/ViewModel/StackCardButtonViewModel.swift @@ -1,18 +1,29 @@ import Foundation import Combine -public class StackCardButtonProperties: ObservableObject { - @Published var buttons: (left: String, right: String) = ("", "") +public enum StackCardDirection: CaseIterable { + case left, right +} + +public class StackCardButtonPublisher: ObservableObject { + // @Published var buttons: (left: String, right: String) = ("", "") + @Published var direction: StackCardDirection? - public init(left: String = "", right: String = "") { - self.buttons = (left, right) - } + public init() { } - public func setLeftButton(for id: String) { - self.buttons.left = id + public func setDirection(direction: StackCardDirection) { + self.direction = direction } - public func setRightButton(for id: String) { - self.buttons.left = id - } +// public init(left: String = "", right: String = "") { +// self.buttons = (left, right) +// } + +// public func setLeftButton(for id: String) { +// self.buttons.left = id +// } +// +// public func setRightButton(for id: String) { +// self.buttons.left = id +// } } From 46697894c220ca7ec5185a9f2494374b78b234f5 Mon Sep 17 00:00:00 2001 From: Badarinath Venkatnarayansetty Date: Wed, 31 Jan 2024 21:38:07 -0800 Subject: [PATCH 2/2] view updates --- .../StackCardView.xcodeproj/project.pbxproj | 4 + .../StackCardView/StackCardButtonView.swift | 96 +++++++++++++++++++ Example/StackCardView/StackCardDemoView.swift | 36 +------ Example/StackCardView/ViewController.swift | 20 +++- README.md | 11 ++- .../ViewModel/StackCardButtonViewModel.swift | 13 --- 6 files changed, 131 insertions(+), 49 deletions(-) create mode 100644 Example/StackCardView/StackCardButtonView.swift diff --git a/Example/StackCardView.xcodeproj/project.pbxproj b/Example/StackCardView.xcodeproj/project.pbxproj index 51db396..91c4fc0 100644 --- a/Example/StackCardView.xcodeproj/project.pbxproj +++ b/Example/StackCardView.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; 712FE8292B6858E500923019 /* StackCardDemoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712FE8282B6858E500923019 /* StackCardDemoView.swift */; }; + 712FE8642B6B610E00923019 /* StackCardButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712FE8632B6B610E00923019 /* StackCardButtonView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -41,6 +42,7 @@ 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 676E58D48FC9FFC1A07BBA81 /* Pods_StackCardView_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StackCardView_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 712FE8282B6858E500923019 /* StackCardDemoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackCardDemoView.swift; sourceTree = ""; }; + 712FE8632B6B610E00923019 /* StackCardButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackCardButtonView.swift; sourceTree = ""; }; B6FE68BE563F9126C6FCCDDE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; C80498C57BC4EC2A1BE20A00 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; CB4C86EA9E2E89C83A41E822 /* StackCardView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = StackCardView.podspec; path = ../StackCardView.podspec; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -114,6 +116,7 @@ 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 607FACD31AFB9204008FA782 /* Supporting Files */, 712FE8282B6858E500923019 /* StackCardDemoView.swift */, + 712FE8632B6B610E00923019 /* StackCardButtonView.swift */, ); name = "Example for StackCardView"; path = StackCardView; @@ -306,6 +309,7 @@ 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 712FE8292B6858E500923019 /* StackCardDemoView.swift in Sources */, 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, + 712FE8642B6B610E00923019 /* StackCardButtonView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Example/StackCardView/StackCardButtonView.swift b/Example/StackCardView/StackCardButtonView.swift new file mode 100644 index 0000000..2661a14 --- /dev/null +++ b/Example/StackCardView/StackCardButtonView.swift @@ -0,0 +1,96 @@ +// +// StackCardWithButtonsView.swift +// StackCardView_Example +// +// Created by Rani Badri on 1/31/24. +// Copyright © 2024 CocoaPods. All rights reserved. +// + +import Foundation +import SwiftUI +import StackCardView + +struct StackCardButtonView: View { + @State private var stackCardModels:[StackCardModel] = [] + @StateObject private var viewModel = StackCardViewModel() + @StateObject private var stackCardButtonPublisher = StackCardButtonPublisher() + + var body: some View { + VStack { + /// iterate over the list of cards + ForEach(stackCardModels.reversed(), id: \.id) { card in + StackCard(model: card, viewModel: viewModel, stackCardButtonPublisher: stackCardButtonPublisher) { + // content + Image(card.image) + .resizable() + .aspectRatio(contentMode: .fill) + } + .setCardDisplayType(value: .bottom) + .setRotationAngle(value: 20) + .onRightSwipe { + print("Right Swipe \(card.id)") + } + .onLeftSwipe { + print("left Swipe \(card.id)") + } + .onLeftButtonTap { + print("Left Button Tap \(card.id)") + } + .onRightButtonTap { + print("Right Button Tap \(card.id)") + } + } + .embedInZStack() + .bind(model: stackCardModels) + + //empty State View + if viewModel.displayingCards?.isEmpty ?? false { + Text("Come back later we can find more matches for you!") + .font(.caption) + .foregroundColor(.gray) + } + + + HStack { + Button(action: { + stackCardButtonPublisher.setDirection(direction: StackCardDirection.left) + }, label: { + Text("Keep Unread") + .foregroundColor(.white) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.black) + .cornerRadius(8) + + }) + + Spacer() + + Button { + stackCardButtonPublisher.setDirection(direction: StackCardDirection.right) + } label: { + Text("Mark As read") + .foregroundColor(.black) + .padding(.horizontal, 20) + .padding(.vertical, 10) + .background(Color.gray.opacity(0.2)) + .cornerRadius(8) + } + } + .padding(.all, 10) + .padding(.top, 30) + } + .padding(.all, 20) + .frame(maxWidth: .infinity, maxHeight: .infinity) + .frame(height: UIScreen.main.bounds.size.height - 100) + .onAppear { + stackCardModels = [ + StackCardModel(id: UUID().uuidString, name: "Park1", image: "park1"), + StackCardModel(id: UUID().uuidString, name: "Park2", image: "park2"), + StackCardModel(id: UUID().uuidString,name: "Park3", image: "park3"), + StackCardModel(id: UUID().uuidString,name: "Park4", image: "park4") + ] + } + } +} + diff --git a/Example/StackCardView/StackCardDemoView.swift b/Example/StackCardView/StackCardDemoView.swift index ddc972e..ac53e43 100644 --- a/Example/StackCardView/StackCardDemoView.swift +++ b/Example/StackCardView/StackCardDemoView.swift @@ -22,14 +22,14 @@ struct StackCardModel: StackCardModelProtocol, Identifiable { struct StackCardDemoView: View { @State private var stackCardModels:[StackCardModel] = [] - @StateObject private var viewModel = StackCardViewModel() - @StateObject private var stackCardButtonPublisher = StackCardButtonPublisher() + @StateObject private var viewModel = StackCardViewModel() + var body: some View { VStack { /// iterate over the list of cards ForEach(stackCardModels.reversed(), id: \.id) { card in - StackCard(model: card, viewModel: viewModel, stackCardButtonPublisher: stackCardButtonPublisher) { + StackCard(model: card, viewModel: viewModel) { // content Image(card.image) .resizable() @@ -71,35 +71,5 @@ struct StackCardDemoView: View { StackCardModel(id: UUID().uuidString,name: "Park4", image: "park4") ] } - - - HStack { - Button(action: { - stackCardButtonPublisher.setDirection(direction: StackCardDirection.left) - }, label: { - Text("Keep Unread") - .foregroundColor(.white) - .padding(.horizontal, 20) - .padding(.vertical, 10) - .background(Color.black) - .cornerRadius(8) - - }) - - Spacer() - - Button { - stackCardButtonPublisher.setDirection(direction: StackCardDirection.right) - } label: { - Text("Mark As read") - .foregroundColor(.black) - .padding(.horizontal, 20) - .padding(.vertical, 10) - .background(Color.gray.opacity(0.2)) - .cornerRadius(8) - } - } - .padding(.horizontal, 40) - .padding(.top) } } diff --git a/Example/StackCardView/ViewController.swift b/Example/StackCardView/ViewController.swift index 766c63f..87eced2 100644 --- a/Example/StackCardView/ViewController.swift +++ b/Example/StackCardView/ViewController.swift @@ -16,7 +16,7 @@ class ViewController: UIViewController { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. - let stackCardView = UIHostingController(rootView: StackCardDemoView()) + let stackCardView = UIHostingController(rootView: ContentView()) stackCardView.view.translatesAutoresizingMaskIntoConstraints = false self.view.addSubview(stackCardView.view) @@ -35,4 +35,22 @@ class ViewController: UIViewController { } } +struct ContentView: View { + var body: some View { + TabView { + StackCardDemoView() + .tabItem { + Image(systemName: "square.stack.3d.up.fill") + Text("First Tab") + } + + StackCardButtonView() + .tabItem { + Image(systemName: "square.stack.3d.up.fill") + Text("Second Tab") + } + } + } +} + diff --git a/README.md b/README.md index 3cde68a..d929d3b 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,12 @@

-StepperView +StackCardView +

+ +
+

+StackCardViewWithButtons

## Example @@ -70,10 +75,12 @@ struct StackCardModel: StackCardModelProtocol, Identifiable { @State private var stackCardModels:[StackCardModel] =[] +@StateObject private var viewModel = StackCardViewModel() + var body: some View { VStack { ForEach(stackCardModels.reversed(), id: \.id) { card in - StackCard(model: card) { + StackCard(model: card, viewModel: viewModel) { // content Image(card.image) .resizable() diff --git a/Sources/ViewModel/StackCardButtonViewModel.swift b/Sources/ViewModel/StackCardButtonViewModel.swift index 43b39df..2a14353 100644 --- a/Sources/ViewModel/StackCardButtonViewModel.swift +++ b/Sources/ViewModel/StackCardButtonViewModel.swift @@ -6,7 +6,6 @@ public enum StackCardDirection: CaseIterable { } public class StackCardButtonPublisher: ObservableObject { - // @Published var buttons: (left: String, right: String) = ("", "") @Published var direction: StackCardDirection? public init() { } @@ -14,16 +13,4 @@ public class StackCardButtonPublisher: ObservableObject { public func setDirection(direction: StackCardDirection) { self.direction = direction } - -// public init(left: String = "", right: String = "") { -// self.buttons = (left, right) -// } - -// public func setLeftButton(for id: String) { -// self.buttons.left = id -// } -// -// public func setRightButton(for id: String) { -// self.buttons.left = id -// } }