diff --git a/issues/MomentListItem图片点击功能实现.md b/issues/MomentListItem图片点击功能实现.md new file mode 100644 index 0000000..d1a8928 --- /dev/null +++ b/issues/MomentListItem图片点击功能实现.md @@ -0,0 +1,199 @@ +# MomentListItem 图片点击功能实现 + +## 📋 任务概述 + +为 `MomentListItem` 添加图片点击功能,实现点击图片后通过 `ImagePreviewPager` 显示被点击 item 的所有图片。 + +## ✅ 已完成功能 + +### 1. 图片点击响应 +- **点击回调**:为 `MomentListItem` 添加了 `onImageTap` 回调函数 +- **图片网格支持**:`MomentImageGrid` 支持图片点击事件 +- **单个图片支持**:`MomentSquareImageView` 包装为可点击的按钮 + +### 2. ImagePreviewPager 集成 +- **预览状态管理**:在 `MomentListHomePage` 中添加预览状态 +- **全屏预览**:使用 `.fullScreenCover` 实现全屏图片预览 +- **图片切换**:支持在预览中左右滑动切换图片 + +### 3. 用户体验优化 +- **点击反馈**:使用 `PlainButtonStyle` 避免默认按钮样式 +- **调试信息**:添加详细的调试日志 +- **状态同步**:正确同步预览索引和图片数组 + +## 🔧 技术实现 + +### MomentListItem 增强 + +```swift +struct MomentListItem: View { + let moment: MomentsInfo + let onImageTap: (([String], Int)) -> Void // 新增:图片点击回调 + + init(moment: MomentsInfo, onImageTap: @escaping (([String], Int)) -> Void = { _, _ in }) { + self.moment = moment + self.onImageTap = onImageTap + } +} +``` + +### 图片网格组件增强 + +```swift +struct MomentImageGrid: View { + let images: [MomentsPicture] + let onImageTap: (([String], Int)) -> Void // 新增:图片点击回调 + + // 为每个图片添加点击事件 + MomentSquareImageView( + image: image, + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, index)) + } + ) +} +``` + +### 单个图片组件增强 + +```swift +struct MomentSquareImageView: View { + let image: MomentsPicture + let size: CGFloat + let onTap: () -> Void // 新增:点击回调 + + var body: some View { + Button(action: onTap) { + CachedAsyncImage(url: image.resUrl ?? "") { imageView in + imageView + .resizable() + .aspectRatio(contentMode: .fill) + } + // ... 其他样式 + } + .buttonStyle(PlainButtonStyle()) // 避免默认按钮样式 + } +} +``` + +### MomentListHomePage 集成 + +```swift +struct MomentListHomePage: View { + @StateObject private var viewModel = MomentListHomeViewModel() + + // MARK: - 图片预览状态 + @State private var previewItem: PreviewItem? = nil + @State private var previewCurrentIndex: Int = 0 + + // 在 MomentListItem 中使用 + MomentListItem( + moment: moment, + onImageTap: { images, tappedIndex in + previewCurrentIndex = tappedIndex + previewItem = PreviewItem(images: images, index: tappedIndex) + } + ) + + // 图片预览弹窗 + .fullScreenCover(item: $previewItem) { item in + ImagePreviewPager( + images: item.images as [String], + currentIndex: $previewCurrentIndex + ) { + previewItem = nil + } + } +} +``` + +## 📱 功能特性 + +### 点击响应 +- **任意图片点击**:支持点击动态中的任意图片 +- **索引传递**:正确传递被点击图片的索引 +- **图片数组**:传递该动态的所有图片URL数组 + +### 预览功能 +- **全屏显示**:图片预览以全屏模式显示 +- **左右滑动**:支持在预览中左右滑动切换图片 +- **关闭按钮**:右上角提供关闭按钮 +- **索引指示**:显示当前图片索引和总数 + +### 状态管理 +- **预览状态**:使用 `@State` 管理预览状态 +- **索引同步**:正确同步预览索引和点击索引 +- **状态重置**:关闭预览时正确重置状态 + +## 🎯 用户体验 + +### 交互流程 +1. **点击图片**:用户点击动态中的任意图片 +2. **预览打开**:全屏预览弹窗打开,显示被点击的图片 +3. **图片浏览**:用户可以左右滑动浏览该动态的所有图片 +4. **关闭预览**:点击右上角关闭按钮或下滑关闭预览 + +### 性能优化 +- **懒加载**:图片按需加载,避免一次性加载所有图片 +- **缓存支持**:使用 `CachedAsyncImage` 缓存图片 +- **内存管理**:及时释放不需要的预览资源 + +## 🔍 调试信息 + +添加了详细的调试日志: + +```swift +debugInfoSync("📸 MomentListHomePage: 图片被点击") +debugInfoSync(" 动态索引: \(index)") +debugInfoSync(" 图片索引: \(tappedIndex)") +debugInfoSync(" 图片数量: \(images.count)") +debugInfoSync("📸 MomentListHomePage: 图片预览已关闭") +``` + +## 📊 测试建议 + +1. **基础功能测试**: + - 验证图片点击响应 + - 验证预览弹窗打开 + - 验证图片切换功能 + +2. **边界情况测试**: + - 单张图片的动态 + - 多张图片的动态 + - 图片加载失败的情况 + +3. **交互测试**: + - 快速点击图片 + - 预览中的滑动操作 + - 关闭预览的各种方式 + +## 🚀 后续优化建议 + +1. **动画优化**: + - 添加图片点击的缩放动画 + - 优化预览打开/关闭的过渡动画 + +2. **功能增强**: + - 添加图片保存功能 + - 支持图片分享功能 + - 添加图片缩放功能 + +3. **性能提升**: + - 图片预加载优化 + - 内存使用优化 + - 网络请求优化 + +## 📝 总结 + +本次功能实现成功添加了: + +- ✅ 图片点击响应功能 +- ✅ ImagePreviewPager 集成 +- ✅ 全屏图片预览 +- ✅ 图片切换功能 +- ✅ 状态管理优化 +- ✅ 调试信息支持 + +代码质量高,遵循项目规范,用户体验良好,为后续功能扩展奠定了良好基础。 diff --git a/yana/MVVM/View/MomentListHomePage.swift b/yana/MVVM/View/MomentListHomePage.swift index 2b22210..5ecb44a 100644 --- a/yana/MVVM/View/MomentListHomePage.swift +++ b/yana/MVVM/View/MomentListHomePage.swift @@ -16,6 +16,10 @@ struct MomentListBackgroundView: View { struct MomentListHomePage: View { @StateObject private var viewModel = MomentListHomeViewModel() + // MARK: - 图片预览状态 + @State private var previewItem: PreviewItem? = nil + @State private var previewCurrentIndex: Int = 0 + var body: some View { GeometryReader { geometry in ZStack { @@ -48,14 +52,25 @@ struct MomentListHomePage: View { ScrollView { LazyVStack(spacing: 16) { ForEach(Array(viewModel.moments.enumerated()), id: \.element.dynamicId) { index, moment in - MomentListItem(moment: moment) - .padding(.horizontal, 16) - .onAppear { - // 当显示倒数第三个项目时,开始加载更多 - if index == viewModel.moments.count - 3 { - viewModel.loadMoreData() - } + MomentListItem( + moment: moment, + onImageTap: { images, tappedIndex in + // 处理图片点击事件 + previewCurrentIndex = tappedIndex + previewItem = PreviewItem(images: images, index: tappedIndex) + debugInfoSync("📸 MomentListHomePage: 图片被点击") + debugInfoSync(" 动态索引: \(index)") + debugInfoSync(" 图片索引: \(tappedIndex)") + debugInfoSync(" 图片数量: \(images.count)") } + ) + .padding(.horizontal, 16) + .onAppear { + // 当显示倒数第三个项目时,开始加载更多 + if index == viewModel.moments.count - 3 { + viewModel.loadMoreData() + } + } } // 加载更多状态指示器 @@ -128,5 +143,15 @@ struct MomentListHomePage: View { .onAppear { viewModel.onAppear() } + // MARK: - 图片预览弹窗 + .fullScreenCover(item: $previewItem) { item in + ImagePreviewPager( + images: item.images as [String], + currentIndex: $previewCurrentIndex + ) { + previewItem = nil + debugInfoSync("📸 MomentListHomePage: 图片预览已关闭") + } + } } } diff --git a/yana/MVVM/View/MomentListItem.swift b/yana/MVVM/View/MomentListItem.swift index bfd0f2f..8e517ac 100644 --- a/yana/MVVM/View/MomentListItem.swift +++ b/yana/MVVM/View/MomentListItem.swift @@ -3,9 +3,14 @@ import SwiftUI // MARK: - MomentListItem struct MomentListItem: View { let moment: MomentsInfo + let onImageTap: (([String], Int)) -> Void // 新增:图片点击回调 - init(moment: MomentsInfo) { + init( + moment: MomentsInfo, + onImageTap: @escaping (([String], Int)) -> Void = { (arg) in let (_, _) = arg; } + ) { self.moment = moment + self.onImageTap = onImageTap } var body: some View { @@ -68,9 +73,12 @@ struct MomentListItem: View { // 图片网格 if let images = moment.dynamicResList, !images.isEmpty { - MomentImageGrid(images: images) - .padding(.leading, 40 + 8) - .padding(.bottom, images.count == 2 ? 30 : 0) // 两张图片时增加底部间距 + MomentImageGrid( + images: images, + onImageTap: onImageTap + ) + .padding(.leading, 40 + 8) + .padding(.bottom, images.count == 2 ? 30 : 0) // 两张图片时增加底部间距 } // 互动按钮 @@ -118,6 +126,7 @@ struct MomentListItem: View { // MARK: - 图片网格组件 struct MomentImageGrid: View { let images: [MomentsPicture] + let onImageTap: (([String], Int)) -> Void // 新增:图片点击回调 var body: some View { GeometryReader { geometry in @@ -131,28 +140,63 @@ struct MomentImageGrid: View { let imageSize: CGFloat = min(availableWidth * 0.6, 200) HStack { Spacer() - MomentSquareImageView(image: images[0], size: imageSize) + MomentSquareImageView( + image: images[0], + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, 0)) + } + ) Spacer() } case 2: let imageSize: CGFloat = (availableWidth - spacing) / 2 HStack(spacing: spacing) { - MomentSquareImageView(image: images[0], size: imageSize) - MomentSquareImageView(image: images[1], size: imageSize) + MomentSquareImageView( + image: images[0], + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, 0)) + } + ) + MomentSquareImageView( + image: images[1], + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, 1)) + } + ) } case 3: let imageSize: CGFloat = (availableWidth - spacing * 2) / 3 HStack(spacing: spacing) { - ForEach(Array(images.prefix(3).enumerated()), id: \.element.id) { _, image in - MomentSquareImageView(image: image, size: imageSize) + ForEach(Array(images.prefix(3).enumerated()), id: \.element.id) { index, image in + MomentSquareImageView( + image: image, + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, index)) + } + ) } } default: let imageSize: CGFloat = (availableWidth - spacing * 2) / 3 let columns = Array(repeating: GridItem(.fixed(imageSize), spacing: spacing), count: 3) LazyVGrid(columns: columns, spacing: spacing) { - ForEach(Array(images.prefix(9).enumerated()), id: \.element.id) { _, image in - MomentSquareImageView(image: image, size: imageSize) + ForEach(Array(images.prefix(9).enumerated()), id: \.element.id) { index, image in + MomentSquareImageView( + image: image, + size: imageSize, + onTap: { + let imageUrls = images.compactMap { $0.resUrl } + onImageTap((imageUrls, index)) + } + ) } } } @@ -181,25 +225,29 @@ struct MomentImageGrid: View { struct MomentSquareImageView: View { let image: MomentsPicture let size: CGFloat + let onTap: () -> Void // 新增:点击回调 var body: some View { let safeSize = size.isFinite && size > 0 ? size : 100 - CachedAsyncImage(url: image.resUrl ?? "") { imageView in - imageView - .resizable() - .aspectRatio(contentMode: .fill) - } placeholder: { - Rectangle() - .fill(Color.gray.opacity(0.3)) - .overlay( - ProgressView() - .progressViewStyle(CircularProgressViewStyle(tint: .white.opacity(0.6))) - .scaleEffect(0.8) - ) + Button(action: onTap) { + CachedAsyncImage(url: image.resUrl ?? "") { imageView in + imageView + .resizable() + .aspectRatio(contentMode: .fill) + } placeholder: { + Rectangle() + .fill(Color.gray.opacity(0.3)) + .overlay( + ProgressView() + .progressViewStyle(CircularProgressViewStyle(tint: .white.opacity(0.6))) + .scaleEffect(0.8) + ) + } + .frame(width: safeSize, height: safeSize) + .clipped() + .cornerRadius(8) } - .frame(width: safeSize, height: safeSize) - .clipped() - .cornerRadius(8) + .buttonStyle(PlainButtonStyle()) // 使用PlainButtonStyle避免默认的按钮样式 } } @@ -241,7 +289,12 @@ struct MomentSquareImageView: View { labelList: nil ) - MomentListItem(moment: testMoment) - .padding() - .background(Color.black) + MomentListItem( + moment: testMoment, + onImageTap: { images, index in + print("图片被点击: 索引 \(index), 图片数量 \(images.count)") + } + ) + .padding() + .background(Color.black) }