
- 新增MainView Tab切换问题分析文档,详细描述问题原因及解决方案。 - 优化BottomTabView的绑定逻辑,简化状态管理,确保Tab切换时状态正确更新。 - 在MeView中实现用户信息加载逻辑调整,确保动态列表仅在首次进入时加载,并添加错误处理视图。 - 创建EmptyStateView组件,提供统一的空状态展示和重试功能。 - 增强调试信息输出,便于后续问题排查和用户体验提升。
270 lines
12 KiB
Swift
270 lines
12 KiB
Swift
import SwiftUI
|
||
import ComposableArchitecture
|
||
|
||
struct MeView: View {
|
||
let store: StoreOf<MeFeature>
|
||
// 新增:图片预览状态
|
||
@State private var previewItem: PreviewItem? = nil
|
||
@State private var previewCurrentIndex: Int = 0
|
||
// 新增:是否显示关闭按钮
|
||
let showCloseButton: Bool
|
||
// 新增:dismiss环境变量
|
||
@Environment(\.dismiss) private var dismiss
|
||
|
||
init(store: StoreOf<MeFeature>, showCloseButton: Bool = false) {
|
||
self.store = store
|
||
self.showCloseButton = showCloseButton
|
||
}
|
||
|
||
var body: some View {
|
||
WithPerceptionTracking {
|
||
GeometryReader { geometry in
|
||
ZStack {
|
||
// 背景图片
|
||
Image("bg")
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fill)
|
||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||
.clipped()
|
||
.ignoresSafeArea(.all)
|
||
|
||
// 顶部栏,右上角设置按钮
|
||
VStack {
|
||
HStack {
|
||
// 新增:关闭按钮(仅在present时显示)
|
||
if showCloseButton {
|
||
Button(action: {
|
||
dismiss()
|
||
}) {
|
||
Image(systemName: "xmark")
|
||
.font(.system(size: 20, weight: .medium))
|
||
.foregroundColor(.white)
|
||
.frame(width: 44, height: 44)
|
||
.background(Color.black.opacity(0.3))
|
||
.clipShape(Circle())
|
||
}
|
||
.padding(.leading, 16)
|
||
.padding(.top, 8)
|
||
}
|
||
|
||
Spacer()
|
||
|
||
if !store.isDisplayingOtherUser {
|
||
Button(action: {
|
||
store.send(.settingButtonTapped)
|
||
}) {
|
||
Image(systemName: "gearshape")
|
||
.font(.system(size: 33, weight: .medium))
|
||
.foregroundColor(.white)
|
||
}
|
||
.padding(.trailing, 16)
|
||
.padding(.top, 8)
|
||
}
|
||
}
|
||
Spacer()
|
||
}
|
||
|
||
// 主要内容区域
|
||
VStack(spacing: 16) {
|
||
// 用户信息区域
|
||
userInfoSection()
|
||
|
||
// 动态内容区域
|
||
momentsSection()
|
||
|
||
Spacer()
|
||
}
|
||
.frame(maxWidth: .infinity, alignment: .top)
|
||
.padding(.top, 8)
|
||
}
|
||
}
|
||
.onAppear {
|
||
debugInfoSync("📱 MeView onAppear")
|
||
debugInfoSync(" 用户信息: \(store.userInfo?.nick ?? "nil")")
|
||
debugInfoSync(" 动态数量: \(store.moments.count)")
|
||
debugInfoSync(" 用户信息错误: \(store.userInfoError ?? "nil")")
|
||
debugInfoSync(" 动态错误: \(store.momentsError ?? "nil")")
|
||
debugInfoSync(" 显示错误视图: \(store.showErrorView)")
|
||
store.send(.onAppear)
|
||
}
|
||
// 新增:图片预览弹窗
|
||
.fullScreenCover(item: $previewItem) { item in
|
||
ImagePreviewPager(images: item.images as [String], currentIndex: $previewCurrentIndex) {
|
||
previewItem = nil
|
||
}
|
||
}
|
||
// 新增:详情页导航
|
||
.navigationDestination(isPresented: Binding(
|
||
get: { store.showDetail },
|
||
set: { _ in store.send(.detailDismissed) }
|
||
)) {
|
||
if let selectedMoment = store.selectedMoment {
|
||
let detailStore = Store(
|
||
initialState: DetailFeature.State(moment: selectedMoment)
|
||
) {
|
||
DetailFeature()
|
||
}
|
||
|
||
DetailView(store: detailStore)
|
||
.onChange(of: detailStore.shouldDismiss) { _, shouldDismiss in
|
||
if shouldDismiss {
|
||
store.send(.detailDismissed)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// MARK: - 用户信息区域
|
||
@ViewBuilder
|
||
private func userInfoSection() -> some View {
|
||
WithPerceptionTracking {
|
||
VStack(spacing: 16) {
|
||
// 头像
|
||
AsyncImage(url: URL(string: store.userInfo?.avatar ?? "")) { image in
|
||
image
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fill)
|
||
} placeholder: {
|
||
Image(systemName: "person.circle.fill")
|
||
.resizable()
|
||
.aspectRatio(contentMode: .fill)
|
||
.foregroundColor(.gray)
|
||
}
|
||
.frame(width: 130, height: 130)
|
||
.clipShape(Circle())
|
||
.overlay(
|
||
Circle()
|
||
.stroke(Color.white, lineWidth: 2)
|
||
)
|
||
|
||
// 用户昵称
|
||
Text(store.userInfo?.nick ?? "未知用户")
|
||
.font(.system(size: 20, weight: .medium))
|
||
.foregroundColor(.white)
|
||
|
||
// 用户ID
|
||
UserIDDisplay(uid: store.userInfo?.uid ?? 0, isDisplayCopy: true)
|
||
}
|
||
.padding(.horizontal, 32)
|
||
}
|
||
}
|
||
|
||
// MARK: - 动态内容区域
|
||
@ViewBuilder
|
||
private func momentsSection() -> some View {
|
||
WithPerceptionTracking {
|
||
if store.isLoadingUserInfo || store.isLoadingMoments {
|
||
VStack {
|
||
ProgressView()
|
||
.progressViewStyle(CircularProgressViewStyle(tint: .white))
|
||
.scaleEffect(1.2)
|
||
Text("加载中...")
|
||
.font(.system(size: 14))
|
||
.foregroundColor(.white.opacity(0.7))
|
||
.padding(.top, 8)
|
||
}
|
||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||
} else if let error = store.userInfoError {
|
||
VStack(spacing: 12) {
|
||
Image(systemName: "exclamationmark.triangle")
|
||
.font(.system(size: 32))
|
||
.foregroundColor(.orange)
|
||
Text("用户信息加载失败")
|
||
.font(.system(size: 16, weight: .medium))
|
||
.foregroundColor(.white)
|
||
Text(error)
|
||
.font(.system(size: 14))
|
||
.foregroundColor(.white.opacity(0.7))
|
||
.multilineTextAlignment(.center)
|
||
.padding(.horizontal, 32)
|
||
Button("重试") {
|
||
store.send(.loadUserInfo)
|
||
}
|
||
.font(.system(size: 14, weight: .medium))
|
||
.foregroundColor(.white)
|
||
.padding(.horizontal, 24)
|
||
.padding(.vertical, 8)
|
||
.background(Color.blue.opacity(0.8))
|
||
.cornerRadius(8)
|
||
}
|
||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||
} else if store.showErrorView {
|
||
// 显示错误视图组件
|
||
EmptyStateView {
|
||
store.send(.retryMoments)
|
||
}
|
||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||
} else if store.moments.isEmpty {
|
||
VStack(spacing: 12) {
|
||
Image(systemName: "doc.text")
|
||
.font(.system(size: 32))
|
||
.foregroundColor(.white.opacity(0.5))
|
||
Text("暂无动态")
|
||
.font(.system(size: 16, weight: .medium))
|
||
.foregroundColor(.white.opacity(0.7))
|
||
// 添加调试信息
|
||
Text("调试: moments.count = \(store.moments.count)")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
Text("调试: isLoadingUserInfo = \(store.isLoadingUserInfo)")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
Text("调试: isLoadingMoments = \(store.isLoadingMoments)")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
Text("调试: userInfoError = \(store.userInfoError ?? "nil")")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
Text("调试: momentsError = \(store.momentsError ?? "nil")")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
Text("调试: showErrorView = \(store.showErrorView)")
|
||
.font(.system(size: 12))
|
||
.foregroundColor(.yellow)
|
||
}
|
||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||
} else {
|
||
ScrollView {
|
||
WithPerceptionTracking {
|
||
LazyVStack(spacing: 12) {
|
||
ForEach(Array(store.moments.enumerated()), id: \.element.dynamicId) { index, moment in
|
||
OptimizedDynamicCardView(
|
||
moment: moment,
|
||
allMoments: store.moments,
|
||
currentIndex: index,
|
||
onImageTap: { images, tappedIndex in
|
||
previewCurrentIndex = tappedIndex
|
||
previewItem = PreviewItem(images: images, index: tappedIndex)
|
||
},
|
||
onLikeTap: { _, _, _, _ in
|
||
// 暂时不处理点赞,后续可以添加点赞功能
|
||
},
|
||
onCardTap: {
|
||
store.send(.showDetail(moment))
|
||
},
|
||
onAvatarTap: nil // MeView中不需要头像点击功能
|
||
)
|
||
.padding(.horizontal, 12)
|
||
}
|
||
if store.hasMore {
|
||
ProgressView()
|
||
.onAppear {
|
||
store.send(.loadMore)
|
||
}
|
||
}
|
||
// 新增底部间距
|
||
Color.clear.frame(height: 120)
|
||
}
|
||
.padding(.top, 8)
|
||
}
|
||
}
|
||
.refreshable {
|
||
store.send(.refresh)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|