Skip to content

Commit d159ace

Browse files
Merge pull request #87 from componentskit/improve-card
Improve card
2 parents 329ac0f + 4597913 commit d159ace

File tree

4 files changed

+123
-18
lines changed

4 files changed

+123
-18
lines changed

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/CardPreview.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,24 @@ struct CardPreview: View {
1515
SUCard(model: self.model, content: self.suCardContent)
1616
}
1717
Form {
18+
AnimationScalePicker(selection: self.$model.animationScale)
1819
Picker("Background Color", selection: self.$model.backgroundColor) {
19-
Text("Default").tag(Optional<UniversalColor>.none)
20+
Text("Background").tag(UniversalColor.background)
2021
Text("Secondary Background").tag(UniversalColor.secondaryBackground)
2122
Text("Accent Background").tag(UniversalColor.accentBackground)
2223
Text("Success Background").tag(UniversalColor.successBackground)
2324
Text("Warning Background").tag(UniversalColor.warningBackground)
2425
Text("Danger Background").tag(UniversalColor.dangerBackground)
2526
}
27+
Picker("Border Color", selection: self.$model.borderColor) {
28+
Text("Divider").tag(UniversalColor.divider)
29+
Text("Primary").tag(UniversalColor.primary)
30+
Text("Accent").tag(UniversalColor.accent)
31+
Text("Success").tag(UniversalColor.success)
32+
Text("Warning").tag(UniversalColor.warning)
33+
Text("Danger").tag(UniversalColor.danger)
34+
Text("Custom").tag(UniversalColor.universal(.uiColor(.systemPurple)))
35+
}
2636
BorderWidthPicker(selection: self.$model.borderWidth)
2737
Picker("Content Paddings", selection: self.$model.contentPaddings) {
2838
Text("12px").tag(Paddings(padding: 12))
@@ -39,6 +49,7 @@ struct CardPreview: View {
3949
Text("Large").tag(Shadow.large)
4050
Text("Custom").tag(Shadow.custom(20.0, .zero, UniversalColor.accentBackground))
4151
}
52+
Toggle("Tappable", isOn: self.$model.isTappable)
4253
}
4354
}
4455
}

Sources/ComponentsKit/Components/Card/Models/CardVM.swift

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,16 @@ import Foundation
22

33
/// A model that defines the appearance properties for a card component.
44
public struct CardVM: ComponentVM {
5+
/// The scaling factor for the card's tap animation, with a value between 0 and 1.
6+
///
7+
/// Defaults to `.medium`.
8+
public var animationScale: AnimationScale = .medium
9+
510
/// The background color of the card.
6-
public var backgroundColor: UniversalColor?
11+
public var backgroundColor: UniversalColor = .background
12+
13+
/// The border color of the card.
14+
public var borderColor: UniversalColor = .divider
715

816
/// The border thickness of the card.
917
///
@@ -20,6 +28,11 @@ public struct CardVM: ComponentVM {
2028
/// Defaults to `.medium`.
2129
public var cornerRadius: ContainerRadius = .medium
2230

31+
/// A Boolean value indicating whether the card should allow to be tapped.
32+
///
33+
/// Defaults to `true`.
34+
public var isTappable: Bool = false
35+
2336
/// The shadow of the card.
2437
///
2538
/// Defaults to `.medium`.
@@ -28,11 +41,3 @@ public struct CardVM: ComponentVM {
2841
/// Initializes a new instance of `CardVM` with default values.
2942
public init() {}
3043
}
31-
32-
// MARK: - Helpers
33-
34-
extension CardVM {
35-
var preferredBackgroundColor: UniversalColor {
36-
return self.backgroundColor ?? .background
37-
}
38-
}

Sources/ComponentsKit/Components/Card/SUCard.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ public struct SUCard<Content: View>: View {
1616

1717
/// A model that defines the appearance properties.
1818
public let model: CardVM
19+
/// A closure that is triggered when the card is tapped.
20+
public var onTap: () -> Void
21+
22+
/// A Boolean value indicating whether the card is pressed.
23+
@State public var isPressed: Bool = false
1924

2025
@ViewBuilder private let content: () -> Content
26+
@State private var contentSize: CGSize = .zero
2127

2228
// MARK: - Initialization
2329

@@ -28,23 +34,48 @@ public struct SUCard<Content: View>: View {
2834
/// - content: The content that is displayed in the card.
2935
public init(
3036
model: CardVM = .init(),
31-
content: @escaping () -> Content
37+
content: @escaping () -> Content,
38+
onTap: @escaping () -> Void = {}
3239
) {
3340
self.model = model
3441
self.content = content
42+
self.onTap = onTap
3543
}
3644

3745
// MARK: - Body
3846

3947
public var body: some View {
4048
self.content()
4149
.padding(self.model.contentPaddings.edgeInsets)
42-
.background(self.model.preferredBackgroundColor.color)
50+
.background(self.model.backgroundColor.color)
4351
.cornerRadius(self.model.cornerRadius.value)
4452
.overlay(
4553
RoundedRectangle(cornerRadius: self.model.cornerRadius.value)
46-
.stroke(UniversalColor.divider.color, lineWidth: self.model.borderWidth.value)
54+
.stroke(
55+
self.model.borderColor.color,
56+
lineWidth: self.model.borderWidth.value
57+
)
4758
)
4859
.shadow(self.model.shadow)
60+
.observeSize { self.contentSize = $0 }
61+
.simultaneousGesture(DragGesture(minimumDistance: 0.0)
62+
.onChanged { _ in
63+
guard self.model.isTappable else { return }
64+
self.isPressed = true
65+
}
66+
.onEnded { value in
67+
guard self.model.isTappable else { return }
68+
69+
defer { self.isPressed = false }
70+
71+
if CGRect(origin: .zero, size: self.contentSize).contains(value.location) {
72+
self.onTap()
73+
}
74+
}
75+
)
76+
.scaleEffect(
77+
self.isPressed ? self.model.animationScale.value : 1,
78+
anchor: .center
79+
)
4980
}
5081
}

Sources/ComponentsKit/Components/Card/UKCard.swift

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,22 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
2121
/// The primary content of the card, provided as a custom view.
2222
public let content: Content
2323

24-
// MARK: - Properties
24+
// MARK: - Public Properties
2525

26-
private var contentConstraints = LayoutConstraints()
26+
/// A closure that is triggered when the card is tapped.
27+
public var onTap: () -> Void
28+
29+
/// A Boolean value indicating whether the button is pressed.
30+
public private(set) var isPressed: Bool = false {
31+
didSet {
32+
self.transform = self.isPressed
33+
? .init(
34+
scaleX: self.model.animationScale.value,
35+
y: self.model.animationScale.value
36+
)
37+
: .identity
38+
}
39+
}
2740

2841
/// A model that defines the appearance properties.
2942
public var model: CardVM {
@@ -32,6 +45,10 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
3245
}
3346
}
3447

48+
// MARK: - Private Properties
49+
50+
private var contentConstraints = LayoutConstraints()
51+
3552
// MARK: - Initialization
3653

3754
/// Initializer.
@@ -41,10 +58,12 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
4158
/// - content: The content that is displayed in the card.
4259
public init(
4360
model: CardVM = .init(),
44-
content: @escaping () -> Content
61+
content: @escaping () -> Content,
62+
onTap: @escaping () -> Void = {}
4563
) {
4664
self.model = model
4765
self.content = content()
66+
self.onTap = onTap
4867

4968
super.init(frame: .zero)
5069

@@ -95,6 +114,8 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
95114
self.layer.shadowPath = UIBezierPath(rect: self.bounds).cgPath
96115
}
97116

117+
// MARK: - Update
118+
98119
/// Updates appearance when the model changes.
99120
open func update(_ oldValue: CardVM) {
100121
guard self.model != oldValue else { return }
@@ -113,6 +134,43 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
113134

114135
// MARK: - UIView Methods
115136

137+
open override func touchesBegan(
138+
_ touches: Set<UITouch>,
139+
with event: UIEvent?
140+
) {
141+
super.touchesBegan(touches, with: event)
142+
143+
guard self.model.isTappable else { return }
144+
145+
self.isPressed = true
146+
}
147+
148+
open override func touchesEnded(
149+
_ touches: Set<UITouch>,
150+
with event: UIEvent?
151+
) {
152+
super.touchesEnded(touches, with: event)
153+
154+
guard self.model.isTappable else { return }
155+
156+
defer { self.isPressed = false }
157+
158+
if self.model.isTappable,
159+
let location = touches.first?.location(in: self),
160+
self.bounds.contains(location) {
161+
self.onTap()
162+
}
163+
}
164+
165+
open override func touchesCancelled(
166+
_ touches: Set<UITouch>,
167+
with event: UIEvent?
168+
) {
169+
super.touchesCancelled(touches, with: event)
170+
171+
self.isPressed = false
172+
}
173+
116174
open override func traitCollectionDidChange(
117175
_ previousTraitCollection: UITraitCollection?
118176
) {
@@ -130,10 +188,10 @@ open class UKCard<Content: UIView>: UIView, UKComponent {
130188
extension UKCard {
131189
fileprivate enum Style {
132190
static func mainView(_ view: UIView, model: Model) {
133-
view.backgroundColor = model.preferredBackgroundColor.uiColor
191+
view.backgroundColor = model.backgroundColor.uiColor
134192
view.layer.cornerRadius = model.cornerRadius.value
135193
view.layer.borderWidth = model.borderWidth.value
136-
view.layer.borderColor = UniversalColor.divider.cgColor
194+
view.layer.borderColor = model.borderColor.cgColor
137195
view.shadow(model.shadow)
138196
}
139197
}

0 commit comments

Comments
 (0)