Skip to content

Commit e2b8f3f

Browse files
committed
Add SidebarTabView
1 parent 5cc29a2 commit e2b8f3f

1 file changed

Lines changed: 120 additions & 0 deletions

File tree

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import SwiftUI
2+
3+
struct SidebarItem: Identifiable, Equatable {
4+
var id: Int { tag }
5+
var tag: Int
6+
var title: String
7+
var subtitle: String? = nil
8+
var image: String? = nil
9+
}
10+
11+
struct SidebarItemPreferenceKey: PreferenceKey {
12+
static var defaultValue: [SidebarItem] = []
13+
static func reduce(value: inout [SidebarItem], nextValue: () -> [SidebarItem]) {
14+
value.append(contentsOf: nextValue())
15+
}
16+
}
17+
18+
extension View {
19+
func sidebarItem(
20+
tag: Int,
21+
currentTag: Int,
22+
title: String,
23+
subtitle: String? = nil,
24+
image: String? = nil
25+
) -> some View {
26+
return opacity(tag != currentTag ? 0 : 1)
27+
.background(GeometryReader { _ in
28+
Color.clear.preference(
29+
key: SidebarItemPreferenceKey.self,
30+
value: [.init(tag: tag, title: title, subtitle: subtitle, image: image)]
31+
)
32+
})
33+
}
34+
}
35+
36+
struct SidebarTabView<Content: View>: View {
37+
@State var sidebarItems = [SidebarItem]()
38+
@Binding var tag: Int
39+
@ViewBuilder var views: (_ tag: Int) -> Content
40+
var body: some View {
41+
HStack(spacing: 0) {
42+
ScrollView {
43+
VStack(alignment: .leading) {
44+
ForEach(sidebarItems) { item in
45+
Button(action: {
46+
tag = item.tag
47+
}) {
48+
HStack {
49+
if let image = item.image {
50+
Image(systemName: image)
51+
.resizable()
52+
.scaledToFit()
53+
.frame(height: 20)
54+
}
55+
VStack(alignment: .leading) {
56+
Text(item.title)
57+
.foregroundStyle(.primary)
58+
if let subtitle = item.subtitle {
59+
Text(subtitle)
60+
.font(.caption)
61+
.foregroundStyle(.tertiary)
62+
.opacity(0.5)
63+
}
64+
}
65+
}
66+
.frame(maxWidth: .infinity, alignment: .leading)
67+
.padding(.horizontal, 8)
68+
.padding(.vertical, 4)
69+
.background(
70+
Color.primary.opacity(tag == item.tag ? 0.1 : 0),
71+
in: RoundedRectangle(cornerRadius: 4)
72+
)
73+
.padding(.horizontal, 8)
74+
}
75+
.buttonStyle(.borderless)
76+
}
77+
}
78+
.frame(width: 200)
79+
.padding(.vertical, 8)
80+
}
81+
.background(Color.primary.opacity(0.05))
82+
83+
Divider()
84+
85+
ZStack(alignment: .topLeading) {
86+
views(tag)
87+
}
88+
}
89+
.onPreferenceChange(SidebarItemPreferenceKey.self) { items in
90+
sidebarItems = items
91+
}
92+
}
93+
}
94+
95+
struct SidebarTabView_Previews: PreviewProvider {
96+
static var previews: some View {
97+
SidebarTabView(tag: .constant(0)) { tag in
98+
Color.red.sidebarItem(
99+
tag: 0,
100+
currentTag: tag,
101+
title: "Hello",
102+
subtitle: "Meow",
103+
image: "person.circle.fill"
104+
)
105+
Color.blue.sidebarItem(
106+
tag: 1,
107+
currentTag: tag,
108+
title: "World",
109+
image: "person.circle.fill"
110+
)
111+
Color.blue.sidebarItem(
112+
tag: 3,
113+
currentTag: tag,
114+
title: "Pikachu",
115+
image: "person.circle.fill"
116+
)
117+
}
118+
}
119+
}
120+

0 commit comments

Comments
 (0)