Skip to content

Commit cffc7c4

Browse files
graycreateclaude
andcommitted
fix: filter menu now properly overlays content instead of pushing it down
- Moved filter menu rendering from TopBar to MainPage as an overlay - FilterMenuView now renders above content with proper z-index stacking - Removed duplicate overlay logic from FeedPage - Adjusted FilterMenuView positioning to work as a true overlay - Menu now appears on top of feed content without affecting layout 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 997295c commit cffc7c4

File tree

4 files changed

+98
-78
lines changed

4 files changed

+98
-78
lines changed

V2er/View/Feed/FeedPage.swift

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,11 @@ struct FeedPage: BaseHomePageView {
2424
}
2525

2626
var body: some View {
27-
ZStack {
28-
contentView
29-
.hide(!isSelected)
30-
.onAppear {
31-
log("FeedPage.onAppear")
32-
}
33-
34-
FilterMenuView(
35-
selectedTab: state.selectedTab,
36-
isShowing: state.showFilterMenu,
37-
onTabSelected: { tab in
38-
dispatch(FeedActions.SelectTab(tab: tab))
39-
},
40-
onDismiss: {
41-
dispatch(FeedActions.ToggleFilterMenu())
42-
}
43-
)
44-
}
27+
contentView
28+
.hide(!isSelected)
29+
.onAppear {
30+
log("FeedPage.onAppear")
31+
}
4532
}
4633

4734
@ViewBuilder

V2er/View/Feed/FilterMenuView.swift

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ struct FilterMenuView: View {
1616
let onDismiss: () -> Void
1717

1818
var body: some View {
19-
ZStack(alignment: .topLeading) {
19+
ZStack {
2020
if isShowing {
2121
// Background overlay
2222
Color.black.opacity(0.3)
@@ -26,38 +26,43 @@ struct FilterMenuView: View {
2626
}
2727
.transition(.opacity)
2828

29-
// Menu content - positioned at top left
30-
VStack(alignment: .leading, spacing: 0) {
31-
ScrollView {
32-
VStack(alignment: .leading, spacing: 4) {
33-
ForEach(Tab.allTabs, id: \.self) { tab in
34-
TabFilterMenuItem(
35-
tab: tab,
36-
isSelected: tab == selectedTab,
37-
needsLogin: tab.needsLogin() && !AccountState.hasSignIn()
38-
) {
39-
if tab.needsLogin() && !AccountState.hasSignIn() {
40-
Toast.show("登录后才能查看「\(tab.displayName())」下的内容")
41-
} else {
42-
onTabSelected(tab)
29+
// Menu content - positioned below navbar
30+
VStack(spacing: 0) {
31+
HStack {
32+
Spacer()
33+
ScrollView {
34+
VStack(alignment: .leading, spacing: 4) {
35+
ForEach(Tab.allTabs, id: \.self) { tab in
36+
TabFilterMenuItem(
37+
tab: tab,
38+
isSelected: tab == selectedTab,
39+
needsLogin: tab.needsLogin() && !AccountState.hasSignIn()
40+
) {
41+
if tab.needsLogin() && !AccountState.hasSignIn() {
42+
Toast.show("登录后才能查看「\(tab.displayName())」下的内容")
43+
} else {
44+
onTabSelected(tab)
45+
}
4346
}
4447
}
4548
}
49+
.padding(.vertical, 8)
4650
}
47-
.padding(.vertical, 8)
51+
.frame(width: 200)
52+
.background(Color.itemBg)
53+
.cornerRadius(8)
54+
.shadow(color: Color.black.opacity(0.2), radius: 12, x: 0, y: 4)
55+
.frame(maxHeight: 450)
56+
.padding(.top, 8)
57+
Spacer()
4858
}
49-
.frame(width: 200)
50-
.background(Color.itemBg)
51-
.cornerRadius(12)
52-
.shadow(color: Color.black.opacity(0.15), radius: 8, x: 0, y: 4)
53-
.frame(maxHeight: 450)
54-
.padding(.top, topSafeAreaInset().top + 50)
55-
.padding(.leading, 16)
59+
.transition(.asymmetric(
60+
insertion: .move(edge: .top).combined(with: .opacity),
61+
removal: .move(edge: .top).combined(with: .opacity)
62+
))
63+
64+
Spacer()
5665
}
57-
.transition(.asymmetric(
58-
insertion: .move(edge: .top).combined(with: .opacity),
59-
removal: .move(edge: .top).combined(with: .opacity)
60-
))
6166
}
6267
}
6368
.animation(.spring(response: 0.3, dampingFraction: 0.8), value: isShowing)

V2er/View/MainPage.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,26 @@ struct MainPage: StateView {
2525
var body: some View {
2626
NavigationView {
2727
ZStack {
28-
FeedPage(selecedTab: state.selectedTab)
29-
ExplorePage(selecedTab: state.selectedTab)
30-
MessagePage(selecedTab: state.selectedTab)
31-
MePage(selecedTab: state.selectedTab)
28+
// Main content pages
29+
ZStack {
30+
FeedPage(selecedTab: state.selectedTab)
31+
ExplorePage(selecedTab: state.selectedTab)
32+
MessagePage(selecedTab: state.selectedTab)
33+
MePage(selecedTab: state.selectedTab)
34+
}
35+
36+
// Filter menu overlay - will be rendered above content
37+
FilterMenuView(
38+
selectedTab: store.appState.feedState.selectedTab,
39+
isShowing: state.selectedTab == .feed && store.appState.feedState.showFilterMenu,
40+
onTabSelected: { tab in
41+
dispatch(FeedActions.SelectTab(tab: tab))
42+
},
43+
onDismiss: {
44+
dispatch(FeedActions.ToggleFilterMenu())
45+
}
46+
)
47+
.zIndex(1000)
3248
}
3349
.safeAreaInset(edge: .top, spacing: 0) {
3450
TopBar(selectedTab: state.selectedTab)

V2er/View/Widget/TopBar.swift

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,39 +34,51 @@ struct TopBar: View {
3434

3535
var body: some View {
3636
VStack(spacing: 0) {
37-
HStack {
38-
if isHomePage {
39-
HStack(spacing: 4) {
37+
ZStack {
38+
HStack {
39+
Image(systemName: "square.grid.2x2")
40+
.foregroundColor(.primary)
41+
.font(.system(size: 22))
42+
.padding(6)
43+
.forceClickable()
44+
.hide()
45+
Spacer()
46+
Image(systemName: "magnifyingglass")
47+
.foregroundColor(.primary)
48+
.font(.system(size: 22))
49+
.padding(6)
50+
.forceClickable()
51+
.to { SearchPage() }
52+
}
53+
.padding(.horizontal, 10)
54+
.padding(.vertical, 8)
55+
56+
// Centered title
57+
HStack {
58+
Spacer()
59+
if isHomePage {
60+
HStack(spacing: 4) {
61+
Text(title)
62+
.font(.title2)
63+
.foregroundColor(.primary)
64+
.fontWeight(.heavy)
65+
Image(systemName: store.appState.feedState.showFilterMenu ? "chevron.up" : "chevron.down")
66+
.font(.system(size: 14, weight: .semibold))
67+
.foregroundColor(.primary)
68+
}
69+
.onTapGesture {
70+
dispatch(FeedActions.ToggleFilterMenu())
71+
}
72+
} else {
4073
Text(title)
41-
.font(.title2)
42-
.foregroundColor(.primary)
43-
.fontWeight(.heavy)
44-
Image(systemName: store.appState.feedState.showFilterMenu ? "chevron.up" : "chevron.down")
45-
.font(.system(size: 14, weight: .semibold))
74+
.font(.headline)
4675
.foregroundColor(.primary)
76+
.fontWeight(.bold)
4777
}
48-
.onTapGesture {
49-
dispatch(FeedActions.ToggleFilterMenu())
50-
}
51-
} else {
52-
Text(title)
53-
.font(.headline)
54-
.foregroundColor(.primary)
55-
.fontWeight(.bold)
56-
.padding(.leading, 10)
78+
Spacer()
5779
}
58-
59-
Spacer()
60-
61-
Image(systemName: "magnifyingglass")
62-
.foregroundColor(.primary)
63-
.font(.system(size: 22))
64-
.padding(6)
65-
.forceClickable()
66-
.to { SearchPage() }
80+
.allowsHitTesting(isHomePage)
6781
}
68-
.padding(.horizontal, 10)
69-
.padding(.vertical, 8)
7082
.padding(.top, topSafeAreaInset().top)
7183
.background(VEBlur())
7284

@@ -82,7 +94,7 @@ struct TopBar: View {
8294
struct TopBar_Previews: PreviewProvider {
8395
// @State static var selecedTab = TabId.feed
8496
static var selecedTab = TabId.explore
85-
97+
8698
static var previews: some View {
8799
VStack {
88100
TopBar(selectedTab: selecedTab)

0 commit comments

Comments
 (0)