wangdl 42b60a21ec feat(ios): TabBar 隐藏/出现增加平滑动画
- ContentView 新增 TabBarVisibleKey 环境值 + AnimatedTabBarHide modifier
- TabView 使用 .toolbar(visible/hidden) + .animation 驱动动画
- 子页面 onAppear 隐藏 → onDisappear 出现,带 easeInOut 0.28s
- 替换所有静态 .toolbar(.hidden, for: .tabBar) 为 .animatedTabBarHide()

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 22:16:22 +08:00

67 lines
2.9 KiB
Swift

import SwiftUI
struct AIChatPage: View {
@StateObject private var vm = AIChatViewModel()
var body: some View {
ZStack {
Color.zxBg0.ignoresSafeArea()
VStack(spacing: 0) {
ScrollViewReader { proxy in
ScrollView {
VStack(spacing: 16) {
ForEach(vm.messages) { m in
chatBubble(m)
.id(m.id)
}
if vm.isSending {
HStack(spacing: 8) {
Image(systemName: "brain.head.profile").foregroundColor(Color.zxPurple)
.frame(width: 28, height: 28)
.background(Color(hex: "#7C6EFA", opacity: 0.15))
.clipShape(Circle())
ZXDotLoader(color: Color.zxPurple)
.padding(.leading, 4)
Spacer()
}
.padding(.horizontal, 20)
}
}
.padding(.horizontal, 20).padding(.top, 8).padding(.bottom, 100)
}
.scrollIndicators(.hidden)
.onChange(of: vm.messages.count) { _ in
withAnimation { proxy.scrollTo(vm.messages.last?.id) }
}
}
ZXAIInputBar(text: $vm.inputText, onSend: { vm.send() })
.padding(.horizontal, 20).padding(.bottom, 34)
}
}
.navigationBarTitleDisplayMode(.inline).animatedTabBarHide()
.toolbarBackground(.hidden, for: .navigationBar)
}
private func chatBubble(_ m: AIMessage) -> some View {
HStack(alignment: .top, spacing: 8) {
if m.role == .ai {
Image(systemName: "brain.head.profile").foregroundColor(Color.zxPurple)
.frame(width: 28, height: 28)
.background(Color(hex: "#7C6EFA", opacity: 0.15))
.clipShape(Circle())
}
Text(m.content).zxFontScaled(size: 14)
.foregroundColor(m.role == .user ? .white : Color.zxF007)
.padding(12)
.background(m.role == .user ? AnyView(ZXGradient.brandPurple) : AnyView(Color.zxFill004))
.clipShape(RoundedRectangle(cornerRadius: 16))
if m.role == .user {
Circle().frame(width: 28, height: 28)
.foregroundColor(Color.zxPurpleBG(0.2))
.overlay(Text("").font(.system(size: 10, weight: .bold)).foregroundColor(Color.zxPurple))
}
}
.frame(maxWidth: .infinity, alignment: m.role == .user ? .trailing : .leading)
}
}