Skip to content

Commit 61d2571

Browse files
committed
Initial commit
0 parents  commit 61d2571

25 files changed

+556
-0
lines changed

.github/workflows/swift.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name: Swift
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
build:
11+
12+
runs-on: macos-latest
13+
14+
steps:
15+
- uses: actions/checkout@v2
16+
- name: Build
17+
run: swift build -v
18+
- name: Run tests
19+
run: swift test -v

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/

.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>IDEDidComputeMac32BitWarning</key>
6+
<true/>
7+
</dict>
8+
</plist>
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Scheme
3+
LastUpgradeVersion = "1140"
4+
version = "1.3">
5+
<BuildAction
6+
parallelizeBuildables = "YES"
7+
buildImplicitDependencies = "YES">
8+
<BuildActionEntries>
9+
<BuildActionEntry
10+
buildForTesting = "YES"
11+
buildForRunning = "YES"
12+
buildForProfiling = "YES"
13+
buildForArchiving = "YES"
14+
buildForAnalyzing = "YES">
15+
<BuildableReference
16+
BuildableIdentifier = "primary"
17+
BlueprintIdentifier = "CardStack"
18+
BuildableName = "CardStack"
19+
BlueprintName = "CardStack"
20+
ReferencedContainer = "container:">
21+
</BuildableReference>
22+
</BuildActionEntry>
23+
<BuildActionEntry
24+
buildForTesting = "YES"
25+
buildForRunning = "YES"
26+
buildForProfiling = "NO"
27+
buildForArchiving = "NO"
28+
buildForAnalyzing = "YES">
29+
<BuildableReference
30+
BuildableIdentifier = "primary"
31+
BlueprintIdentifier = "CardStackTests"
32+
BuildableName = "CardStackTests"
33+
BlueprintName = "CardStackTests"
34+
ReferencedContainer = "container:">
35+
</BuildableReference>
36+
</BuildActionEntry>
37+
</BuildActionEntries>
38+
</BuildAction>
39+
<TestAction
40+
buildConfiguration = "Debug"
41+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
42+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
43+
shouldUseLaunchSchemeArgsEnv = "YES"
44+
codeCoverageEnabled = "YES">
45+
<Testables>
46+
<TestableReference
47+
skipped = "NO">
48+
<BuildableReference
49+
BuildableIdentifier = "primary"
50+
BlueprintIdentifier = "CardStackTests"
51+
BuildableName = "CardStackTests"
52+
BlueprintName = "CardStackTests"
53+
ReferencedContainer = "container:">
54+
</BuildableReference>
55+
</TestableReference>
56+
</Testables>
57+
</TestAction>
58+
<LaunchAction
59+
buildConfiguration = "Debug"
60+
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
61+
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
62+
launchStyle = "0"
63+
useCustomWorkingDirectory = "NO"
64+
ignoresPersistentStateOnLaunch = "NO"
65+
debugDocumentVersioning = "YES"
66+
debugServiceExtension = "internal"
67+
allowLocationSimulation = "YES">
68+
</LaunchAction>
69+
<ProfileAction
70+
buildConfiguration = "Release"
71+
shouldUseLaunchSchemeArgsEnv = "YES"
72+
savedToolIdentifier = ""
73+
useCustomWorkingDirectory = "NO"
74+
debugDocumentVersioning = "YES">
75+
<MacroExpansion>
76+
<BuildableReference
77+
BuildableIdentifier = "primary"
78+
BlueprintIdentifier = "CardStack"
79+
BuildableName = "CardStack"
80+
BlueprintName = "CardStack"
81+
ReferencedContainer = "container:">
82+
</BuildableReference>
83+
</MacroExpansion>
84+
</ProfileAction>
85+
<AnalyzeAction
86+
buildConfiguration = "Debug">
87+
</AnalyzeAction>
88+
<ArchiveAction
89+
buildConfiguration = "Release"
90+
revealArchiveInOrganizer = "YES">
91+
</ArchiveAction>
92+
</Scheme>

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020 Deniz Adalar
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.resolved

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// swift-tools-version:5.1
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "CardStack",
6+
platforms: [
7+
.iOS(.v13),
8+
.watchOS(.v6),
9+
.macOS(.v10_15)
10+
],
11+
products: [
12+
.library(name: "CardStack", targets: ["CardStack"])
13+
],
14+
dependencies: [
15+
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing.git", from: "1.7.2")
16+
],
17+
targets: [
18+
.target(name: "CardStack", dependencies: []),
19+
.testTarget(name: "CardStackTests", dependencies: ["CardStack", "SnapshotTesting"])
20+
]
21+
)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# CardStack
2+
3+
A description of this package.

Sources/CardStack/CardStack.swift

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import SwiftUI
2+
3+
public struct CardStack<Direction, ID: Hashable, Data: RandomAccessCollection, Content: View>: View where Data.Index: Hashable {
4+
5+
@Environment(\.cardStackConfiguration) private var configuration: CardStackConfiguration
6+
@State private var currentIndex: Data.Index
7+
8+
private let direction: (Double) -> Direction?
9+
private let data: Data
10+
private let id: KeyPath<Data.Element, ID>
11+
private let onSwipe: (Direction) -> ()
12+
private let content: (Data.Element, Direction?, Bool) -> Content
13+
14+
public init(
15+
direction: @escaping (Double) -> Direction?,
16+
data: Data,
17+
id: KeyPath<Data.Element, ID>,
18+
onSwipe: @escaping (Direction) -> (),
19+
@ViewBuilder content: @escaping (Data.Element, Direction?, Bool) -> Content
20+
) {
21+
self.direction = direction
22+
self.data = data
23+
self.id = id
24+
self.onSwipe = onSwipe
25+
self.content = content
26+
27+
self._currentIndex = State<Data.Index>(initialValue: data.startIndex)
28+
}
29+
30+
public var body: some View {
31+
ZStack {
32+
ForEach(data.indices.reversed(), id: \.self) { index -> AnyView in
33+
let relativeIndex = self.data.distance(from: self.currentIndex, to: index)
34+
if relativeIndex >= 0 && relativeIndex < self.configuration.maxVisibleCards {
35+
return AnyView(self.card(index: index, relativeIndex: relativeIndex))
36+
} else {
37+
return AnyView(EmptyView())
38+
}
39+
}
40+
}
41+
}
42+
43+
private func card(index: Data.Index, relativeIndex: Int) -> some View {
44+
CardView(
45+
direction: direction,
46+
isOnTop: relativeIndex == 0,
47+
onSwipe: { direction in
48+
self.currentIndex = self.data.index(after: index)
49+
self.onSwipe(direction)
50+
},
51+
content: { direction in
52+
self.content(self.data[index], direction, relativeIndex == 0)
53+
.offset(
54+
x: 0,
55+
y: CGFloat(relativeIndex) * self.configuration.cardOffset
56+
)
57+
.scaleEffect(
58+
1 - self.configuration.cardScale * CGFloat(relativeIndex),
59+
anchor: .bottom
60+
)
61+
}
62+
)
63+
}
64+
65+
}
66+
67+
public extension CardStack where Data.Element: Identifiable, ID == Data.Element.ID {
68+
69+
init(
70+
direction: @escaping (Double) -> Direction?,
71+
data: Data,
72+
onSwipe: @escaping (Direction) -> (),
73+
@ViewBuilder content: @escaping (Data.Element, Direction?, Bool) -> Content
74+
) {
75+
self.init(
76+
direction: direction,
77+
data: data,
78+
id: \Data.Element.id,
79+
onSwipe: onSwipe,
80+
content: content
81+
)
82+
}
83+
84+
}
85+
86+
public extension CardStack where Data.Element: Hashable, ID == Data.Element {
87+
88+
init(
89+
direction: @escaping (Double) -> Direction?,
90+
data: Data,
91+
onSwipe: @escaping (Direction) -> (),
92+
@ViewBuilder content: @escaping (Data.Element, Direction?, Bool) -> Content
93+
) {
94+
self.init(
95+
direction: direction,
96+
data: data,
97+
id: \Data.Element.self,
98+
onSwipe: onSwipe,
99+
content: content
100+
)
101+
}
102+
103+
}

0 commit comments

Comments
 (0)