Skip to content

Commit e9c9985

Browse files
committed
[add] actionWillSelect
`tabItem(_:label:actionWillSelect:)` accepts a closure performing before the tab is selected.
1 parent a7afcac commit e9c9985

File tree

5 files changed

+91
-21
lines changed

5 files changed

+91
-21
lines changed

Example/Example/ContentView.swift

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,31 @@ struct ContentView: View {
1919

2020
var body: some View {
2121
TabBar(selection: $item, visibility: $visibility) {
22-
TextField("", text: $text)
23-
.textFieldStyle(.roundedBorder)
24-
.padding()
25-
.background(.brown)
22+
homeView()
2623
.tabItem(0) {
2724
Image(systemName: item == 0 ? "house.fill" : "house")
2825
.font(.title3)
2926
Text("Home")
3027
.font(.system(.footnote, design: .rounded).weight(item == 0 ? .bold : .medium))
28+
} actionWillSelect: {
29+
if item == 0 {
30+
text = ""
31+
}
3132
}
32-
List(1...30, id: \.self) { Text("Row \($0)") }
33+
marksView()
3334
.tabItem(1) {
3435
Image(systemName: item == 1 ? "star.fill" : "star")
3536
.font(.title3)
3637
Text("Marks")
3738
.font(.system(.footnote, design: .rounded).weight(item == 1 ? .bold : .medium))
3839
}
39-
ZStack {
40-
Color.orange
41-
Text("User View")
42-
}
43-
.tabItem(2) {
44-
Image(systemName: item == 2 ? "person.fill" : "person")
45-
.font(.title3)
46-
Text("User")
47-
.font(.system(.footnote, design: .rounded).weight(item == 2 ? .bold : .medium))
48-
}
40+
userView()
41+
.tabItem(2) {
42+
Image(systemName: item == 2 ? "person.fill" : "person")
43+
.font(.title3)
44+
Text("User")
45+
.font(.system(.footnote, design: .rounded).weight(item == 2 ? .bold : .medium))
46+
}
4947
}
5048
.tabBarFill(.regularMaterial)
5149
.tabBarForeground {
@@ -69,6 +67,26 @@ struct ContentView: View {
6967
}
7068
}
7169

70+
private func homeView() -> some View {
71+
TextField("", text: $text)
72+
.textFieldStyle(.roundedBorder)
73+
.padding()
74+
.background(.brown)
75+
}
76+
77+
private func marksView() -> some View {
78+
List(1...30, id: \.self) {
79+
Text("Row \($0)")
80+
}
81+
}
82+
83+
private func userView() -> some View {
84+
ZStack {
85+
Color.orange
86+
Text("User View")
87+
}
88+
}
89+
7290
private func nextVisibility() {
7391
switch visibility {
7492
case .automatic: visibility = .visible
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//
2+
// ItemActionWillSelectPreferenceKey.swift
3+
//
4+
//
5+
// Created by Zijie V on 24/08/2023.
6+
// Copyright © 2023 Zijie V . All rights reserved.
7+
//
8+
// ====================================================================================================================
9+
//
10+
11+
import SwiftUI
12+
13+
struct ItemActionWillSelectPreferenceKey<Selection: Hashable>: PreferenceKey {
14+
static var defaultValue: [Selection: TabItemAction<Selection>] { [:] }
15+
16+
static func reduce(
17+
value: inout [Selection: TabItemAction<Selection>],
18+
nextValue: () -> [Selection: TabItemAction<Selection>]
19+
) {
20+
value.merge(nextValue(), uniquingKeysWith: { $1 })
21+
}
22+
}
23+
24+
struct TabItemAction<Selection: Hashable>: Hashable, Equatable {
25+
let selectedItemHashValue: Int?
26+
let item: Selection
27+
let action: ActionWillSelect?
28+
29+
func hash(into hasher: inout Hasher) {
30+
hasher.combine(selectedItemHashValue)
31+
hasher.combine(item)
32+
}
33+
34+
static func == (lhs: TabItemAction, rhs: TabItemAction) -> Bool {
35+
lhs.hashValue == rhs.hashValue
36+
}
37+
}

Sources/TabBarModule/Internal/ViewModifier/TabItemViewModifier.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ struct TabItemViewModifier<Selection: Hashable, V: View>: ViewModifier {
1414
@Environment(\.tabItemSelectionHashValue) private var selectionHashValue
1515
private let item: Selection
1616
@ViewBuilder private let itemBuilder: () -> V
17+
private let actionWillSelect: ActionWillSelect?
1718

18-
init(item: Selection, @ViewBuilder itemBuilder: @escaping () -> V) {
19+
init(item: Selection, @ViewBuilder itemBuilder: @escaping () -> V, actionWillSelect: ActionWillSelect?) {
1920
self.item = item
2021
self.itemBuilder = itemBuilder
22+
self.actionWillSelect = actionWillSelect
2123
}
2224

2325
func body(content: Content) -> some View {
@@ -35,5 +37,11 @@ struct TabItemViewModifier<Selection: Hashable, V: View>: ViewModifier {
3537
)
3638
]
3739
)
40+
.preference(
41+
key: ItemActionWillSelectPreferenceKey.self,
42+
value: [
43+
item: TabItemAction(selectedItemHashValue: selectionHashValue, item: item, action: actionWillSelect)
44+
]
45+
)
3846
}
3947
}

Sources/TabBarModule/Public/TabBar.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ public struct TabBar<Selection, Content>: View where Selection: Hashable, Conten
2424
@Environment(\.tabBarShape) private var barShape
2525
@State private var items: [Selection] = []
2626
@State private var tabItemBuilders: [Selection: AnyItemViewBuilder<Selection>] = [:]
27+
@State private var tabItemActions: [Selection: TabItemAction<Selection>] = [:]
2728
@Binding private var selection: Selection
2829
@Binding private var visibility: Visibility
2930
@StateObject private var keyboardObserver: KeyboardObserver = .shared
@@ -46,6 +47,7 @@ public struct TabBar<Selection, Content>: View where Selection: Hashable, Conten
4647
.safeAreaInset(edge: .bottom, alignment: .center, spacing: barSpacing) { tabBar(in: geo) }
4748
.onPreferenceChange(ItemsPreferenceKey<Selection>.self) { self.items = $0 }
4849
.onPreferenceChange(ItemViewBuilderPreferenceKey<Selection>.self) { self.tabItemBuilders = $0 }
50+
.onPreferenceChange(ItemActionWillSelectPreferenceKey<Selection>.self) { self.tabItemActions = $0 }
4951
.environment(\.tabItemSelectionHashValue, selection.hashValue)
5052
.animation(animationBuilder(isVisible), value: isVisible)
5153
}
@@ -71,7 +73,10 @@ public struct TabBar<Selection, Content>: View where Selection: Hashable, Conten
7173
if let builder = tabItemBuilders[item]?.content {
7274
builder()
7375
.contentShape(Rectangle())
74-
.onTapGesture { selection = item }
76+
.onTapGesture {
77+
tabItemActions[item]?.action?()
78+
selection = item
79+
}
7580
.frame(width: width)
7681
}
7782
}

Sources/TabBarModule/Public/View+Extension.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010

1111
import SwiftUI
1212

13+
public typealias ActionWillSelect = () -> Void
14+
1315
extension View {
1416
public func tabItem<Selection: Hashable, V: View>(
1517
_ selection: Selection,
16-
@ViewBuilder label: @escaping () -> V
18+
@ViewBuilder label: @escaping () -> V,
19+
actionWillSelect: ActionWillSelect? = nil
1720
) -> some View {
18-
modifier(TabItemViewModifier(item: selection, itemBuilder: label))
21+
modifier(TabItemViewModifier(item: selection, itemBuilder: label, actionWillSelect: actionWillSelect))
1922
}
2023

2124
public func tabBarFill<S: ShapeStyle>(_ content: S, style: FillStyle = .init()) -> some View {
22-
self
23-
.environment(\.tabBarShapeStyle, .init(AnyShapeStyle(content)))
25+
self.environment(\.tabBarShapeStyle, .init(AnyShapeStyle(content)))
2426
.environment(\.tabBarFillStyle, style)
2527
}
2628

0 commit comments

Comments
 (0)