fix: context-dependent FAB — play/pause on Reader, + on other tabs
Removed Tab(role: .search) — it was always visible on all tabs. Now the floating action button changes based on selected tab: - Home/Fitness: FAB (+) → opens food assistant (same as before) - Reader: Play/Pause circle → toggles auto-scroll - Idle: play.fill icon, warm accent background - Playing: pause.fill icon, red background - Tapping toggles between play/pause Speed controls appear as glass capsule above the FAB when playing. Auto-scroll stops when switching away from Reader tab. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,35 +50,40 @@ struct MainTabView: View {
|
|||||||
Tab("Reader", systemImage: "newspaper.fill", value: 2) {
|
Tab("Reader", systemImage: "newspaper.fill", value: 2) {
|
||||||
ReaderTabView(vm: readerVM, isAutoScrolling: $isAutoScrolling, scrollSpeed: $scrollSpeed)
|
ReaderTabView(vm: readerVM, isAutoScrolling: $isAutoScrolling, scrollSpeed: $scrollSpeed)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-scroll button — uses .search role for the separated
|
|
||||||
// circular placement on the trailing side of the tab bar
|
|
||||||
Tab("Auto", systemImage: "play.fill", value: 3, role: .search) {
|
|
||||||
// When tapped, toggle auto-scroll instead of showing search
|
|
||||||
Color.clear
|
|
||||||
.onAppear {
|
|
||||||
// Switch to Reader tab and start auto-scroll
|
|
||||||
selectedTab = 2
|
|
||||||
withAnimation(.spring(duration: 0.3)) {
|
|
||||||
isAutoScrolling = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tint(Color.accentWarm)
|
.tint(Color.accentWarm)
|
||||||
.modifier(TabBarMinimizeModifier())
|
.tabBarMinimizeBehavior(.onScrollDown)
|
||||||
|
|
||||||
// Floating buttons (hidden on Reader tab)
|
// Floating action button — context-dependent
|
||||||
if selectedTab != 2 {
|
VStack {
|
||||||
VStack {
|
Spacer()
|
||||||
Spacer()
|
HStack(alignment: .bottom) {
|
||||||
HStack(alignment: .bottom) {
|
if selectedTab != 2 {
|
||||||
FeedbackButton()
|
FeedbackButton()
|
||||||
.padding(.leading, 20)
|
.padding(.leading, 20)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
|
if selectedTab == 2 && showReader {
|
||||||
|
// Reader: play/pause auto-scroll
|
||||||
|
Button {
|
||||||
|
withAnimation(.spring(duration: 0.3)) {
|
||||||
|
isAutoScrolling.toggle()
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: isAutoScrolling ? "pause.fill" : "play.fill")
|
||||||
|
.font(.system(size: 18))
|
||||||
|
.foregroundStyle(.white)
|
||||||
|
.frame(width: 56, height: 56)
|
||||||
|
.background(isAutoScrolling ? Color.red.opacity(0.85) : Color.accentWarm)
|
||||||
|
.clipShape(Circle())
|
||||||
|
.shadow(color: .black.opacity(0.2), radius: 8, y: 4)
|
||||||
|
}
|
||||||
|
.padding(.trailing, 20)
|
||||||
|
} else {
|
||||||
|
// Home/Fitness: FAB (+) for food
|
||||||
Button { showAssistant = true } label: {
|
Button { showAssistant = true } label: {
|
||||||
Image(systemName: "plus")
|
Image(systemName: "plus")
|
||||||
.font(.title2.weight(.semibold))
|
.font(.title2.weight(.semibold))
|
||||||
@@ -90,8 +95,8 @@ struct MainTabView: View {
|
|||||||
}
|
}
|
||||||
.padding(.trailing, 20)
|
.padding(.trailing, 20)
|
||||||
}
|
}
|
||||||
.padding(.bottom, 70)
|
|
||||||
}
|
}
|
||||||
|
.padding(.bottom, 70)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-scroll speed controls (overlay when playing)
|
// Auto-scroll speed controls (overlay when playing)
|
||||||
@@ -120,28 +125,12 @@ struct MainTabView: View {
|
|||||||
.foregroundStyle(Color.accentWarm)
|
.foregroundStyle(Color.accentWarm)
|
||||||
.frame(width: 32, height: 32)
|
.frame(width: 32, height: 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
|
||||||
.frame(height: 20)
|
|
||||||
|
|
||||||
Button {
|
|
||||||
withAnimation(.spring(duration: 0.3)) {
|
|
||||||
isAutoScrolling = false
|
|
||||||
}
|
|
||||||
} label: {
|
|
||||||
Image(systemName: "stop.fill")
|
|
||||||
.font(.system(size: 12))
|
|
||||||
.foregroundStyle(.white)
|
|
||||||
.frame(width: 34, height: 34)
|
|
||||||
.background(Color.red.opacity(0.8))
|
|
||||||
.clipShape(Circle())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 14)
|
.padding(.horizontal, 14)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
.background(.ultraThinMaterial, in: Capsule())
|
.background(.ultraThinMaterial, in: Capsule())
|
||||||
.shadow(color: .black.opacity(0.1), radius: 8, y: 2)
|
.shadow(color: .black.opacity(0.1), radius: 8, y: 2)
|
||||||
.padding(.bottom, 70)
|
.padding(.bottom, 140)
|
||||||
.transition(.move(edge: .bottom).combined(with: .opacity))
|
.transition(.move(edge: .bottom).combined(with: .opacity))
|
||||||
}
|
}
|
||||||
.animation(.spring(duration: 0.3), value: isAutoScrolling)
|
.animation(.spring(duration: 0.3), value: isAutoScrolling)
|
||||||
@@ -167,6 +156,10 @@ struct MainTabView: View {
|
|||||||
renderer.attachToWindow()
|
renderer.attachToWindow()
|
||||||
await readerVM.loadInitial()
|
await readerVM.loadInitial()
|
||||||
}
|
}
|
||||||
|
.onChange(of: selectedTab) { _, newTab in
|
||||||
|
// Stop auto-scroll when leaving Reader
|
||||||
|
if newTab != 2 { isAutoScrolling = false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func foodAdded() {
|
private func foodAdded() {
|
||||||
@@ -233,11 +226,3 @@ struct AssistantSheetView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Tab Bar Minimize (iOS 26+)
|
|
||||||
|
|
||||||
struct TabBarMinimizeModifier: ViewModifier {
|
|
||||||
func body(content: Content) -> some View {
|
|
||||||
content.tabBarMinimizeBehavior(.onScrollDown)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user