Skip to content

Commit 34c683b

Browse files
Remake UIStackViewElement into UIStackViewConfiguration for more flexibility
1 parent ac84953 commit 34c683b

File tree

8 files changed

+187
-91
lines changed

8 files changed

+187
-91
lines changed

Sources/DeclarativeLayoutKit/AutoLayout/AutoLayoutItem.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
import UIKit
99

1010

11-
public protocol AutoLayoutItemConvertible: UIStackViewElementConvertable {
11+
public protocol AutoLayoutItemConvertible: UIStackViewConfiguration {
1212
func asAutoLayoutItem() -> AutoLayoutItem
1313
}
1414

1515
extension AutoLayoutItemConvertible {
16-
public func asUIStackViewElement() -> UIStackViewElement {
17-
UIStackViewElement.arranged(self)
16+
public func configure(stackView: UIStackView) {
17+
let item = self.asAutoLayoutItem()
18+
stackView.addArrangedSubview(item.view)
19+
item.move(to: stackView)
20+
item.activate()
1821
}
1922
}
2023

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Ernest Babayan on 04.07.2021.
6+
//
7+
8+
import Foundation
9+
10+
private final class DeallocObserver {
11+
private let completion: () -> Void
12+
13+
@discardableResult
14+
fileprivate init(
15+
_ object: AnyObject,
16+
_ completion: @escaping () -> Void
17+
) {
18+
self.completion = completion
19+
objc_setAssociatedObject(object, "DeallocObserver", self, .OBJC_ASSOCIATION_RETAIN)
20+
}
21+
22+
deinit { completion() }
23+
}
24+
25+
func whenDeallocated(_ object: AnyObject, _ completion: @escaping () -> Void) {
26+
DeallocObserver(object, completion)
27+
}

Sources/DeclarativeLayoutKit/StackView/UIStackViewBuilder.swift

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import UIKit
1010

1111
public extension UIStackView {
1212
/// Reset arrangedSubiews
13-
func set(elements: [UIStackViewElementConvertable]) {
13+
func set(elements: [UIStackViewConfiguration]) {
1414
for subview in arrangedSubviews {
1515
removeArrangedSubview(subview)
1616
subview.removeFromSuperview()
@@ -19,31 +19,9 @@ public extension UIStackView {
1919
append(elements: elements)
2020
}
2121

22-
func append(elements: [UIStackViewElementConvertable]) {
22+
func append(elements: [UIStackViewConfiguration]) {
2323
for element in elements {
24-
switch element.asUIStackViewElement() {
25-
case UIStackViewElement.space(let space):
26-
guard let view = arrangedSubviews.last else {
27-
assertionFailure("You cannot add a space before the first arranged view")
28-
continue
29-
}
30-
31-
var finalSpace = space
32-
33-
let currentSpaces = customSpacing(after: view)
34-
let systemSpaces = [UIStackView.spacingUseDefault, UIStackView.spacingUseSystem]
35-
if !systemSpaces.contains(currentSpaces) {
36-
finalSpace += currentSpaces
37-
}
38-
39-
setCustomSpacing(finalSpace, after: view)
40-
41-
case UIStackViewElement.arranged(let itemBox):
42-
let item = itemBox.asAutoLayoutItem()
43-
addArrangedSubview(item.view)
44-
item.move(to: self)
45-
item.activate()
46-
}
24+
element.configure(stackView: self)
4725
}
4826
}
4927
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Ernest Babayan on 05.06.2021.
6+
//
7+
8+
import UIKit
9+
10+
11+
public protocol UIStackViewConfiguration {
12+
func configure(stackView: UIStackView)
13+
}
14+
15+
public struct AnyUIStackViewConfiguration: UIStackViewConfiguration {
16+
public init(_ configuration: @escaping (UIStackView) -> Void) {
17+
self.configuration = configuration
18+
}
19+
20+
let configuration: (UIStackView) -> Void
21+
22+
public func configure(stackView: UIStackView) {
23+
configuration(stackView)
24+
}
25+
}

Sources/DeclarativeLayoutKit/StackView/UIStackViewElement.swift

Lines changed: 0 additions & 40 deletions
This file was deleted.

Sources/DeclarativeLayoutKit/StackView/UIStackViewHelpers.swift

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,105 +8,105 @@
88
import UIKit
99

1010

11-
public typealias ArrangedViewsBuilder = ArrayBuilder<UIStackViewElementConvertable>
11+
public typealias StackViewConfigurationsComposer = ArrayBuilder<UIStackViewConfiguration>
1212

1313

1414
public extension UIStackView {
1515
@discardableResult
16-
func set(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> Self {
16+
func set(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> Self {
1717
set(elements: elements())
1818
return self
1919
}
2020

2121
@discardableResult
22-
func append(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> Self {
22+
func append(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> Self {
2323
append(elements: elements())
2424
return self
2525
}
2626
}
2727

2828
// MARK: - Horizontal
2929

30-
public func HorizontalStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
30+
public func HorizontalStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
3131
let stack = UIStackView()
3232
stack.axis = NSLayoutConstraint.Axis.horizontal
3333
stack.append(elements: elements)
3434
return stack
3535
}
3636

37-
public func HorizontalStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
37+
public func HorizontalStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
3838
HorizontalStack(elements())
3939
}
4040

41-
public func TopStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
41+
public func TopStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
4242
let stack = HorizontalStack(elements)
4343
stack.alignment = .top
4444
return stack
4545
}
4646

47-
public func TopStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
47+
public func TopStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
4848
TopStack(elements())
4949
}
5050

51-
public func BottomStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
51+
public func BottomStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
5252
let stack = HorizontalStack(elements)
5353
stack.alignment = .bottom
5454
return stack
5555
}
5656

57-
public func BottomStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
57+
public func BottomStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
5858
BottomStack(elements())
5959
}
6060

61-
public func HorizontalCenterStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
61+
public func HorizontalCenterStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
6262
let stack = HorizontalStack(elements)
6363
stack.alignment = .center
6464
return stack
6565
}
6666

67-
public func HorizontalCenterStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
67+
public func HorizontalCenterStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
6868
HorizontalCenterStack(elements())
6969
}
7070

7171
// MARK: - Vertical
7272

73-
public func VerticalStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
73+
public func VerticalStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
7474
let stack = UIStackView()
7575
stack.axis = NSLayoutConstraint.Axis.vertical
7676
stack.append(elements: elements)
7777
return stack
7878
}
7979

80-
public func VerticalStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
80+
public func VerticalStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
8181
VerticalStack(elements())
8282
}
8383

84-
public func LeftStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
84+
public func LeftStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
8585
let stack = VerticalStack(elements)
8686
stack.alignment = .leading
8787
return stack
8888
}
8989

90-
public func LeftStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
90+
public func LeftStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
9191
LeftStack(elements())
9292
}
9393

94-
public func RightStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
94+
public func RightStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
9595
let stack = VerticalStack(elements)
9696
stack.alignment = .trailing
9797
return stack
9898
}
9999

100-
public func RightStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
100+
public func RightStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
101101
RightStack(elements())
102102
}
103103

104-
public func VerticalCenterStack(_ elements: [UIStackViewElementConvertable]) -> UIStackView {
104+
public func VerticalCenterStack(_ elements: [UIStackViewConfiguration]) -> UIStackView {
105105
let stack = VerticalStack(elements)
106106
stack.alignment = .center
107107
return stack
108108
}
109109

110-
public func VerticalCenterStack(@ArrangedViewsBuilder _ elements: () -> [UIStackViewElementConvertable]) -> UIStackView {
110+
public func VerticalCenterStack(@StackViewConfigurationsComposer _ elements: () -> [UIStackViewConfiguration]) -> UIStackView {
111111
VerticalCenterStack(elements())
112112
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Ernest Babayan on 04.07.2021.
6+
//
7+
8+
import UIKit
9+
10+
11+
public func UIStackViewSpace(_ value: CGFloat, withSummation: Bool = false) -> UIStackViewConfiguration {
12+
AnyUIStackViewConfiguration { (stackView: UIStackView) in
13+
let view = stackView.lastArrangedView
14+
15+
var finalSpace = value
16+
17+
if withSummation {
18+
let currentSpaces = stackView.customSpacing(after: view)
19+
20+
if !UIStackView.systemSpaces.contains(currentSpaces) {
21+
finalSpace += currentSpaces
22+
}
23+
}
24+
25+
stackView.setCustomSpacing(finalSpace, after: view)
26+
}
27+
}
28+
public func UIStackViewSpace(_ value: CGFloat, bindVisibilityTo view: UIView) -> UIStackViewConfiguration {
29+
AnyUIStackViewConfiguration { [weak view] (stackView: UIStackView) in
30+
guard let view = view else { return }
31+
32+
let subscription = view.observe(
33+
\.isHidden,
34+
35+
options: [.initial, .new],
36+
37+
changeHandler: { [weak stackView] view, change in
38+
guard let isHidden = change.newValue else { return }
39+
40+
stackView?.setCustomSpacing(
41+
isHidden ? UIStackView.spacingUseDefault : value,
42+
after: view
43+
)
44+
})
45+
46+
whenDeallocated(view, subscription.invalidate)
47+
}
48+
}
49+
50+
public typealias UIStackViewSpaceObserver = (CGFloat) -> Void
51+
public typealias UIStackViewSpaceProvider = (UIStackViewSpaceObserver) -> Void
52+
53+
public func UIStackViewSpace(dynamic provider: @escaping UIStackViewSpaceProvider) -> UIStackViewConfiguration {
54+
AnyUIStackViewConfiguration { (stackView: UIStackView) in
55+
let view = stackView.lastArrangedView
56+
57+
provider({ [weak stackView, weak view] (newSpace: CGFloat) in
58+
guard let view = view else { return }
59+
60+
stackView?.setCustomSpacing(newSpace, after: view)
61+
})
62+
}
63+
}
64+
65+
private extension UIStackView {
66+
var lastArrangedView: UIView {
67+
guard let view = arrangedSubviews.last else {
68+
preconditionFailure("You cannot add a space before the first arranged view")
69+
}
70+
71+
return view
72+
}
73+
74+
static let systemSpaces = [UIStackView.spacingUseDefault, UIStackView.spacingUseSystem]
75+
76+
func observeArrangedSubviews(_ observer: @escaping ([UIView]) -> Void) {
77+
let subscription = layer.observe(
78+
\.sublayers,
79+
80+
options: [.initial, .new],
81+
82+
changeHandler: { [unowned self] _, _ in
83+
observer(arrangedSubviews)
84+
}
85+
)
86+
87+
whenDeallocated(self, subscription.invalidate)
88+
}
89+
}

0 commit comments

Comments
 (0)