Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Passing ViewModel as part of the constructor adopting Button publisher Readme updates #11

Merged
merged 2 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Example/StackCardView.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -41,6 +42,7 @@
607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = "<group>"; };
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 = "<group>"; };
712FE8632B6B610E00923019 /* StackCardButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackCardButtonView.swift; sourceTree = "<group>"; };
B6FE68BE563F9126C6FCCDDE /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
C80498C57BC4EC2A1BE20A00 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
CB4C86EA9E2E89C83A41E822 /* StackCardView.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = StackCardView.podspec; path = ../StackCardView.podspec; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
Expand Down Expand Up @@ -114,6 +116,7 @@
607FACDE1AFB9204008FA782 /* LaunchScreen.xib */,
607FACD31AFB9204008FA782 /* Supporting Files */,
712FE8282B6858E500923019 /* StackCardDemoView.swift */,
712FE8632B6B610E00923019 /* StackCardButtonView.swift */,
);
name = "Example for StackCardView";
path = StackCardView;
Expand Down Expand Up @@ -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;
};
Expand Down
96 changes: 96 additions & 0 deletions Example/StackCardView/StackCardButtonView.swift
Original file line number Diff line number Diff line change
@@ -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<StackCardModel>()
@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")
]
}
}
}

13 changes: 10 additions & 3 deletions Example/StackCardView/StackCardDemoView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
typealias CardType = StackCardModel

var id: String
var name:String = ""

Check warning on line 12 in Example/StackCardView/StackCardDemoView.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals. (colon)
var image: String = ""

init(id: String, name: String, image: String) {
Expand All @@ -21,14 +21,15 @@


struct StackCardDemoView: View {

@State private var stackCardModels:[StackCardModel] = []

Check warning on line 24 in Example/StackCardView/StackCardDemoView.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Colon Violation: Colons should be next to the identifier when specifying a type and next to the key in dictionary literals. (colon)

@StateObject private var viewModel = StackCardViewModel<StackCardModel>()

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) {
// content
Image(card.image)
.resizable()
Expand All @@ -42,12 +43,18 @@
.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)
Expand All @@ -60,8 +67,8 @@
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"),

Check warning on line 70 in Example/StackCardView/StackCardDemoView.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Comma Spacing Violation: There should be no space before and one after any comma. (comma)
StackCardModel(id: UUID().uuidString,name: "Park4", image: "park4")

Check warning on line 71 in Example/StackCardView/StackCardDemoView.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Comma Spacing Violation: There should be no space before and one after any comma. (comma)
]
}
}
Expand Down
20 changes: 19 additions & 1 deletion Example/StackCardView/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.

Check warning on line 18 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
let stackCardView = UIHostingController(rootView: StackCardDemoView())
let stackCardView = UIHostingController(rootView: ContentView())
stackCardView.view.translatesAutoresizingMaskIntoConstraints = false
self.view.addSubview(stackCardView.view)

Check warning on line 22 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
//set constraints
NSLayoutConstraint.activate([
stackCardView.view.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 50),
stackCardView.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
stackCardView.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
stackCardView.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),

Check warning on line 28 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Comma Violation: Collection literals should not have trailing commas. (trailing_comma)
])
}

Expand All @@ -35,4 +35,22 @@
}
}

struct ContentView: View {
var body: some View {
TabView {
StackCardDemoView()
.tabItem {
Image(systemName: "square.stack.3d.up.fill")
Text("First Tab")
}

Check warning on line 46 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace. (trailing_whitespace)
StackCardButtonView()
.tabItem {
Image(systemName: "square.stack.3d.up.fill")
Text("Second Tab")
}
}
}
}


Check warning on line 56 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Newline Violation: Files should have a single trailing newline. (trailing_newline)

Check warning on line 56 in Example/StackCardView/ViewController.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Vertical Whitespace Violation: Limit vertical whitespace to a single empty line. Currently 2. (vertical_whitespace)
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@

<br/>
<p align="center">
<img src="https://raw.githubusercontent.com/badrinathvm/StackCardView/master/images/StackCardView.gif" height="450" alt="StepperView"/>
<img src="https://raw.githubusercontent.com/badrinathvm/StackCardView/master/images/StackCardView.gif" height="450" alt="StackCardView"/>
</p>

<br/>
<p align="center">
<img src="https://raw.githubusercontent.com/badrinathvm/StackCardView/master/images/StackCardWithButtons.gif" height="450" alt="StackCardViewWithButtons"/>
</p>

## Example
Expand Down Expand Up @@ -70,10 +75,12 @@ struct StackCardModel: StackCardModelProtocol, Identifiable {


@State private var stackCardModels:[StackCardModel] =[]
@StateObject private var viewModel = StackCardViewModel<StackCardModel>()

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()
Expand Down
42 changes: 26 additions & 16 deletions Sources/StackCardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public struct StackCard<Content: View, T: Identifiable & StackCardModelProtocol>
private var content: () -> Content
private var model: T

@StateObject private var viewModel = StackCardViewModel<T>()
@ObservedObject private var viewModel: StackCardViewModel<T>

@State var offset:CGFloat = 0.0
@State var endSwipe: Bool = false
Expand All @@ -33,11 +33,14 @@ public struct StackCard<Content: View, T: Identifiable & StackCardModelProtocol>

@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<T>,
stackCardButtonPublisher: StackCardButtonPublisher? = nil,
@ViewBuilder content: @escaping () -> Content) {
self.model = model
self.stackCardButtonPublisher = stackCardButtonPublisher ?? StackCardButtonProperties()
self.viewModel = viewModel
self.stackCardButtonPublisher = stackCardButtonPublisher ?? StackCardButtonPublisher()
self.content = content
}

Expand All @@ -56,20 +59,27 @@ public struct StackCard<Content: View, T: Identifiable & StackCardModelProtocol>
.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
Expand Down
20 changes: 9 additions & 11 deletions Sources/ViewModel/StackCardButtonViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import Foundation
import Combine

public class StackCardButtonProperties: ObservableObject {
@Published var buttons: (left: String, right: String) = ("", "")
public init(left: String = "", right: String = "") {
self.buttons = (left, right)
}
public enum StackCardDirection: CaseIterable {
case left, right
}

public class StackCardButtonPublisher: ObservableObject {
@Published var direction: StackCardDirection?

public func setLeftButton(for id: String) {
self.buttons.left = id
}
public init() { }

public func setRightButton(for id: String) {
self.buttons.left = id
public func setDirection(direction: StackCardDirection) {
self.direction = direction
}
}