Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impl bottom floating material glassed tabbar in iOS #483

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app-ios/Sources/AboutFeature/AboutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ public struct AboutView: View {
.padding(.bottom, 16)
}
.padding(.horizontal, 16)
// bottom floating tabbar padding
Color.clear.padding(.bottom, 60)
}
.background(AssetColors.Surface.surface.swiftUIColor)
}
Expand Down
16 changes: 0 additions & 16 deletions app-ios/Sources/App/RootReducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ public struct RootReducer {
case timetable(TimetableReducer.Action)
case favorite(FavoriteReducer.Action)
case about(AboutReducer.Action)
case view(View)
case paths(Paths)

@CasePathable
Expand All @@ -75,10 +74,6 @@ public struct RootReducer {
case favorite(StackActionOf<RootReducer.Path.Favorite>)
case about(StackActionOf<RootReducer.Path.About>)
}

public enum View {
case sameTabTapped(DroidKaigiAppTab)
}
}

public var body: some ReducerOf<Self> {
Expand Down Expand Up @@ -143,17 +138,6 @@ public struct RootReducer {
return .none
}

case let .view(.sameTabTapped(tab)):
switch tab {
case .timetable: state.paths.timetable.removeAll()
case .favorite: state.paths.favorite.removeAll()
case .about: state.paths.about.removeAll()
case .map: break
case .idCard: break
}

return .none

default:
return .none
}
Expand Down
153 changes: 78 additions & 75 deletions app-ios/Sources/App/RootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TimetableFeature
import EventMapFeature
import Theme

public enum DroidKaigiAppTab {
public enum DroidKaigiAppTab: Hashable {
case timetable
case map
case favorite
Expand All @@ -30,67 +30,19 @@ public struct RootView: View {
}

public var body: some View {
TabView(
selection: Binding(
get: { selection },
set: {
if selection != $0 {
selection = $0
return
}
store.send(.view(.sameTabTapped($0)))
shin-usu marked this conversation as resolved.
Show resolved Hide resolved
}
)
) {
Group {
Group {
switch selection {
case .timetable:
timetableTab
.tag(DroidKaigiAppTab.timetable)
.tabItem {
Label(
title: { Text("Timetable") },
icon: { Image(.icTimetable).renderingMode(.template) }
)
}

case .map:
eventMapTab
.tag(DroidKaigiAppTab.map)
.tabItem {
Label(
title: { Text("Event Map") },
icon: { Image(.icMap).renderingMode(.template) }
)
}

case .favorite:
favoriteTab
.tag(DroidKaigiAppTab.favorite)
.tabItem {
Label(
title: { Text("Favorite") },
icon: { Image(.icFav).renderingMode(.template) }
)
}

case .about:
aboutTab
.tag(DroidKaigiAppTab.about)
.tabItem {
Label(
title: { Text("About") },
icon: { Image(.icInfo).renderingMode(.template) }
)
}

Text("ID Card Feature")
.tag(DroidKaigiAppTab.idCard)
.tabItem {
Label(
title: { Text("ID Card") },
icon: { Image(.icProfileCard).renderingMode(.template) }
)
}
case .idCard:
idCardTab
}
.toolbarBackground(AssetColors.Surface.surface.swiftUIColor, for: .tabBar)
// If there are not this code, tab bar color is clear when scroll down to edge.
.toolbarBackground(.visible, for: .tabBar)
}
.navigationBarTitleStyle(
color: AssetColors.Surface.onSurface.swiftUIColor,
Expand All @@ -99,6 +51,33 @@ public struct RootView: View {
)
}

@MainActor
@ViewBuilder
private var tabItems: some View {
let items: [(tab: DroidKaigiAppTab, icon: ImageResource)] = [
(tab: .timetable, icon: .icTimetable),
(tab: .map, icon: .icMap),
(tab: .favorite, icon: .icFav),
(tab: .about, icon: .icInfo),
(tab: .idCard, icon: .icProfileCard),
]
HStack(spacing: 36) {
ForEach(items, id: \.tab) { item in
let isSelected = selection == item.tab
Button {
selection = item.tab
} label: {
Image(item.icon).renderingMode(.template).tint(isSelected ? nil : .white)
}
}
}
.padding(.vertical)
.padding(.horizontal, 24)
.background(.ultraThinMaterial, in: Capsule())
.overlay(Capsule().stroke(.gray, lineWidth: 1))
.environment(\.colorScheme, .dark)
}

@MainActor
private var timetableTab: some View {
NavigationStack(
Expand All @@ -107,12 +86,15 @@ public struct RootView: View {
action: \.paths.timetable
)
) {
TimetableView(
store: store.scope(
state: \.timetable,
action: \.timetable
ZStack(alignment: .bottom) {
TimetableView(
store: store.scope(
state: \.timetable,
action: \.timetable
)
)
)
tabItems
}
} destination: { store in
switch store.case {
case let .timetableDetail(store):
Expand All @@ -132,12 +114,15 @@ public struct RootView: View {
action: \.paths.about
)
) {
AboutView(
store: store.scope(
state: \.about,
action: \.about
ZStack(alignment: .bottom) {
AboutView(
store: store.scope(
state: \.about,
action: \.about
)
)
)
tabItems
}
} destination: { store in
switch store.case {
case let .staff(store):
Expand All @@ -163,12 +148,15 @@ public struct RootView: View {
action: \.paths.favorite
)
) {
FavoriteView(
store: store.scope(
state: \.favorite,
action: \.favorite
ZStack(alignment: .bottom) {
FavoriteView(
store: store.scope(
state: \.favorite,
action: \.favorite
)
)
)
tabItems
}
} destination: { store in
switch store.case {
case let .timetableDetail(store):
Expand All @@ -180,9 +168,24 @@ public struct RootView: View {
@MainActor
private var eventMapTab: some View {
NavigationStack {
EventMapView(store: Store(initialState: .init(), reducer: {
EventMapReducer()
}))
ZStack(alignment: .bottom) {
EventMapView(store: Store(initialState: .init(), reducer: {
EventMapReducer()
}))
tabItems
}
}
}

@MainActor
private var idCardTab: some View {
NavigationStack {
ZStack(alignment: .bottom) {
ScrollView {
Text("ID Card Feature")
}
tabItems
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions app-ios/Sources/EventMapFeature/EventMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public struct EventMapView: View {
EventItem(event: event)
}
}
// bottom floating tabbar padding
Color.clear.padding(.bottom, 60)
}
.background(AssetColors.Surface.surface.swiftUIColor)
.navigationBarTitleDisplayMode(.large)
Expand Down
2 changes: 2 additions & 0 deletions app-ios/Sources/FavoriteFeature/FavoriteView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public struct FavoriteView: View {
}
}
.padding(.horizontal, 16)
// bottom floating tabbar padding
Color.clear.padding(.bottom, 60)
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions app-ios/Sources/TimetableFeature/TimetableListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ struct TimetableListView: View {
.onAppear {
store.send(.view(.onAppear))
}.background(AssetColors.Surface.surface.swiftUIColor)
// bottom floating tabbar padding
Color.clear.padding(.bottom, 60)
Comment on lines +106 to +107
Copy link
Contributor

@shin-usu shin-usu Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TimetableView has list mode and grid mode, and user can switch each mode. (Grid mode is developing now)
Grid mode also has floating bottom tab, like list mode. Therefore, I think it would be appropriate for this code to be added to the TimetableView, not the TimetableListView,

Copy link
Contributor Author

@shimastripe shimastripe Aug 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I understand your suggestion. However TimetableListView and TimetableGridView are ScrollView.

So if the padding is set in TimetableView, the scrollable area is above the floating tab bar.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I understand.
It seems to be fine as it is👌🏻

}
}
}
Expand Down
Loading