Files
e-party-iOS/yana/Views/MeView.swift
edwinQQQ 4ff92c8c4d feat: 修复MainView Tab切换问题并优化MeView逻辑
- 新增MainView Tab切换问题分析文档,详细描述问题原因及解决方案。
- 优化BottomTabView的绑定逻辑,简化状态管理,确保Tab切换时状态正确更新。
- 在MeView中实现用户信息加载逻辑调整,确保动态列表仅在首次进入时加载,并添加错误处理视图。
- 创建EmptyStateView组件,提供统一的空状态展示和重试功能。
- 增强调试信息输出,便于后续问题排查和用户体验提升。
2025-08-05 15:51:07 +08:00

270 lines
12 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}
}
}
}
}