Skip to content

Commit 9037497

Browse files
committed
Update
1 parent 668e850 commit 9037497

6 files changed

Lines changed: 233 additions & 32 deletions

File tree

Sources/StackKit/StackLayer/HStackLayer.swift renamed to Sources/StackKit/Layer/HStackLayer.swift

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ extension HStackLayer {
139139

140140
private func autoSpacing() -> CGFloat {
141141
let unspacerViews = viewsWithoutSpacer()
142-
let spacersCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
142+
let spacersCount = spacerLayers().map({ isSpacerBetweenInTwoLayers(spacerLayer: $0) }).filter({ $0 }).count
143143
let number = unspacerViews.count - spacersCount - 1
144144
return Swift.max(0, (frame.width - viewsWidth() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number)))
145145
}
@@ -211,20 +211,6 @@ extension HStackLayer {
211211
// MARK: Spacer
212212
extension HStackLayer {
213213

214-
private func isSpacerBetweenViews(_ spacer: SpacerLayer) -> Bool {
215-
guard let index = effectiveSublayers.firstIndex(of: spacer) else {
216-
return false
217-
}
218-
219-
guard effectiveSublayers.count >= 3 else {
220-
return false
221-
}
222-
223-
let start: Int = 1
224-
let end: Int = effectiveSublayers.count - 2
225-
return (start ... end).contains(index)
226-
}
227-
228214
private func fillSpecifySpacer() {
229215
let spacers = effectiveSublayers.compactMap({ $0 as? SpacerLayer })
230216
for spacer in spacers {
@@ -241,7 +227,7 @@ extension HStackLayer {
241227
guard unspacerViews.count != effectiveSublayers.count else { return }
242228

243229
// 在 view 与 view 之间的 spacer view 数量: 两个 view 夹一个 spacer view
244-
let betweenInViewsCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
230+
let betweenInViewsCount = spacerLayers().map({ isSpacerBetweenInTwoLayers(spacerLayer: $0) }).filter({ $0 }).count
245231
// 非 spacer view 的总宽度
246232
let unspacerViewsWidth = viewsWidth()
247233
// 排除 spacer view 后的间距
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import UIKit
2+
3+
///
4+
/// All subviews(`UIView`) will be converted to `CALayer` to display.
5+
/// 该类仅作展示使用,所有的 UIView 均会被转换为 CALayer 作为显示
6+
///
7+
/// There may be performance problems in heavy use. It is recommended to use `VStackView` / `HStackView` when there are many views.
8+
/// 大量使用可能会有性能问题, 视图较多时建议直接使用 `VStackView` / `HStackView` 或使用 `H/VStackLayer` 替代.
9+
///
10+
open class HStackLayerWrapperView: _StackLayerWrapperView {
11+
12+
open override class var layerClass: AnyClass {
13+
HStackLayer.self
14+
}
15+
16+
public var hStackLayer: HStackLayer {
17+
self.layer as! HStackLayer
18+
}
19+
20+
public required init(
21+
alignment: HStackAlignment = .center,
22+
distribution: HStackDistribution = .autoSpacing,
23+
padding: UIEdgeInsets = .zero,
24+
@_StackKitHStackContentResultBuilder content: () -> [UIView] = { [] }
25+
) {
26+
super.init(frame: .zero)
27+
hStackLayer.alignment = alignment
28+
hStackLayer.distribution = distribution
29+
hStackLayer.padding = padding
30+
31+
for v in content() {
32+
addSubview(v)
33+
}
34+
}
35+
36+
public required init?(coder: NSCoder) {
37+
super.init(coder: coder)
38+
}
39+
40+
public func addContent(@_StackKitHStackContentResultBuilder _ content: () -> [UIView]) {
41+
for v in content() {
42+
addSubview(v)
43+
}
44+
}
45+
46+
public func resetContent(@_StackKitHStackContentResultBuilder _ content: () -> [UIView]) {
47+
subviews.forEach { $0.removeFromSuperview() }
48+
for v in content() {
49+
addSubview(v)
50+
}
51+
}
52+
53+
open override func convertDividerView(_ subview: UIView) -> Bool {
54+
guard let dividerView = subview as? DividerView else { return false }
55+
56+
let dividerLayer = DividerLayer()
57+
dividerLayer.maxLength = dividerView.maxLength
58+
dividerLayer.frame.size.width = dividerView.thickness
59+
dividerLayer.backgroundColor = dividerView.backgroundColor?.cgColor
60+
dividerLayer.cornerRadius = dividerView.cornerRadius
61+
layer.addSublayer(dividerLayer)
62+
63+
return true
64+
}
65+
66+
open override func sizeThatFits(_ size: CGSize) -> CGSize {
67+
hStackLayer.sizeThatFits(size)
68+
}
69+
}

Sources/StackKit/StackLayer/StackLayer.swift renamed to Sources/StackKit/Layer/StackLayer.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,19 @@ extension StackLayer where Self: CALayer {
7575
func lengthOfAllFixedLengthDivier() -> CGFloat {
7676
dividerLayers().map { $0.thickness }.reduce(0, +)
7777
}
78+
79+
func isSpacerBetweenInTwoLayers(spacerLayer: SpacerLayer) -> Bool {
80+
guard let index = effectiveSublayers.firstIndex(of: spacerLayer) else {
81+
return false
82+
}
83+
84+
guard effectiveSublayers.count >= 3 else {
85+
return false
86+
}
87+
88+
let start: Int = 1
89+
let end: Int = effectiveSublayers.count - 2
90+
return (start ... end).contains(index)
91+
}
92+
7893
}

Sources/StackKit/StackLayer/VStackLayer.swift renamed to Sources/StackKit/Layer/VStackLayer.swift

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ extension VStackLayer {
141141

142142
private func autoSpacing() -> CGFloat {
143143
let unspacerViews = viewsWithoutSpacer()
144-
let spacersCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
144+
let spacersCount = spacerLayers().map({ isSpacerBetweenInTwoLayers(spacerLayer: $0) }).filter({ $0 }).count
145145
let number = unspacerViews.count - spacersCount - 1
146146
return Swift.max(0, (frame.height - viewsHeight() - lengthOfAllFixedLengthSpacer()) / CGFloat(max(1, number)))
147147
}
@@ -215,20 +215,6 @@ extension VStackLayer {
215215
// MARK: Spacer
216216
extension VStackLayer {
217217

218-
private func isSpacerBetweenViews(_ spacer: SpacerLayer) -> Bool {
219-
guard let index = effectiveSublayers.firstIndex(of: spacer) else {
220-
return false
221-
}
222-
223-
guard effectiveSublayers.count >= 3 else {
224-
return false
225-
}
226-
227-
let start: Int = 1
228-
let end: Int = effectiveSublayers.count - 2
229-
return (start ... end).contains(index)
230-
}
231-
232218
private func fillSpecifySpacer() {
233219
let spacers = effectiveSublayers.compactMap({ $0 as? SpacerLayer })
234220
for spacer in spacers {
@@ -241,7 +227,7 @@ extension VStackLayer {
241227
let unspacerViews = viewsWithoutSpacer()
242228
guard unspacerViews.count != effectiveSublayers.count else { return }
243229

244-
let betweenInViewsCount = spacerLayers().map({ isSpacerBetweenViews($0) }).filter({ $0 }).count
230+
let betweenInViewsCount = spacerLayers().map({ isSpacerBetweenInTwoLayers(spacerLayer: $0) }).filter({ $0 }).count
245231
let unspacerViewsHeight = viewsHeight()
246232
// 排除 spacer view 后的间距
247233
let unspacerViewsSpacing: CGFloat
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import UIKit
2+
3+
///
4+
/// All subviews(`UIView`) will be converted to `CALayer` to display.
5+
/// 该类仅作展示使用,所有的 UIView 均会被转换为 CALayer 作为显示
6+
///
7+
/// There may be performance problems in heavy use. It is recommended to use `VStackView` / `HStackView` when there are many views.
8+
/// 大量使用可能会有性能问题, 视图较多时建议直接使用 `VStackView` / `HStackView` 或使用 `H/VStackLayer` 替代.
9+
///
10+
open class VStackLayerWrapperView: _StackLayerWrapperView {
11+
12+
open override class var layerClass: AnyClass {
13+
VStackLayer.self
14+
}
15+
16+
public var vStackLayer: VStackLayer {
17+
self.layer as! VStackLayer
18+
}
19+
20+
public required init(
21+
alignment: VStackAlignment = .center,
22+
distribution: VStackDistribution = .spacing(2),
23+
padding: UIEdgeInsets = .zero,
24+
@_StackKitVStackContentResultBuilder content: () -> [UIView] = { [] }
25+
) {
26+
super.init(frame: .zero)
27+
vStackLayer.alignment = alignment
28+
vStackLayer.distribution = distribution
29+
vStackLayer.padding = padding
30+
31+
for v in content() {
32+
addSubview(v)
33+
}
34+
}
35+
36+
public required init?(coder: NSCoder) {
37+
super.init(coder: coder)
38+
}
39+
40+
public func addContent(@_StackKitVStackContentResultBuilder _ content: () -> [UIView]) {
41+
for v in content() {
42+
addSubview(v)
43+
}
44+
}
45+
46+
public func resetContent(@_StackKitVStackContentResultBuilder _ content: () -> [UIView]) {
47+
subviews.forEach { $0.removeFromSuperview() }
48+
for v in content() {
49+
addSubview(v)
50+
}
51+
}
52+
53+
open override func convertDividerView(_ subview: UIView) -> Bool {
54+
guard let dividerView = subview as? DividerView else { return false }
55+
56+
let dividerLayer = DividerLayer()
57+
dividerLayer.maxLength = dividerView.maxLength
58+
dividerLayer.frame.size.height = dividerView.thickness
59+
dividerLayer.backgroundColor = dividerView.backgroundColor?.cgColor
60+
dividerLayer.cornerRadius = dividerView.cornerRadius
61+
layer.addSublayer(dividerLayer)
62+
63+
return true
64+
}
65+
66+
open override func sizeThatFits(_ size: CGSize) -> CGSize {
67+
vStackLayer.sizeThatFits(size)
68+
}
69+
70+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import UIKit
2+
3+
open class _StackLayerWrapperView: UIView {
4+
5+
open override func didAddSubview(_ subview: UIView) {
6+
super.didAddSubview(subview)
7+
defer {
8+
subview.removeFromSuperview()
9+
}
10+
11+
subview._fitSize(with: subview._stackKit_fitType)
12+
13+
// Copy UIImageView
14+
if convertUIImageView(subview) {
15+
return
16+
}
17+
if convertDividerView(subview) {
18+
return
19+
}
20+
if convertSpacerView(subview) {
21+
return
22+
}
23+
convertViewToImage(subview)
24+
}
25+
26+
private func convertUIImageView(_ subview: UIView) -> Bool {
27+
guard let imageView = subview as? UIImageView else { return false }
28+
let imageLayer = CALayer()
29+
30+
// fix subview.layer.contents is nil below on iOS 15?
31+
//
32+
// test on iOS 14.3, subview.layer.contents is nil,
33+
// test on iOS 15, subview.layer.contents has some value.
34+
// ⚠️ ❌ imageLayer.contents = subview.layer.contents
35+
36+
imageLayer.contents = imageView.image?.cgImage
37+
imageLayer.bounds = subview.layer.bounds
38+
imageLayer.backgroundColor = subview.backgroundColor?.cgColor
39+
layer.addSublayer(imageLayer)
40+
41+
return true
42+
}
43+
44+
/// override in subclass
45+
open func convertDividerView(_ subview: UIView) -> Bool {
46+
false
47+
}
48+
49+
private func convertSpacerView(_ subview: UIView) -> Bool {
50+
guard let spacerView = subview as? SpacerView else { return false }
51+
52+
let spacerLayer = spacerView.spacerLayer
53+
layer.addSublayer(spacerLayer)
54+
55+
return true
56+
}
57+
58+
private func convertViewToImage(_ subview: UIView) {
59+
// render view to image and create CALayer with it
60+
//
61+
// If you use `view.layer` directly, there may be problems with display, for example: `UILabel` -> `_UILabelLayer` cannot display text normally
62+
// 如果直接使用 view.layer 可能显示会有问题, 例如: UILabel -> _UILabelLayer 无法正常显示文字
63+
let image = UIGraphicsImageRenderer(bounds: subview.bounds).image { context in
64+
subview.layer.render(in: context.cgContext)
65+
}
66+
let tempLayer = CALayer()
67+
tempLayer.contents = image.cgImage
68+
tempLayer.bounds = subview.bounds
69+
layer.addSublayer(tempLayer)
70+
}
71+
72+
open override var intrinsicContentSize: CGSize {
73+
sizeThatFits(.zero)
74+
}
75+
}

0 commit comments

Comments
 (0)