Skip to content

Commit 631933c

Browse files
authored
Merge pull request #23 from iWECon/padding
Add padding for H/VStackView
2 parents 160e34e + f8415e8 commit 631933c

8 files changed

Lines changed: 147 additions & 150 deletions

Demo/Demo/HVStackDemoViewController.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,13 @@ class HVStackDemoViewController: UIViewController {
2424
// Support Spacer (Inspired by SwiftUI)
2525
Spacer()
2626

27-
VStackView {
27+
VStackView(distribution: .spacing(4)) {
2828
UILabel().stack.then { label in
2929
label.font = .systemFont(ofSize: 14, weight: .semibold)
3030
label.textColor = .systemGreen
3131
label.text = "H/VStack in UIKit"
3232
}
33-
Spacer(length: 4)
3433
Divider(color: UIColor.blue)
35-
Spacer(length: 12)
3634
UILabel().stack.then { label in
3735
label.font = .systemFont(ofSize: 12, weight: .regular)
3836
label.textColor = .gray
@@ -42,7 +40,7 @@ class HVStackDemoViewController: UIViewController {
4240

4341
Spacer()
4442

45-
VStackView(distribution: .fillWidth(spacing: 10)) {
43+
VStackView(distribution: .fillWidth(spacing: 4)) {
4644

4745
// view.stack.then (Inspired by Then [ https://github.com/devxoul/Then ])
4846
UILabel().stack.then { label in
@@ -91,18 +89,14 @@ class HVStackDemoViewController: UIViewController {
9189
}
9290
}
9391

94-
HStackView(alignment: .top, distribution: .spacing(14)) {
95-
Spacer(length: 12)
96-
92+
HStackView(alignment: .top, distribution: .spacing(14), padding: UIEdgeInsets(top: 2, left: 12, bottom: 2, right: 6)) {
9793
VStackView {
98-
Spacer(length: 6)
9994
UIView().stack.size(6).then {
10095
$0.backgroundColor = .systemBlue
10196
$0.layer.cornerRadius = 3
10297
$0.clipsToBounds = true
10398
}
10499
}
105-
106100
Spacer(length: 4)
107101
UILabel().stack.then {
108102
$0.textColor = .darkText

Demo/Demo/HVStackWrapperViewDemoViewController.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ let contentWidth: CGFloat = UIScreen.main.bounds.width - 32
1212

1313
class HVStackWrapperViewDemoViewController: UIViewController {
1414

15-
let content = VStackView {
15+
let content = VStackView(distribution: .fillWidth(spacing: 0), padding: UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)) {
1616
HStackView() {
1717
VStackView(alignment: .left) {
1818
UILabel().stack.then {
@@ -44,7 +44,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
4444
}
4545

4646
// wrapper in other view, need set width and sizeToFit
47-
}.stack.width(contentWidth).sizeToFit(.width)
47+
}
4848

4949
Spacer(length: 20)
5050

@@ -86,7 +86,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
8686
}
8787
}
8888
}
89-
}.stack.width(contentWidth).sizeToFit(.width)
89+
}
9090

9191
Spacer(length: 30)
9292
HStackView(alignment: .bottom) {
@@ -101,11 +101,11 @@ class HVStackWrapperViewDemoViewController: UIViewController {
101101
$0.font = .systemFont(ofSize: 18, weight: .medium)
102102
$0.textColor = .blue
103103
}
104-
}.stack.width(contentWidth).sizeToFit(.width)
104+
}.stack.sizeToFit(.width)
105105

106106
Spacer(length: 20)
107-
VStackView(alignment: .left) {
108-
107+
108+
VStackView(distribution: .fillWidth()) {
109109
HStackView {
110110
UIView().stack.size(40).then {
111111
$0.backgroundColor = .systemPink
@@ -130,7 +130,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
130130
$0.textColor = .black
131131
$0.font = .systemFont(ofSize: 18, weight: .medium)
132132
}
133-
}.stack.width(contentWidth).sizeToFit(.width)
133+
}
134134
}
135135
}
136136

@@ -141,7 +141,7 @@ class HVStackWrapperViewDemoViewController: UIViewController {
141141

142142

143143
let textLayer = CATextLayer()
144-
textLayer.string = "This is CATextLayer in VStackLayerWrapperView2"
144+
textLayer.string = "This is CATextLayer in VStackLayerContainerView"
145145
textLayer.fontSize = 12
146146
textLayer.foregroundColor = UIColor.black.cgColor
147147
textLayer.contentsScale = UIScreen.main.scale
@@ -152,14 +152,14 @@ class HVStackWrapperViewDemoViewController: UIViewController {
152152
textLayer
153153
}
154154
}
155-
155+
content.sizeToFit()
156156
self.view.addSubview(content)
157157
}
158158

159159
override func viewDidLayoutSubviews() {
160160
super.viewDidLayoutSubviews()
161161

162-
content.pin.top(view.pin.safeArea).horizontally(16).sizeToFit()
162+
content.pin.top(view.pin.safeArea).horizontally().sizeToFit(.width)
163163
}
164164

165165
/*

Demo/Demo/WrapStackDemoViewController.swift

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,6 @@ class WrapStackDemoViewController: UIViewController {
1515

1616
// Do any additional setup after loading the view.
1717

18-
let vStack = VStackView(distribution: .fillWidth(spacing: 10)) {
19-
UILabel().stack.then {
20-
$0.text = "StackKit"
21-
$0.font = .systemFont(ofSize: 18, weight: .semibold)
22-
$0.textColor = .black
23-
}
24-
Divider()
25-
UILabel().stack.then {
26-
$0.text = "Version 1.0.0"
27-
$0.font = .systemFont(ofSize: 14, weight: .regular)
28-
$0.textColor = .gray
29-
}
30-
}
31-
vStack.sizeToFit()
32-
vStack.frame.origin = CGPoint(x: 20, y: 120)
33-
34-
view.addSubview(vStack)
3518
}
3619

3720
/*

Sources/StackKit/HStackView.swift

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
import UIKit
22

3-
open class HStackView: UIView {
3+
open class HStackView: UIView, StackView {
44

55
public var alignment: HStackAlignment = .center
66
public var distribution: HStackDistribution = .autoSpacing
7+
public var padding: UIEdgeInsets = .zero
78

89
public required init(
910
alignment: HStackAlignment = .center,
1011
distribution: HStackDistribution = .spacing(2),
12+
padding: UIEdgeInsets = .zero,
1113
@_StackKitHStackContentResultBuilder content: () -> [UIView] = { [] }
1214
) {
1315
super.init(frame: .zero)
1416

1517
self.alignment = alignment
1618
self.distribution = distribution
19+
self.padding = padding
1720

1821
for v in content() {
1922
addSubview(v)
@@ -75,7 +78,7 @@ open class HStackView: UIView {
7578
let w = effectiveSubviews.map({ $0.bounds }).reduce(CGRect.zero) { result, rect in
7679
result.union(rect)
7780
}.height
78-
return CGSize(width: h, height: w)
81+
return CGSize(width: h + paddingRight, height: w + paddingVertically)
7982
}
8083

8184
open func hideIfNoEffectiveViews() {
@@ -95,11 +98,11 @@ open class HStackView: UIView {
9598

9699
switch alignment {
97100
case .top:
98-
effectiveSubviews.forEach { $0.frame.origin.y = 0 }
101+
effectiveSubviews.forEach { $0.frame.origin.y = _stackContentRect.minY }
99102
case .center:
100-
effectiveSubviews.forEach { $0.center.y = frame.height / 2 }
103+
effectiveSubviews.forEach { $0.center.y = _stackContentRect.midY }
101104
case .bottom:
102-
effectiveSubviews.forEach { $0.frame.origin.y = frame.height - $0.frame.height }
105+
effectiveSubviews.forEach { $0.frame.origin.y = _stackContentRect.maxY - $0.frame.height }
103106
}
104107

105108
switch distribution {
@@ -153,26 +156,6 @@ open class HStackView: UIView {
153156
}
154157
}
155158

156-
extension HStackView {
157-
158-
private func spacerViews() -> [SpacerView] {
159-
effectiveSubviews.compactMap({ $0 as? SpacerView })
160-
}
161-
private func dynamicSpacerViews() -> [SpacerView] {
162-
effectiveSubviews.compactMap({ $0 as? SpacerView }).filter({ $0.length == .greatestFiniteMagnitude })
163-
}
164-
private func dividerViews() -> [DividerView] {
165-
effectiveSubviews.compactMap({ $0 as? DividerView })
166-
}
167-
168-
private func viewsWithoutSpacer() -> [UIView] {
169-
effectiveSubviews.filter({ ($0 as? SpacerView) == nil })
170-
}
171-
private func viewsWithoutSpacerAndDivider() -> [UIView] {
172-
effectiveSubviews.filter({ ($0 as? SpacerView) == nil && ($0 as? DividerView) == nil })
173-
}
174-
}
175-
176159
extension HStackView {
177160

178161
/// 自动间距
@@ -185,9 +168,9 @@ extension HStackView {
185168
///
186169
private func autoSpacing() -> CGFloat {
187170
let unspacerViews = viewsWithoutSpacer()
188-
let spacersCount = spacerViews().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
171+
let spacersCount = spacerViews().map({ isSpacerBetweenInTwoViews(spacerView: $0) }).filter({ $0 }).count
189172
let number = unspacerViews.count - spacersCount - 1
190-
return (frame.width - viewsWidth() - spacerSpecifyLength()) / CGFloat(max(1, number))
173+
return (frame.width - viewsWidth() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number))
191174
}
192175

193176
private func viewsWidth() -> CGFloat {
@@ -197,7 +180,7 @@ extension HStackView {
197180
private func makeSpacing(_ spacing: CGFloat) {
198181
for (index, subview) in effectiveSubviews.enumerated() {
199182
if index == 0 {
200-
subview.frame.origin.x = 0
183+
subview.frame.origin.x = _stackContentRect.minX
201184
} else {
202185
let previousView = effectiveSubviews[index - 1]
203186
if (previousView as? SpacerView) != nil || (subview as? SpacerView) != nil { // spacer and view no spacing
@@ -215,18 +198,18 @@ extension HStackView {
215198
}
216199
for subview in effectiveSubviews {
217200
let oldHeight = subview.frame.height
218-
subview.frame.size.height = frame.height
201+
subview.frame.size.height = _stackContentWidth
219202

220203
// fix #https://github.com/iWECon/StackKit/issues/21
221204
guard alignment == .center else {
222205
continue
223206
}
224-
subview.frame.origin.y -= (frame.height - oldHeight) / 2
207+
subview.frame.origin.y -= (_stackContentWidth - oldHeight) / 2
225208
}
226209
}
227210

228211
private func fillWidth() {
229-
let maxW = frame.width - spacerSpecifyLength() - dividerSpecifyLength()
212+
let maxW = frame.width - lengthOfAllFixedLengthSpacer() - dividerSpecifyLength()
230213
var w = (maxW) / CGFloat(viewsWithoutSpacerAndDivider().count)
231214

232215
let unspacersView = viewsWithoutSpacerAndDivider()
@@ -265,27 +248,6 @@ extension HStackView {
265248
// MARK: Spacer
266249
extension HStackView {
267250

268-
// 取出固定 length 的 spacer
269-
private func spacerSpecifyLength() -> CGFloat {
270-
spacerViews()
271-
.map({ $0.setLength })
272-
.reduce(0, +)
273-
}
274-
275-
private func isSpacerBetweenViews(_ spacer: SpacerView) -> Bool {
276-
guard let index = effectiveSubviews.firstIndex(of: spacer) else {
277-
return false
278-
}
279-
280-
guard effectiveSubviews.count >= 3 else {
281-
return false
282-
}
283-
284-
let start: Int = 1
285-
let end: Int = effectiveSubviews.count - 2
286-
return (start ... end).contains(index)
287-
}
288-
289251
private func fillSpecifySpacer() {
290252
let spacers = effectiveSubviews.compactMap({ $0 as? SpacerView })
291253
for spacer in spacers {
@@ -302,7 +264,7 @@ extension HStackView {
302264
guard unspacerViews.count != effectiveSubviews.count else { return }
303265

304266
// 在 view 与 view 之间的 spacer view 数量: 两个 view 夹一个 spacer view
305-
let betweenInViewsCount = spacerViews().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
267+
let betweenInViewsCount = spacerViews().map({ isSpacerBetweenInTwoViews(spacerView: $0) }).filter({ $0 }).count
306268
// 非 spacer view 的总宽度
307269
let unspacerViewsWidth = viewsWidth()
308270
// 排除 spacer view 后的间距
@@ -329,7 +291,7 @@ extension HStackView {
329291

330292
// 非 spacerView 的所有宽度
331293
let unspacerViewsMaxWidth = unspacerViewsWidth + unspacerViewsSpacing
332-
let spacersWidth = (frame.width - unspacerViewsMaxWidth - self.spacerSpecifyLength())
294+
let spacersWidth = (frame.width - unspacerViewsMaxWidth - self.lengthOfAllFixedLengthSpacer())
333295
let spacerWidth = spacersWidth / CGFloat(self.dynamicSpacerViews().count)
334296

335297
let spacerViews = self.spacerViews()

Sources/StackKit/Layer+LayerWraperView/VStackLayer.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ extension VStackLayer {
144144
let unspacerViews = viewsWithoutSpacer()
145145
let spacersCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
146146
let number = unspacerViews.count - spacersCount - 1
147-
return Swift.max(0, (frame.height - viewsHeight() - spacerSpecifyLength()) / CGFloat(max(1, number)))
147+
return Swift.max(0, (frame.height - viewsHeight() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number)))
148148
}
149149

150150
private func viewsHeight() -> CGFloat {
@@ -176,7 +176,7 @@ extension VStackLayer {
176176
///
177177
/// 填充高度, 所有视图(排除 spacer)高度一致
178178
private func fillHeight() {
179-
let maxH = frame.height - spacerSpecifyLength() - dividerSpecifyLength()
179+
let maxH = frame.height - lengthOfAllFixedLengthSpacer() - dividerSpecifyLength()
180180
var h = (maxH) / CGFloat(viewsWithoutSpacerAndDivider().count)
181181

182182
let unspacersView = viewsWithoutSpacerAndDivider()
@@ -215,7 +215,7 @@ extension VStackLayer {
215215
extension VStackLayer {
216216

217217
// 取出固定 length 的 spacer
218-
private func spacerSpecifyLength() -> CGFloat {
218+
private func lengthOfAllFixedLengthSpacer() -> CGFloat {
219219
spacerLayers()
220220
.map({ $0.setLength })
221221
.reduce(0, +)
@@ -268,7 +268,7 @@ extension VStackLayer {
268268
}
269269

270270
let unspacerViewsMaxHeight = unspacerViewsHeight + unspacerViewsSpacing
271-
let spacersHeight = (frame.height - unspacerViewsMaxHeight - self.spacerSpecifyLength())
271+
let spacersHeight = (frame.height - unspacerViewsMaxHeight - self.lengthOfAllFixedLengthSpacer())
272272
let spacerWidth = spacersHeight / CGFloat(self.dynamicSpacerLayers().count)
273273

274274
let spacerViews = self.spacerLayers()

Sources/StackKit/Layer+LayerWraperView/VStackLayerContainerView.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ open class VStackLayerContainerView: UIView {
5757
vStackLayer.sizeThatFits(size)
5858
}
5959

60+
open override func sizeToFit() {
61+
vStackLayer.sizeToFit()
62+
}
63+
6064
open func addContent(@_StackKitVStackLayerContentResultBuilder _ content: () -> [CALayer]) {
6165
vStackLayer.addContent(content)
6266
}

0 commit comments

Comments
 (0)