Skip to content

Commit a97255d

Browse files
[CM-1253] Add dim effect to buttons (#7)
* [UPDATE]rebase conflicts * [UPDATE] preview with default values * [FEAT] added disable functionality to increment and decrement [UPDATE] change opacity for disable button, updated images, updated test cases * [UPDATE] review comments resolved * [UPDATE] review comment resolve, added "." to documentation comment.
1 parent 3b0b8f6 commit a97255d

File tree

6 files changed

+104
-92
lines changed

6 files changed

+104
-92
lines changed

Sources/YStepper/SwiftUI/Views/Stepper.swift

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,19 @@ public struct Stepper {
1717
@ObservedObject private var appearanceObserver = Stepper.AppearanceObserver()
1818
@ObservedObject private var valueObserver = Stepper.ValueObserver()
1919

20-
/// Receive value change notification
20+
var isIncrementDisabled: Bool {
21+
value >= maximumValue
22+
}
23+
var isDecrementDisabled: Bool {
24+
value <= minimumValue
25+
}
26+
var shouldShowDelete: Bool {
27+
appearance.showDeleteImage
28+
&& value <= stepValue
29+
&& minimumValue == 0
30+
}
31+
32+
/// Receive value change notification.
2133
public weak var delegate: StepperDelegate?
2234

2335
/// Stepper appearance
@@ -58,19 +70,19 @@ public struct Stepper {
5870
set { onValueChange(newValue: newValue) }
5971
}
6072

61-
/// Decimal digits in current value
73+
/// Decimal digits in current value.
6274
public var decimalPlaces: Int {
6375
get { valueObserver.decimalValue }
6476
set { valueObserver.decimalValue = newValue }
6577
}
6678

6779
/// Initializes a stepper view.
6880
/// - Parameters:
69-
/// - appearance: appearance for the stepper. Default is `.default`
70-
/// - minimumValue: minimum value. Default is `0`
71-
/// - maximumValue: maximum value. Default is `100`
72-
/// - stepValue: Step value. Default is `1`
73-
/// - value: Current value. Default is `0` or minimumValue
81+
/// - appearance: appearance for the stepper. Default is `.default`.
82+
/// - minimumValue: minimum value. Default is `0`.
83+
/// - maximumValue: maximum value. Default is `100`.
84+
/// - stepValue: Step value. Default is `1`.
85+
/// - value: Current value. Default is `0` or minimumValue.
7486
public init(
7587
appearance: StepperControl.Appearance = .default,
7688
minimumValue: Double = 0,
@@ -105,17 +117,25 @@ extension Stepper: View {
105117
@ViewBuilder
106118
func getIncrementButton() -> some View {
107119
Button { buttonAction(buttonType: .increment) } label: {
108-
getIncrementImage().renderingMode(.template)
120+
getIncrementImage()
121+
.foregroundColor(
122+
Color(appearance.textStyle.textColor).opacity(isIncrementDisabled ? 0.5 : 1)
123+
)
109124
}
110125
.accessibilityLabel(StepperControl.Strings.incrementA11yButton.localized)
126+
.disabled(isIncrementDisabled)
111127
}
112128

113129
@ViewBuilder
114130
func getDecrementButton() -> some View {
115131
Button { buttonAction(buttonType: .decrement) } label: {
116-
getImageForDecrementButton()?.renderingMode(.template)
132+
getImageForDecrementButton()
133+
.foregroundColor(
134+
Color(appearance.textStyle.textColor).opacity(isDecrementDisabled ? 0.5 : 1)
135+
)
117136
}
118137
.accessibilityLabel(getAccessibilityText())
138+
.disabled(isDecrementDisabled)
119139
}
120140

121141
func getTextView() -> some View {
@@ -130,7 +150,7 @@ extension Stepper: View {
130150
.frame(width: getStringSize(sizeCategory).width)
131151
.accessibilityLabel(getAccessibilityLabelText())
132152
}
133-
153+
134154
@ViewBuilder
135155
func getShape() -> some View {
136156
switch appearance.layout.shape {
@@ -199,9 +219,7 @@ extension Stepper {
199219
}
200220

201221
func getAccessibilityText() -> String {
202-
if appearance.hasDeleteButton
203-
&& value <= stepValue
204-
&& minimumValue == 0 {
222+
if shouldShowDelete {
205223
return StepperControl.Strings.deleteA11yButton.localized
206224
}
207225
return StepperControl.Strings.decrementA11yButton.localized
@@ -245,11 +263,8 @@ extension Stepper {
245263
}
246264

247265
extension Stepper {
248-
func getDeleteImage() -> Image? {
249-
if let image = appearance.deleteImage {
250-
return Image(uiImage: image)
251-
}
252-
return nil
266+
func getDeleteImage() -> Image {
267+
Image(uiImage: appearance.deleteImage)
253268
}
254269

255270
@ViewBuilder
@@ -262,10 +277,8 @@ extension Stepper {
262277
Image(uiImage: appearance.decrementImage)
263278
}
264279

265-
func getImageForDecrementButton() -> Image? {
266-
if appearance.hasDeleteButton
267-
&& value <= stepValue
268-
&& minimumValue == 0 {
280+
func getImageForDecrementButton() -> Image {
281+
if shouldShowDelete {
269282
return getDeleteImage()
270283
}
271284
return getDecrementImage()

Sources/YStepper/UIKit/StepperControl+Appearance+Layout.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@
99
import UIKit
1010

1111
extension StepperControl.Appearance {
12-
/// A collection of layout properties for the `StepperControl`
12+
/// A collection of layout properties for the `StepperControl`.
1313
public struct Layout: Equatable {
1414
/// The content inset from edges. Stepper "content" consists of the two buttons and the text label between them.
1515
/// Default is `{8, 16, 8, 16}`.
1616
public var contentInset: NSDirectionalEdgeInsets
17-
/// The horizontal spacing between the stepper buttons and label. Default is `8.0`
17+
/// The horizontal spacing between the stepper buttons and label. Default is `8.0`.
1818
public var gap: CGFloat
1919
/// Stepper's shape
2020
public var shape: Shape
@@ -24,9 +24,9 @@ extension StepperControl.Appearance {
2424

2525
/// Initializes a `Layout`.
2626
/// - Parameters:
27-
/// - contentInset: content inset from edges
28-
/// - gap: horizontal spacing between icons and label
29-
/// - shape: Stepper's shape. Default is `.capsule`
27+
/// - contentInset: content inset from edges.
28+
/// - gap: horizontal spacing between icons and label.
29+
/// - shape: Stepper's shape. Default is `.capsule`.
3030
public init(
3131
contentInset: NSDirectionalEdgeInsets =
3232
NSDirectionalEdgeInsets(topAndBottom: 8, leadingAndTrailing: 16),

Sources/YStepper/UIKit/StepperControl+Appearance.swift

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,70 +10,76 @@ import UIKit
1010
import YMatterType
1111

1212
extension StepperControl {
13-
/// Appearance for stepper that contains typography and color properties
13+
/// Appearance for stepper that contains typography and color properties.
1414
public struct Appearance {
15-
/// Typography of stepper value label
15+
/// Typography of stepper value label.
1616
public var textStyle: (textColor: UIColor, typography: Typography)
17-
/// Background color for stepper view
17+
/// Background color for stepper view.
1818
public var backgroundColor: UIColor
19-
/// Border color for stepper view
19+
/// Border color for stepper view.
2020
public var borderColor: UIColor
21-
/// Border width for stepper view
21+
/// Border width for stepper view.
2222
public var borderWidth: CGFloat
23-
/// Delete button image. Nil means no delete button
24-
public var deleteImage: UIImage?
23+
/// Delete button image
24+
public var deleteImage: UIImage
2525
/// Increment button image
2626
public var incrementImage: UIImage
2727
/// Decrement button image
2828
public var decrementImage: UIImage
29-
/// Stepper's layout properties such as spacing between views. Default is `.default`
29+
/// Stepper's layout properties such as spacing between views. Default is `.default`.
3030
public var layout: Layout
31-
/// Whether to show delete button or not
32-
var hasDeleteButton: Bool { deleteImage != nil }
31+
/// Whether to show delete image or not.
32+
var showDeleteImage: Bool
3333

34-
/// Initializer for appearance
34+
/// Initializer for appearance.
3535
/// - Parameters:
3636
/// - textStyle: Typography and text color for valueText label.
37-
/// Default is `(UIColor.label, Typography.systemLabel)`
38-
/// - foregroundColor: Foreground color for valueText. Default is `.label`
39-
/// - backgroundColor: Background color for stepper view. Default is `.systemBackground`
40-
/// - borderColor: Border color for stepper view. Default is `UIColor.label`
41-
/// - borderWidth: Border width for day view. Default is `1.0`
42-
/// - deleteImage: Delete button image. Default is `Appearance.defaultDeleteImage`
43-
/// - incrementImage: Increment button image. Default is `Appearance.defaultIncrementImage`
44-
/// - decrementImage: Decrement button image. Default is `Appearance.defaultDecrementImage`
45-
/// - layout: Stepper's layout properties like spacing between views
46-
37+
/// Default is `(UIColor.label, Typography.systemLabel)`.
38+
/// - foregroundColor: Foreground color for valueText. Default is `.label`.
39+
/// - backgroundColor: Background color for stepper view. Default is `.systemBackground`.
40+
/// - borderColor: Border color for stepper view. Default is `UIColor.label`.
41+
/// - borderWidth: Border width for day view. Default is `1.0`.
42+
/// - deleteImage: Delete button image. Default is `nil`.
43+
/// Passing `nil` means to use the default delete image.
44+
/// - incrementImage: Increment button image. Default is `nil`.
45+
/// Passing `nil` means to use the default increment image.
46+
/// - decrementImage: Decrement button image. Default is `nil`.
47+
/// Passing `nil` means to use the default decrement image.
48+
/// - layout: Stepper's layout properties like spacing between views.
49+
/// - showDeleteImage: Whether to show delete button or not. Default is`true`.
50+
4751
public init(
4852
textStyle: (textColor: UIColor, typography: Typography) = (.label, .systemLabel),
4953
foregroundColor: UIColor = .label,
5054
backgroundColor: UIColor = .systemBackground,
5155
borderColor: UIColor = .label,
5256
borderWidth: CGFloat = 1.0,
53-
deleteImage: UIImage? = Appearance.defaultDeleteImage,
54-
incrementImage: UIImage = Appearance.defaultIncrementImage,
55-
decrementImage: UIImage = Appearance.defaultDecrementImage,
56-
layout: Layout = .default
57+
deleteImage: UIImage? = nil,
58+
incrementImage: UIImage? = nil,
59+
decrementImage: UIImage? = nil,
60+
layout: Layout = .default,
61+
showDeleteImage: Bool = true
5762
) {
5863
self.textStyle = textStyle
5964
self.backgroundColor = backgroundColor
6065
self.borderColor = borderColor
6166
self.borderWidth = borderWidth
62-
self.deleteImage = deleteImage
63-
self.incrementImage = incrementImage
64-
self.decrementImage = decrementImage
67+
self.deleteImage = deleteImage ?? Appearance.defaultDeleteImage
68+
self.incrementImage = incrementImage ?? Appearance.defaultIncrementImage
69+
self.decrementImage = decrementImage ?? Appearance.defaultDecrementImage
6570
self.layout = layout
71+
self.showDeleteImage = showDeleteImage
6672
}
6773
}
6874
}
6975

7076
extension StepperControl.Appearance {
7177
/// Default stepper appearance
7278
public static let `default` = StepperControl.Appearance()
73-
/// Default image for delete button. Is a `trash` from SF Symbols in template rendering mode
74-
public static let defaultDeleteImage = StepperControl.Images.delete.image
75-
/// Default image for increment button. Is a `plus` from SF Symbols in template rendering mode
76-
public static let defaultIncrementImage = StepperControl.Images.increment.image
77-
/// Default image for decrement button. Is a `minus` from SF Symbols in template rendering mode
78-
public static let defaultDecrementImage = StepperControl.Images.decrement.image
79+
/// Default image for delete button. Is a `trash` from SF Symbols in template rendering mode.
80+
public static let defaultDeleteImage = StepperControl.Images.delete.image.withRenderingMode(.alwaysTemplate)
81+
/// Default image for increment button. Is a `plus` from SF Symbols in template rendering mode.
82+
public static let defaultIncrementImage = StepperControl.Images.increment.image.withRenderingMode(.alwaysTemplate)
83+
/// Default image for decrement button. Is a `minus` from SF Symbols in template rendering mode.
84+
public static let defaultDecrementImage = StepperControl.Images.decrement.image.withRenderingMode(.alwaysTemplate)
7985
}

Sources/YStepper/UIKit/StepperControl.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,19 @@ public class StepperControl: UIControl {
4343
get { stepperView.value }
4444
set { stepperView.value = newValue }
4545
}
46-
/// Decimal places visible in current value
46+
/// Decimal places visible in current value.
4747
public var decimalPlaces: Int {
4848
get { stepperView.decimalPlaces }
4949
set { stepperView.decimalPlaces = newValue }
5050
}
5151

52-
/// Initializes a stepper control
52+
/// Initializes a stepper control.
5353
/// - Parameters:
54-
/// - appearance: appearance for the stepper. Default is `.default`
55-
/// - minimumValue: minimum value. Default is `0`
56-
/// - maximumValue: maximum value. Default is `100`
57-
/// - stepValue: Step value. Default is `1`
58-
/// - value: Current value. Default is `0` or minimumValue (if provided)
54+
/// - appearance: appearance for the stepper. Default is `.default`.
55+
/// - minimumValue: minimum value. Default is `0`.
56+
/// - maximumValue: maximum value. Default is `100`.
57+
/// - stepValue: Step value. Default is `1`.
58+
/// - value: Current value. Default is `0` or minimumValue (if provided).
5959
public required init(
6060
appearance: StepperControl.Appearance = .default,
6161
minimumValue: Double = 0,

Sources/YStepper/UIKit/YStepper+Shapes.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ extension StepperControl.Appearance {
1313
public enum Shape: Equatable {
1414
/// None
1515
case none
16-
/// Rectangle.
16+
/// Rectangle
1717
case rectangle
18-
/// Rounded rectangle.
18+
/// Rounded rectangle
1919
case roundRect(cornerRadius: CGFloat)
2020
/// Rounded rectangle that scales with Dynamic Type.
2121
case scaledRoundRect(cornerRadius: CGFloat)
22-
/// Capsule.
22+
/// Capsule
2323
case capsule
2424
}
2525
}

Tests/YStepperTests/Views/StepperTests.swift

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,24 @@ final class StepperTests: XCTestCase {
9595

9696
func testImagesShouldNotBeNil() {
9797
let sut = makeSUT(appearance: StepperControl.Appearance(
98-
deleteImage: StepperControl.Appearance.defaultDeleteImage
98+
deleteImage: nil,
99+
incrementImage: nil,
100+
decrementImage: nil
99101
))
100102

101103
XCTAssertNotNil(sut.getIncrementImage())
102104
XCTAssertNotNil(sut.getDecrementImage())
103105
XCTAssertNotNil(sut.getDeleteImage())
104-
}
105-
106-
func testDeleteImageShouldBeNil() {
107-
let sut = makeSUT(appearance: StepperControl.Appearance(deleteImage: nil))
108-
XCTAssertNil(sut.getDeleteImage())
109106

110-
let decrementImage = sut.getDecrementImage()
107+
XCTAssertEqual(StepperControl.Appearance.defaultDeleteImage, sut.appearance.deleteImage)
108+
XCTAssertEqual(StepperControl.Appearance.defaultDecrementImage, sut.appearance.decrementImage)
109+
XCTAssertEqual(StepperControl.Appearance.defaultIncrementImage, sut.appearance.incrementImage)
110+
}
111111

112-
XCTAssertEqual(decrementImage, sut.getImageForDecrementButton())
112+
func testDecrementImageshouldNotReturnDeleteImage() {
113+
let sut = makeSUT(appearance: StepperControl.Appearance(showDeleteImage: false))
114+
let decrementImage = sut.getDeleteImage()
115+
XCTAssertNotEqual(decrementImage, sut.getImageForDecrementButton())
113116
}
114117

115118
func testValueGreaterThanMaxValue() {
@@ -123,17 +126,14 @@ final class StepperTests: XCTestCase {
123126
}
124127

125128
func testImageUpdate() {
126-
var sut = makeSUT(appearance: StepperControl.Appearance(deleteImage: StepperControl.Images.decrement.image))
129+
var sut = makeSUT(appearance: StepperControl.Appearance(showDeleteImage: false))
127130
let deleteImage = sut.getDeleteImage()
128131
let decrementImage = sut.getDecrementImage()
129132
XCTAssertEqual(sut.value, sut.minimumValue)
130133
XCTAssertEqual(sut.getImageForDecrementButton(), decrementImage)
131134

132-
sut.value = sut.stepValue
135+
sut.appearance.showDeleteImage = true
133136
XCTAssertEqual(sut.getImageForDecrementButton(), deleteImage)
134-
135-
sut.appearance.deleteImage = nil
136-
XCTAssertEqual(sut.getImageForDecrementButton(), decrementImage)
137137
}
138138

139139
func testButtonAction() {
@@ -145,13 +145,6 @@ final class StepperTests: XCTestCase {
145145
XCTAssertEqual(sut.value, sut.minimumValue)
146146
}
147147

148-
func testBackgroundNotNil() {
149-
let sut = makeSUT()
150-
let button = sut.getDecrementButton()
151-
152-
XCTAssertNotNil(button)
153-
}
154-
155148
func testShapesNotNil() {
156149
var expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .rectangle))
157150
var sut = makeSUT(appearance: expectedAppearance)
@@ -242,7 +235,7 @@ final class StepperTests: XCTestCase {
242235
var sut = makeSUT()
243236
XCTAssertEqual(sut.getAccessibilityText(), StepperControl.Strings.deleteA11yButton.localized)
244237

245-
sut.appearance.deleteImage = nil
238+
sut.appearance.showDeleteImage = false
246239
XCTAssertEqual(sut.getAccessibilityText(), StepperControl.Strings.decrementA11yButton.localized)
247240
}
248241
}

0 commit comments

Comments
 (0)