Skip to content

Commit 3b0b8f6

Browse files
[CM-1252] Add layout to set edge insets (#6)
* [FEAT] added layout property to appearance [UPDATE] removed frames for buttons and HStack, added default insets * [UPDATE] preview with default values * [UPDATE review comments resolved [UPDATE] test case updated
1 parent cdd8ed6 commit 3b0b8f6

File tree

4 files changed

+81
-34
lines changed

4 files changed

+81
-34
lines changed

Sources/YStepper/SwiftUI/Views/Stepper.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import YMatterType
1212
/// A SwiftUI stepper control.
1313
public struct Stepper {
1414
@Environment(\.sizeCategory) var sizeCategory
15-
let buttonSize: CGSize = CGSize(width: 44, height: 44)
15+
1616
@ScaledMetric var scale = 1.0
1717
@ObservedObject private var appearanceObserver = Stepper.AppearanceObserver()
1818
@ObservedObject private var valueObserver = Stepper.ValueObserver()
@@ -90,12 +90,12 @@ public struct Stepper {
9090
extension Stepper: View {
9191
/// :nodoc:
9292
public var body: some View {
93-
HStack(spacing: 0) {
93+
HStack(spacing: appearance.layout.gap) {
9494
getDecrementButton()
9595
getTextView()
9696
getIncrementButton()
9797
}
98-
.frame(width: (2 * buttonSize.width) + getStringSize(sizeCategory).width)
98+
.padding(EdgeInsets(appearance.layout.contentInset))
9999
.background(
100100
getShape()
101101
.background(getShapeWithoutStroke().foregroundColor(Color(appearance.backgroundColor)))
@@ -107,7 +107,6 @@ extension Stepper: View {
107107
Button { buttonAction(buttonType: .increment) } label: {
108108
getIncrementImage().renderingMode(.template)
109109
}
110-
.frame(width: buttonSize.width, height: buttonSize.height)
111110
.accessibilityLabel(StepperControl.Strings.incrementA11yButton.localized)
112111
}
113112

@@ -116,7 +115,6 @@ extension Stepper: View {
116115
Button { buttonAction(buttonType: .decrement) } label: {
117116
getImageForDecrementButton()?.renderingMode(.template)
118117
}
119-
.frame(width: buttonSize.width, height: buttonSize.height)
120118
.accessibilityLabel(getAccessibilityText())
121119
}
122120

@@ -135,7 +133,7 @@ extension Stepper: View {
135133

136134
@ViewBuilder
137135
func getShape() -> some View {
138-
switch appearance.shape {
136+
switch appearance.layout.shape {
139137
case .none:
140138
EmptyView()
141139
case .rectangle:
@@ -161,7 +159,7 @@ extension Stepper: View {
161159

162160
@ViewBuilder
163161
func getShapeWithoutStroke() -> some View {
164-
switch appearance.shape {
162+
switch appearance.layout.shape {
165163
case .none:
166164
EmptyView()
167165
case .rectangle:
@@ -276,7 +274,7 @@ extension Stepper {
276274

277275
private extension Stepper {
278276
func onMinimumValueChange(newValue: Double) {
279-
if minimumValue < maximumValue {
277+
if newValue < maximumValue {
280278
valueObserver.minimumValue = newValue
281279
if value < minimumValue {
282280
valueObserver.value = minimumValue
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//
2+
// StepperControl+Appearance+Layout.swift
3+
// YStepper
4+
//
5+
// Created by Sahil Saini on 20/03/23.
6+
// Copyright © 2023 Y Media Labs. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
extension StepperControl.Appearance {
12+
/// A collection of layout properties for the `StepperControl`
13+
public struct Layout: Equatable {
14+
/// The content inset from edges. Stepper "content" consists of the two buttons and the text label between them.
15+
/// Default is `{8, 16, 8, 16}`.
16+
public var contentInset: NSDirectionalEdgeInsets
17+
/// The horizontal spacing between the stepper buttons and label. Default is `8.0`
18+
public var gap: CGFloat
19+
/// Stepper's shape
20+
public var shape: Shape
21+
22+
/// Default stepper control layout.
23+
public static let `default` = Layout()
24+
25+
/// Initializes a `Layout`.
26+
/// - Parameters:
27+
/// - contentInset: content inset from edges
28+
/// - gap: horizontal spacing between icons and label
29+
/// - shape: Stepper's shape. Default is `.capsule`
30+
public init(
31+
contentInset: NSDirectionalEdgeInsets =
32+
NSDirectionalEdgeInsets(topAndBottom: 8, leadingAndTrailing: 16),
33+
gap: CGFloat = 8,
34+
shape: Shape = .capsule
35+
) {
36+
self.contentInset = contentInset
37+
self.gap = gap
38+
self.shape = shape
39+
}
40+
}
41+
}

Sources/YStepper/UIKit/StepperControl+Appearance.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ extension StepperControl {
2626
public var incrementImage: UIImage
2727
/// Decrement button image
2828
public var decrementImage: UIImage
29-
/// Stepper's shape
30-
public var shape: Shape
31-
/// Whether to show delete button or not.
29+
/// Stepper's layout properties such as spacing between views. Default is `.default`
30+
public var layout: Layout
31+
/// Whether to show delete button or not
3232
var hasDeleteButton: Bool { deleteImage != nil }
3333

3434
/// Initializer for appearance
@@ -42,7 +42,7 @@ extension StepperControl {
4242
/// - deleteImage: Delete button image. Default is `Appearance.defaultDeleteImage`
4343
/// - incrementImage: Increment button image. Default is `Appearance.defaultIncrementImage`
4444
/// - decrementImage: Decrement button image. Default is `Appearance.defaultDecrementImage`
45-
/// - shape: Stepper's shape. Default is `.capsule`
45+
/// - layout: Stepper's layout properties like spacing between views
4646

4747
public init(
4848
textStyle: (textColor: UIColor, typography: Typography) = (.label, .systemLabel),
@@ -53,7 +53,7 @@ extension StepperControl {
5353
deleteImage: UIImage? = Appearance.defaultDeleteImage,
5454
incrementImage: UIImage = Appearance.defaultIncrementImage,
5555
decrementImage: UIImage = Appearance.defaultDecrementImage,
56-
shape: Shape = .capsule
56+
layout: Layout = .default
5757
) {
5858
self.textStyle = textStyle
5959
self.backgroundColor = backgroundColor
@@ -62,7 +62,7 @@ extension StepperControl {
6262
self.deleteImage = deleteImage
6363
self.incrementImage = incrementImage
6464
self.decrementImage = decrementImage
65-
self.shape = shape
65+
self.layout = layout
6666
}
6767
}
6868
}

Tests/YStepperTests/Views/StepperTests.swift

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -153,76 +153,84 @@ final class StepperTests: XCTestCase {
153153
}
154154

155155
func testShapesNotNil() {
156-
var expectedAppearance = StepperControl.Appearance(shape: .rectangle)
156+
var expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .rectangle))
157157
var sut = makeSUT(appearance: expectedAppearance)
158158
let rectShape = sut.getShape()
159159

160-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
160+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
161161
XCTAssertNotNil(rectShape)
162162

163-
expectedAppearance = StepperControl.Appearance(shape: .roundRect(cornerRadius: 10))
163+
expectedAppearance = StepperControl.Appearance(
164+
layout: StepperControl.Appearance.Layout(shape: .roundRect(cornerRadius: 10))
165+
)
164166
sut.appearance = expectedAppearance
165167
let roundedRectShape = sut.getShape()
166168

167-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
169+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
168170
XCTAssertNotNil(roundedRectShape)
169171

170-
expectedAppearance = StepperControl.Appearance(shape: .scaledRoundRect(cornerRadius: 10))
172+
expectedAppearance = StepperControl.Appearance(
173+
layout: StepperControl.Appearance.Layout(shape: .scaledRoundRect(cornerRadius: 10))
174+
)
171175
sut.appearance = expectedAppearance
172176
let scaledRoundRect = sut.getShape()
173177

174-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
178+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
175179
XCTAssertNotNil(scaledRoundRect)
176180

177-
expectedAppearance = StepperControl.Appearance(shape: .capsule)
181+
expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .capsule))
178182
sut.appearance = expectedAppearance
179183
let capsuleShape = sut.getShape()
180184

181-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
185+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
182186
XCTAssertNotNil(capsuleShape)
183187

184-
expectedAppearance = StepperControl.Appearance(shape: .none)
188+
expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .none))
185189
sut.appearance = expectedAppearance
186190
let emptyView = sut.getShape()
187191

188-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
192+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
189193
XCTAssertNotNil(emptyView)
190194
}
191195

192196
func testShapesWithoutStrokeNotNil() {
193-
var expectedAppearance = StepperControl.Appearance(shape: .rectangle)
197+
var expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .rectangle))
194198
var sut = makeSUT(appearance: expectedAppearance)
195199
let rectShape = sut.getShapeWithoutStroke()
196200

197-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
201+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
198202
XCTAssertNotNil(rectShape)
199203

200-
expectedAppearance = StepperControl.Appearance(shape: .roundRect(cornerRadius: 10))
204+
expectedAppearance = StepperControl.Appearance(
205+
layout: StepperControl.Appearance.Layout(shape: .roundRect(cornerRadius: 10))
206+
)
201207
sut.appearance = expectedAppearance
202208
let roundedRectShape = sut.getShapeWithoutStroke()
203209

204-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
210+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
205211
XCTAssertNotNil(roundedRectShape)
206212

207-
expectedAppearance = StepperControl.Appearance(shape: .scaledRoundRect(cornerRadius: 10))
213+
expectedAppearance = StepperControl.Appearance(
214+
layout: StepperControl.Appearance.Layout(shape: .scaledRoundRect(cornerRadius: 10))
215+
)
208216
sut.appearance = expectedAppearance
209217
let scaledRoundRect = sut.getShapeWithoutStroke()
210218

211-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
219+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
212220
XCTAssertNotNil(scaledRoundRect)
213221

214-
expectedAppearance = StepperControl.Appearance(shape: .capsule)
222+
expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .capsule))
215223
sut.appearance = expectedAppearance
216224
let capsuleShape = sut.getShapeWithoutStroke()
217225

218-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
226+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
219227
XCTAssertNotNil(capsuleShape)
220228

221-
expectedAppearance = StepperControl.Appearance(shape: .none)
229+
expectedAppearance = StepperControl.Appearance(layout: StepperControl.Appearance.Layout(shape: .none))
222230
sut.appearance = expectedAppearance
223231
let emptyView = sut.getShapeWithoutStroke()
224232

225-
XCTAssertEqual(sut.appearance.shape, expectedAppearance.shape)
233+
XCTAssertEqual(sut.appearance.layout.shape, expectedAppearance.layout.shape)
226234
XCTAssertNotNil(emptyView)
227235
}
228236

0 commit comments

Comments
 (0)