Skip to content

Commit ad27055

Browse files
authored
feat: vertical slider (#41)
1 parent 1bf5663 commit ad27055

4 files changed

Lines changed: 106 additions & 26 deletions

File tree

.swiftlint.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,8 @@ function_body_length:
6161
cyclomatic_complexity:
6262
warning: 10
6363
error: 20
64+
65+
identifier_name:
66+
excluded:
67+
- x
68+
- y

Package.resolved

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

Sources/SlidableImage/SlidableImage.swift

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,56 +7,88 @@ import SwiftUI
77

88
public struct SlidableImage<ArrowsIcon: View, LeftView: View, RightView: View>: View {
99
@State private var location: CGPoint?
10+
private let axis: Axis
1011
private let arrows: () -> ArrowsIcon
1112
private let leftView: () -> LeftView
1213
private let rightView: () -> RightView
1314

14-
public init(@ViewBuilder arrows: @escaping () -> ArrowsIcon,
15+
public init(axis: Axis = .horizontal,
16+
@ViewBuilder arrows: @escaping () -> ArrowsIcon,
1517
@ViewBuilder leftView: @escaping () -> LeftView,
1618
@ViewBuilder rightView: @escaping () -> RightView) {
19+
self.axis = axis
1720
self.arrows = arrows
1821
self.leftView = leftView
1922
self.rightView = rightView
2023
}
2124

2225
public var body: some View {
2326
GeometryReader { geometry in
24-
ZStack(alignment: .leading) {
27+
ZStack(alignment: axis == .horizontal ? .leading : .top) {
2528
rightView()
2629
leftView()
2730
.mask {
28-
HStack {
29-
Color.black
30-
Spacer(minLength: maskSize(width: geometry.size.width))
31+
if axis == .horizontal {
32+
HStack {
33+
Color.black
34+
Spacer(minLength: maskSize(total: geometry.size.width))
35+
}
36+
} else {
37+
VStack {
38+
Color.black
39+
Spacer(minLength: maskSize(total: geometry.size.height))
40+
}
3141
}
3242
}
3343

3444
arrows()
3545
.frame(width: Constants.arrowSize, height: Constants.arrowSize)
36-
.padding(.leading, location?.x ?? geometry.size.width / 2 - Constants.arrowSize / 2)
46+
.padding(axis == .horizontal ? .leading : .top,
47+
locationValue ?? defaultOffset(geometry: geometry))
3748
.gesture(
3849
DragGesture()
3950
.onChanged { value in
40-
guard value.location.x <= geometry.size.width - Constants.arrowSize else { return }
41-
42-
location = CGPoint(x: value.location.x, y: geometry.size.height / 2)
51+
if axis == .horizontal {
52+
let x = min(max(value.location.x, 0), geometry.size.width - Constants.arrowSize)
53+
location = CGPoint(x: x, y: geometry.size.height / 2)
54+
} else {
55+
let y = min(max(value.location.y, 0), geometry.size.height - Constants.arrowSize)
56+
location = CGPoint(x: geometry.size.width / 2, y: y)
57+
}
4358
}
4459
)
4560
}
4661
}
4762
}
4863

49-
package func maskSize(width: CGFloat, locationX: CGFloat? = nil) -> CGFloat {
50-
guard let locationX = locationX ?? location?.x else {
51-
return width / 2
64+
private var locationValue: CGFloat? {
65+
axis == .horizontal ? location?.x : location?.y
66+
}
67+
68+
private func defaultOffset(geometry: GeometryProxy) -> CGFloat {
69+
axis == .horizontal
70+
? geometry.size.width / 2 - Constants.arrowSize / 2
71+
: geometry.size.height / 2 - Constants.arrowSize / 2
72+
}
73+
74+
package func maskSize(total: CGFloat, locationValue: CGFloat? = nil) -> CGFloat {
75+
guard let value = locationValue ?? self.locationValue else {
76+
return total / 2
5277
}
5378

54-
return width - locationX - Constants.arrowSize / 2
79+
return total - value - Constants.arrowSize / 2
5580
}
5681
}
5782

5883
#Preview {
59-
SlidableImage(arrows: { Arrows() },
60-
leftView: { Color.red },
61-
rightView: { Color.green })
84+
VStack {
85+
SlidableImage(arrows: { Arrows() },
86+
leftView: { Color.red },
87+
rightView: { Color.green })
88+
89+
SlidableImage(axis: .vertical,
90+
arrows: { Arrows() },
91+
leftView: { Color.blue },
92+
rightView: { Color.orange })
93+
}
6294
}

Tests/SlidableImageTests/SlidableImageTests.swift

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,46 @@ import SwiftUI
55
@MainActor
66
struct SlidableImageTests {
77

8-
private func makeSUT() -> SlidableImage<Arrows, Color, Color> {
9-
SlidableImage(arrows: { Arrows() }, leftView: { Color.red }, rightView: { Color.green })
8+
private func makeSUT(axis: Axis = .horizontal) -> SlidableImage<Arrows, Color, Color> {
9+
SlidableImage(axis: axis, arrows: { Arrows() }, leftView: { Color.red }, rightView: { Color.green })
1010
}
1111

12-
@Test func maskSizeWithoutLocation() {
13-
#expect(makeSUT().maskSize(width: 200) == 100)
12+
// MARK: - Horizontal
13+
14+
@Test func maskSizeHorizontalWithoutLocation() {
15+
#expect(makeSUT().maskSize(total: 200) == 100)
1416
}
1517

16-
@Test func maskSizeWithLocationAtStart() {
17-
let result = makeSUT().maskSize(width: 200, locationX: 0)
18+
@Test func maskSizeHorizontalWithLocationAtStart() {
19+
let result = makeSUT().maskSize(total: 200, locationValue: 0)
1820
#expect(result == 200 - Constants.arrowSize / 2)
1921
}
2022

21-
@Test func maskSizeWithLocationAtCenter() {
22-
let result = makeSUT().maskSize(width: 200, locationX: 100)
23+
@Test func maskSizeHorizontalWithLocationAtCenter() {
24+
let result = makeSUT().maskSize(total: 200, locationValue: 100)
2325
#expect(result == 100 - Constants.arrowSize / 2)
2426
}
2527

26-
@Test func maskSizeWithLocationAtBoundary() {
28+
@Test func maskSizeHorizontalWithLocationAtBoundary() {
2729
let locationX = 200 - Constants.arrowSize
28-
let result = makeSUT().maskSize(width: 200, locationX: locationX)
30+
let result = makeSUT().maskSize(total: 200, locationValue: locationX)
31+
#expect(result == Constants.arrowSize / 2)
32+
}
33+
34+
// MARK: - Vertical
35+
36+
@Test func maskSizeVerticalWithoutLocation() {
37+
#expect(makeSUT(axis: .vertical).maskSize(total: 200) == 100)
38+
}
39+
40+
@Test func maskSizeVerticalWithLocationAtStart() {
41+
let result = makeSUT(axis: .vertical).maskSize(total: 200, locationValue: 0)
42+
#expect(result == 200 - Constants.arrowSize / 2)
43+
}
44+
45+
@Test func maskSizeVerticalWithLocationAtBoundary() {
46+
let locationY = 200 - Constants.arrowSize
47+
let result = makeSUT(axis: .vertical).maskSize(total: 200, locationValue: locationY)
2948
#expect(result == Constants.arrowSize / 2)
3049
}
3150
}

0 commit comments

Comments
 (0)