Skip to content

Commit 6ad4e5f

Browse files
authored
Merge pull request #34 from iWECon/wrap-stack
WrapStack Support
2 parents 1a44b09 + 874e675 commit 6ad4e5f

File tree

10 files changed

+483
-15
lines changed

10 files changed

+483
-15
lines changed

Demo/Demo.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
080677C628BE7D8B00000E16 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 080677C528BE7D8B00000E16 /* ViewController.swift */; };
1111
080677C828BE7DA400000E16 /* WrapStackDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 080677C728BE7DA400000E16 /* WrapStackDemoViewController.swift */; };
1212
0815C093292A2B37001655C6 /* Preview1ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0815C092292A2B37001655C6 /* Preview1ViewController.swift */; };
13+
083B50042A36C54100778E58 /* WrapStackLayerDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083B50032A36C54100778E58 /* WrapStackLayerDemoViewController.swift */; };
1314
08A24B8428E49A9600CDD7E2 /* LookinServer in Frameworks */ = {isa = PBXBuildFile; productRef = 08A24B8328E49A9600CDD7E2 /* LookinServer */; };
1415
08E16CD9289ABA1C0019D7CB /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E16CD8289ABA1C0019D7CB /* AppDelegate.swift */; };
1516
08E16CDB289ABA1C0019D7CB /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08E16CDA289ABA1C0019D7CB /* SceneDelegate.swift */; };
@@ -25,6 +26,7 @@
2526
080677C528BE7D8B00000E16 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
2627
080677C728BE7DA400000E16 /* WrapStackDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrapStackDemoViewController.swift; sourceTree = "<group>"; };
2728
0815C092292A2B37001655C6 /* Preview1ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preview1ViewController.swift; sourceTree = "<group>"; };
29+
083B50032A36C54100778E58 /* WrapStackLayerDemoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WrapStackLayerDemoViewController.swift; sourceTree = "<group>"; };
2830
08E16CD5289ABA1C0019D7CB /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
2931
08E16CD8289ABA1C0019D7CB /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
3032
08E16CDA289ABA1C0019D7CB /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
@@ -77,6 +79,7 @@
7779
0815C092292A2B37001655C6 /* Preview1ViewController.swift */,
7880
08E16CDC289ABA1C0019D7CB /* HVStackDemoViewController.swift */,
7981
080677C728BE7DA400000E16 /* WrapStackDemoViewController.swift */,
82+
083B50032A36C54100778E58 /* WrapStackLayerDemoViewController.swift */,
8083
08E16CDE289ABA1C0019D7CB /* Main.storyboard */,
8184
08E16CE1289ABA1D0019D7CB /* Assets.xcassets */,
8285
08E16CE3289ABA1D0019D7CB /* LaunchScreen.storyboard */,
@@ -185,6 +188,7 @@
185188
08E16CDD289ABA1C0019D7CB /* HVStackDemoViewController.swift in Sources */,
186189
080677C828BE7DA400000E16 /* WrapStackDemoViewController.swift in Sources */,
187190
08E16CD9289ABA1C0019D7CB /* AppDelegate.swift in Sources */,
191+
083B50042A36C54100778E58 /* WrapStackLayerDemoViewController.swift in Sources */,
188192
08E16CDB289ABA1C0019D7CB /* SceneDelegate.swift in Sources */,
189193
);
190194
runOnlyForDeploymentPostprocessing = 0;

Demo/Demo/Base.lproj/Main.storyboard

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,40 @@
1818
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1919
<subviews>
2020
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="21" translatesAutoresizingMaskIntoConstraints="NO" id="M84-Lf-Gfo">
21-
<rect key="frame" x="135" y="375.5" width="144" height="145.5"/>
21+
<rect key="frame" x="132.5" y="347.5" width="149.5" height="201"/>
2222
<subviews>
2323
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7Tv-fS-6AC">
24-
<rect key="frame" x="0.0" y="0.0" width="144" height="34.5"/>
24+
<rect key="frame" x="0.0" y="0.0" width="149.5" height="34.5"/>
2525
<state key="normal" title="Button"/>
2626
<buttonConfiguration key="configuration" style="filled" title="Preview 1"/>
2727
<connections>
2828
<segue destination="tKx-sr-SVC" kind="show" id="uEI-YO-OWR"/>
2929
</connections>
3030
</button>
3131
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="06f-Ws-ezl">
32-
<rect key="frame" x="0.0" y="55.5" width="144" height="34.5"/>
32+
<rect key="frame" x="0.0" y="55.5" width="149.5" height="34.5"/>
3333
<state key="normal" title="Button"/>
3434
<buttonConfiguration key="configuration" style="filled" title="H/VStackView"/>
3535
<connections>
3636
<segue destination="hGA-TO-DpM" kind="show" id="CVz-1t-QBY"/>
3737
</connections>
3838
</button>
3939
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Dpb-UY-wFq">
40-
<rect key="frame" x="0.0" y="111" width="144" height="34.5"/>
40+
<rect key="frame" x="0.0" y="111" width="149.5" height="34.5"/>
4141
<state key="normal" title="Button"/>
4242
<buttonConfiguration key="configuration" style="filled" title="WrapStackView"/>
4343
<connections>
4444
<segue destination="qy3-1p-iRp" kind="show" id="npM-Qe-FDz"/>
4545
</connections>
4646
</button>
47+
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="l2O-iF-vtx">
48+
<rect key="frame" x="0.0" y="166.5" width="149.5" height="34.5"/>
49+
<state key="normal" title="Button"/>
50+
<buttonConfiguration key="configuration" style="filled" title="WrapStackLayer"/>
51+
<connections>
52+
<segue destination="mJ5-v7-Oak" kind="show" id="X1E-c0-Wwr"/>
53+
</connections>
54+
</button>
4755
</subviews>
4856
</stackView>
4957
</subviews>
@@ -126,6 +134,22 @@
126134
</objects>
127135
<point key="canvasLocation" x="3648" y="855"/>
128136
</scene>
137+
<!--WrapStackLayer-->
138+
<scene sceneID="Wzu-Wo-ZdA">
139+
<objects>
140+
<viewController id="mJ5-v7-Oak" customClass="WrapStackLayerDemoViewController" customModule="Demo" customModuleProvider="target" sceneMemberID="viewController">
141+
<view key="view" contentMode="scaleToFill" id="CMD-q7-Sic">
142+
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
143+
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
144+
<viewLayoutGuide key="safeArea" id="0ho-z9-azb"/>
145+
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
146+
</view>
147+
<navigationItem key="navigationItem" title="WrapStackLayer" largeTitleDisplayMode="never" id="Qy7-7k-TEV"/>
148+
</viewController>
149+
<placeholder placeholderIdentifier="IBFirstResponder" id="fjO-n9-FWN" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
150+
</objects>
151+
<point key="canvasLocation" x="3648" y="1550"/>
152+
</scene>
129153
</scenes>
130154
<resources>
131155
<systemColor name="systemBackgroundColor">

Demo/Demo/WrapStackDemoViewController.swift

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,76 @@ import UIKit
99
import StackKit
1010

1111
class WrapStackDemoViewController: UIViewController {
12+
13+
let fixedWrapStackView = WrapStackView(
14+
itemSize: .fixed(CGSize(width: 30, height: 30)),
15+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
16+
itemSpacing: 4,
17+
lineSpacing: 10
18+
)
19+
20+
let adaptiveWrapStackView = WrapStackView(
21+
itemSize: .adaptive(column: 6),
22+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
23+
itemSpacing: 10,
24+
lineSpacing: 4
25+
)
26+
27+
let autoWrapStackView = WrapStackView(
28+
itemSize: .auto,
29+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
30+
itemSpacing: 10,
31+
lineSpacing: 10
32+
)
1233

1334
override func viewDidLoad() {
1435
super.viewDidLoad()
1536

1637
// Do any additional setup after loading the view.
38+
let widths: [CGFloat] = [20, 30, 50, 120, 40, 230, 50, 60, 10]
39+
fixedWrapStackView.addContent {
40+
for _ in (0 ... 3) {
41+
makeUIView().stack.then { $0.frame.size = CGSize(width: widths.randomElement() ?? 10, height: 30) }
42+
}
43+
}
44+
view.addSubview(fixedWrapStackView)
1745

46+
adaptiveWrapStackView.addContent {
47+
for _ in (0 ... 11) {
48+
makeUIView().stack.then { $0.frame.size = CGSize(width: widths.randomElement() ?? 10, height: 30) }
49+
}
50+
}
51+
view.addSubview(adaptiveWrapStackView)
52+
53+
autoWrapStackView.addContent {
54+
for _ in (0 ... 9) {
55+
makeUIView().stack.then { $0.frame.size = CGSize(width: widths.randomElement() ?? 10, height: 30) }
56+
}
57+
makeUIView().stack.size(50, 30)
58+
makeUIView().stack.size(60, 30)
59+
makeUIView().stack.size(20, 30)
60+
makeUIView().stack.size(10, 30)
61+
}
62+
view.addSubview(autoWrapStackView)
1863
}
19-
64+
65+
private func makeUIView() -> UIView {
66+
let v = UIView()
67+
let red: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
68+
let green: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
69+
let blue: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
70+
v.backgroundColor = UIColor.init(red: red, green: green, blue: blue, alpha: 1)
71+
return v
72+
}
73+
74+
override func viewDidLayoutSubviews() {
75+
super.viewDidLayoutSubviews()
76+
77+
fixedWrapStackView.pin.top(120).horizontally().sizeToFit(.content)
78+
adaptiveWrapStackView.pin.top(to: fixedWrapStackView.edge.bottom).marginTop(50).horizontally().sizeToFit(.width)
79+
autoWrapStackView.pin.top(to: adaptiveWrapStackView.edge.bottom).marginTop(50).horizontally().sizeToFit(.width)
80+
}
81+
2082
/*
2183
// MARK: - Navigation
2284

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//
2+
// WrapStackLayerDemoViewController.swift
3+
// Demo
4+
//
5+
// Created by i on 2023/6/12.
6+
//
7+
8+
import UIKit
9+
import StackKit
10+
import PinLayout
11+
12+
extension WrapStackLayer: SizeCalculable { }
13+
14+
class WrapStackLayerDemoViewController: UIViewController {
15+
16+
let fixedWrapStackLayer = WrapStackLayer(
17+
itemSize: .fixed(CGSize(width: 30, height: 30)),
18+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
19+
itemSpacing: 4,
20+
lineSpacing: 10
21+
)
22+
23+
let adaptiveWrapStackLayer = WrapStackLayer(
24+
itemSize: .adaptive(column: 6),
25+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
26+
itemSpacing: 10,
27+
lineSpacing: 4
28+
)
29+
30+
let autoWrapStackLayer = WrapStackLayer(
31+
itemSize: .auto,
32+
contentInsets: UIEdgeInsets(top: 20, left: 14, bottom: 20, right: 14),
33+
itemSpacing: 10,
34+
lineSpacing: 10
35+
)
36+
37+
override func viewDidLoad() {
38+
super.viewDidLoad()
39+
40+
// Do any additional setup after loading the view.
41+
let widths: [CGFloat] = [20, 30, 50, 120, 40, 230, 50, 60, 10]
42+
fixedWrapStackLayer.addContent {
43+
for _ in (0 ... 20) {
44+
makeUIView(size: CGSize(width: widths.randomElement() ?? 10, height: 30))
45+
}
46+
}
47+
view.layer.addSublayer(fixedWrapStackLayer)
48+
49+
adaptiveWrapStackLayer.addContent {
50+
for _ in (0 ... 11) {
51+
makeUIView(size: CGSize(width: widths.randomElement() ?? 10, height: 30))
52+
}
53+
}
54+
view.layer.addSublayer(adaptiveWrapStackLayer)
55+
56+
autoWrapStackLayer.addContent {
57+
for _ in (0 ... 11) {
58+
makeUIView(size: CGSize(width: widths.randomElement() ?? 10, height: 30))
59+
}
60+
}
61+
view.layer.addSublayer(autoWrapStackLayer)
62+
}
63+
64+
private func makeUIView(size: CGSize) -> CALayer {
65+
let v = CALayer()
66+
let red: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
67+
let green: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
68+
let blue: CGFloat = CGFloat((0 ... 255).randomElement() ?? 0) / 255
69+
v.backgroundColor = UIColor.init(red: red, green: green, blue: blue, alpha: 1).cgColor
70+
v.frame.size = size
71+
return v
72+
}
73+
74+
override func viewDidLayoutSubviews() {
75+
super.viewDidLayoutSubviews()
76+
77+
fixedWrapStackLayer.pin.top(120).maxWidth(220).sizeToFit(.width)
78+
adaptiveWrapStackLayer.pin.top(to: fixedWrapStackLayer.edge.bottom).marginTop(50).horizontally().sizeToFit(.width)
79+
autoWrapStackLayer.pin.top(to: adaptiveWrapStackLayer.edge.bottom).marginTop(50).horizontally().sizeToFit(.width)
80+
}
81+
82+
/*
83+
// MARK: - Navigation
84+
85+
// In a storyboard-based application, you will often want to do a little preparation before navigation
86+
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
87+
// Get the new view controller using segue.destination.
88+
// Pass the selected object to the new view controller.
89+
}
90+
*/
91+
92+
}

Sources/StackKit/Enums.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ public enum WrapStackHorizontalAlignment {
191191
public enum WrapStackItemSize {
192192
case fixed(_ size: CGSize)
193193
case adaptive(column: Int)
194+
case auto
194195
}
195196

196197
public enum WrapStackLayout {

Sources/StackKit/Layer/HStackLayer.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ open class HStackLayer: CALayer, StackLayer {
5555
}
5656

5757
open override func preferredFrameSize() -> CGSize {
58-
setNeedsLayout()
59-
setNeedsDisplay()
58+
layoutSublayers()
6059
return contentSize
6160
}
6261

Sources/StackKit/Layer/VStackLayer.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ open class VStackLayer: CALayer, StackLayer {
108108
}
109109

110110
public func sizeThatFits(_ size: CGSize) -> CGSize {
111-
setNeedsLayout()
112-
layoutIfNeeded()
113-
111+
layoutSublayers()
114112
return contentSize
115113
}
116114

Sources/StackKit/StackKitResultBuilders.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,4 @@ extension _StackKitLayerContentResultBuilder {
8383

8484
@resultBuilder public struct _StackKitVStackLayerContentResultBuilder: _StackKitLayerContentResultBuilder { }
8585
@resultBuilder public struct _StackKitHStackLayerContentResultBuilder: _StackKitLayerContentResultBuilder { }
86+
@resultBuilder public struct _StackKitWrapStackLayerContentResultBuilder: _StackKitLayerContentResultBuilder { }

0 commit comments

Comments
 (0)