Skip to content

Commit 8e375cd

Browse files
UIStackViewDynamicSpaceProvider entity instead closure-in-closure provider
1 parent dc0d258 commit 8e375cd

File tree

4 files changed

+118
-19
lines changed

4 files changed

+118
-19
lines changed

Sources/DeclarativeLayoutKit/AutoLayout/AnchorsFactory.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public extension AutoLayoutItemConvertible {
9292
view: view,
9393
anchorProvider: { $0.trailingAnchor },
9494
second: anchor.asHorizontalRelativeAutoLayoutAnchor(),
95-
invertConstant: false
95+
invertConstant: true
9696
)
9797
})
9898
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//
2+
// File.swift
3+
//
4+
//
5+
// Created by Ernest Babayan on 05.07.2021.
6+
//
7+
8+
import Foundation
9+
10+
func onMainThread<Input>(_ handler: @escaping (Input) -> Void) -> (Input) -> Void {
11+
return { (input: Input) in
12+
guard !Thread.isMainThread else {
13+
return handler(input)
14+
}
15+
16+
DispatchQueue.main.async(execute: { handler(input) })
17+
}
18+
}

Sources/DeclarativeLayoutKit/StackView/UIStackViewSpace.swift

Lines changed: 52 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public func UIStackViewSpace(_ value: CGFloat, withSummation: Bool = false) -> U
2525
stackView.setCustomSpacing(finalSpace, after: view)
2626
}
2727
}
28+
2829
public func UIStackViewSpace(_ value: CGFloat, bindVisibilityTo view: UIView) -> UIStackViewConfiguration {
2930
AnyUIStackViewConfiguration { [weak view] (stackView: UIStackView) in
3031
guard let view = view else { return }
@@ -47,18 +48,65 @@ public func UIStackViewSpace(_ value: CGFloat, bindVisibilityTo view: UIView) ->
4748
}
4849
}
4950

50-
public typealias UIStackViewSpaceObserver = (CGFloat) -> Void
51-
public typealias UIStackViewSpaceProvider = (UIStackViewSpaceObserver) -> Void
51+
public final class UIStackViewDynamicSpaceProvider {
52+
public init(_ initialValue: CGFloat = UIStackView.spacingUseDefault) {
53+
self.currentValue = initialValue
54+
}
55+
56+
private(set) public var currentValue: CGFloat
57+
58+
private var observers: [(CGFloat) -> ()] = []
59+
60+
fileprivate func addObserver(_ observer: @escaping (CGFloat) -> Void) {
61+
observers.append(observer)
62+
observer(currentValue)
63+
}
64+
65+
public func update(with newValue: CGFloat) {
66+
currentValue = newValue
67+
for observer in observers { observer(currentValue) }
68+
}
69+
}
70+
71+
public extension UIStackViewDynamicSpaceProvider {
72+
static func binding(space: CGFloat, toVisibility view: UIView) -> UIStackViewDynamicSpaceProvider {
73+
func getSpace(isHidden: Bool) -> CGFloat {
74+
if isHidden { return UIStackView.spacingUseDefault }
75+
else { return space }
76+
}
77+
78+
let provider = UIStackViewDynamicSpaceProvider(getSpace(isHidden: view.isHidden))
79+
80+
let subscription = view.observe(
81+
\.isHidden,
82+
options: [.new],
83+
changeHandler: { [weak provider] _, update in
84+
guard let newSpace = update.newValue.map(getSpace(isHidden:))
85+
else { return}
86+
87+
provider?.update(with: newSpace)
88+
})
89+
90+
whenDeallocated(view, { [weak provider] in
91+
provider?.update(with: getSpace(isHidden: true))
92+
subscription.invalidate()
93+
})
94+
95+
return provider
96+
}
97+
}
5298

53-
public func UIStackViewSpace(dynamic provider: @escaping UIStackViewSpaceProvider) -> UIStackViewConfiguration {
99+
public func UIStackViewSpace(_ provider: UIStackViewDynamicSpaceProvider) -> UIStackViewConfiguration {
54100
AnyUIStackViewConfiguration { (stackView: UIStackView) in
55101
let view = stackView.lastArrangedView
56102

57-
provider({ [weak stackView, weak view] (newSpace: CGFloat) in
103+
provider.addObserver(onMainThread { [weak stackView, weak view] (newSpace: CGFloat) in
58104
guard let view = view else { return }
59105

60106
stackView?.setCustomSpacing(newSpace, after: view)
61107
})
108+
109+
whenDeallocated(stackView, { _ = provider })
62110
}
63111
}
64112

@@ -72,18 +120,4 @@ private extension UIStackView {
72120
}
73121

74122
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-
}
89123
}

Tests/DeclarativeLayoutKitTests/Tests.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,58 @@ final class Tests: XCTestCase {
3737
UIStackViewSpace(10)
3838

3939
UIStackViewSpace(1, withSummation: true)
40+
41+
4042
}
4143

4244
XCTAssertEqual(
4345
stackView.customSpacing(after: afterSpacedView),
4446
11
4547
)
4648
}
49+
50+
func test_stackview_dynamic_spacing() {
51+
let spaceProvider = UIStackViewDynamicSpaceProvider(1)
52+
let afterSpacedView = UIView()
53+
54+
let stackView = HorizontalStack {
55+
afterSpacedView
56+
57+
UIStackViewSpace(spaceProvider)
58+
}
59+
60+
61+
var currentSpace: CGFloat {
62+
stackView.customSpacing(after: afterSpacedView)
63+
}
64+
65+
XCTAssertEqual(currentSpace, 1)
66+
XCTAssertEqual(currentSpace, stackView.customSpacing(after: afterSpacedView))
67+
68+
spaceProvider.update(with: 5)
69+
XCTAssertEqual(currentSpace, 5)
70+
XCTAssertEqual(currentSpace, stackView.customSpacing(after: afterSpacedView))
71+
}
72+
73+
func test_stackview_dynamic_spacing_fromViewVisibility() {
74+
let afterSpacedView = UIView()
75+
76+
let stackView = HorizontalStack {
77+
afterSpacedView
78+
79+
UIStackViewSpace(.binding(space: 10, toVisibility: afterSpacedView))
80+
}
81+
82+
var currentSpace: CGFloat {
83+
stackView.customSpacing(after: afterSpacedView)
84+
}
85+
86+
XCTAssertEqual(currentSpace, 10)
87+
88+
afterSpacedView.isHidden = true
89+
XCTAssertEqual(currentSpace, UIStackView.spacingUseDefault)
90+
91+
afterSpacedView.isHidden = false
92+
XCTAssertEqual(currentSpace, 10)
93+
}
4794
}

0 commit comments

Comments
 (0)