diff --git a/yana/Features/EditFeedFeature.swift b/yana/Features/EditFeedFeature.swift index 21c68a7..101c131 100644 --- a/yana/Features/EditFeedFeature.swift +++ b/yana/Features/EditFeedFeature.swift @@ -1,6 +1,7 @@ import Foundation import ComposableArchitecture import SwiftUI +import PhotosUI // 修正:导入PhotosUI @Reducer struct EditFeedFeature { @@ -11,9 +12,24 @@ struct EditFeedFeature { var errorMessage: String? = nil var shouldDismiss: Bool = false + var selectedImages: [PhotosPickerItem] = [] + var processedImages: [UIImage] = [] + var canAddMoreImages: Bool { + processedImages.count < 9 + } + var canPublish: Bool { !content.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && !isLoading } + // 手动实现Equatable,selectedImages只比较数量(PhotosPickerItem不支持Equatable) + static func == (lhs: State, rhs: State) -> Bool { + lhs.content == rhs.content && + lhs.isLoading == rhs.isLoading && + lhs.errorMessage == rhs.errorMessage && + lhs.shouldDismiss == rhs.shouldDismiss && + lhs.processedImages == rhs.processedImages && + lhs.selectedImages.count == rhs.selectedImages.count + } } enum Action { @@ -23,6 +39,11 @@ struct EditFeedFeature { case clearError case dismissView case clearDismissFlag + // 新增图片相关Action + case photosPickerItemsChanged([PhotosPickerItem]) + case processPhotosPickerItems([PhotosPickerItem]) + case updateProcessedImages([UIImage]) + case removeImage(Int) } @Dependency(\.apiService) var apiService @@ -83,6 +104,36 @@ struct EditFeedFeature { case .clearDismissFlag: state.shouldDismiss = false return .none + case .photosPickerItemsChanged(let items): + state.selectedImages = items + return .run { send in + await send(.processPhotosPickerItems(items)) + } + case .processPhotosPickerItems(let items): + let currentImages = state.processedImages + return .run { send in + var newImages = currentImages + for item in items { + guard let data = try? await item.loadTransferable(type: Data.self), + let image = UIImage(data: data) else { continue } + if newImages.count < 9 { + newImages.append(image) + } + } + await MainActor.run { + send(.updateProcessedImages(newImages)) + } + } + case .updateProcessedImages(let images): + state.processedImages = images + return .none + case .removeImage(let index): + guard index < state.processedImages.count else { return .none } + state.processedImages.remove(at: index) + if index < state.selectedImages.count { + state.selectedImages.remove(at: index) + } + return .none } } } diff --git a/yana/Views/CreateFeedView.swift b/yana/Views/CreateFeedView.swift index 2cd98b5..82c630c 100644 --- a/yana/Views/CreateFeedView.swift +++ b/yana/Views/CreateFeedView.swift @@ -168,66 +168,66 @@ struct CreateFeedView: View { } // MARK: - iOS 16+ 图片选择网格组件 -struct ModernImageSelectionGrid: View { - let images: [UIImage] - let selectedItems: [PhotosPickerItem] - let canAddMore: Bool - let onItemsChanged: ([PhotosPickerItem]) -> Void - let onRemoveImage: (Int) -> Void - - private let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 3) - - var body: some View { - WithPerceptionTracking { - LazyVGrid(columns: columns, spacing: 8) { - // 显示已选择的图片 - ForEach(Array(images.enumerated()), id: \.offset) { index, image in - ZStack(alignment: .topTrailing) { - Image(uiImage: image) - .resizable() - .aspectRatio(contentMode: .fill) - .frame(height: 100) - .clipped() - .cornerRadius(8) - - // 删除按钮 - Button(action: { - onRemoveImage(index) - }) { - Image(systemName: "xmark.circle.fill") - .font(.system(size: 20)) - .foregroundColor(.white) - .background(Color.black.opacity(0.6)) - .clipShape(Circle()) - } - .padding(4) - } - } - - // 添加图片按钮 - if canAddMore { - PhotosPicker( - selection: .init( - get: { selectedItems }, - set: onItemsChanged - ), - maxSelectionCount: 9, - matching: .images - ) { - RoundedRectangle(cornerRadius: 8) - .fill(Color.white.opacity(0.1)) - .frame(height: 100) - .overlay( - Image(systemName: "plus") - .font(.system(size: 40)) - .foregroundColor(.white.opacity(0.6)) - ) - } - } - } - } - } -} +//struct ModernImageSelectionGrid: View { +// let images: [UIImage] +// let selectedItems: [PhotosPickerItem] +// let canAddMore: Bool +// let onItemsChanged: ([PhotosPickerItem]) -> Void +// let onRemoveImage: (Int) -> Void +// +// private let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 3) +// +// var body: some View { +// WithPerceptionTracking { +// LazyVGrid(columns: columns, spacing: 8) { +// // 显示已选择的图片 +// ForEach(Array(images.enumerated()), id: \.offset) { index, image in +// ZStack(alignment: .topTrailing) { +// Image(uiImage: image) +// .resizable() +// .aspectRatio(contentMode: .fill) +// .frame(height: 100) +// .clipped() +// .cornerRadius(8) +// +// // 删除按钮 +// Button(action: { +// onRemoveImage(index) +// }) { +// Image(systemName: "xmark.circle.fill") +// .font(.system(size: 20)) +// .foregroundColor(.white) +// .background(Color.black.opacity(0.6)) +// .clipShape(Circle()) +// } +// .padding(4) +// } +// } +// +// // 添加图片按钮 +// if canAddMore { +// PhotosPicker( +// selection: .init( +// get: { selectedItems }, +// set: onItemsChanged +// ), +// maxSelectionCount: 9, +// matching: .images +// ) { +// RoundedRectangle(cornerRadius: 8) +// .fill(Color.white.opacity(0.1)) +// .frame(height: 100) +// .overlay( +// Image(systemName: "plus") +// .font(.system(size: 40)) +// .foregroundColor(.white.opacity(0.6)) +// ) +// } +// } +// } +// } +// } +//} // MARK: - 预览 //#Preview { diff --git a/yana/Views/EditFeedView.swift b/yana/Views/EditFeedView.swift index f878dff..464bd0d 100644 --- a/yana/Views/EditFeedView.swift +++ b/yana/Views/EditFeedView.swift @@ -67,6 +67,20 @@ struct EditFeedView: View { VStack(spacing: 0) { headerView(geometry: geometry, viewStore: viewStore) textInputArea(viewStore: viewStore) + // 新增:图片输入区域 + ModernImageSelectionGrid( + images: viewStore.processedImages, + selectedItems: viewStore.selectedImages, + canAddMore: viewStore.canAddMoreImages, + onItemsChanged: { items in + viewStore.send(.photosPickerItemsChanged(items)) + }, + onRemoveImage: { index in + viewStore.send(.removeImage(index)) + } + ) + .padding(.horizontal, 24) + .padding(.bottom, 32) Spacer() if !isKeyboardVisible { publishButtonBottom(viewStore: viewStore, geometry: geometry) @@ -193,3 +207,65 @@ struct EditFeedView: View { //#Preview { // EditFeedView() //} + +// MARK: - 九宫格图片选择组件 +import PhotosUI +struct ModernImageSelectionGrid: View { + let images: [UIImage] + let selectedItems: [PhotosPickerItem] + let canAddMore: Bool + let onItemsChanged: ([PhotosPickerItem]) -> Void + let onRemoveImage: (Int) -> Void + private let columns = Array(repeating: GridItem(.flexible(), spacing: 8), count: 3) + var body: some View { + // 将gridItemSize移入body内部变量 + let totalSpacing: CGFloat = 8 * 2 + let totalWidth = UIScreen.main.bounds.width - 24 * 2 - totalSpacing + let gridItemSize: CGFloat = totalWidth / 3 + WithPerceptionTracking { + LazyVGrid(columns: columns, spacing: 8) { + ForEach(Array(images.enumerated()), id: \ .offset) { index, image in + ZStack(alignment: .topTrailing) { + Image(uiImage: image) + .resizable() + .aspectRatio(1, contentMode: .fill) + .frame(width: gridItemSize, height: gridItemSize) + .clipped() + .cornerRadius(12) + Button(action: { + onRemoveImage(index) + }) { + Image(systemName: "xmark.circle.fill") + .font(.system(size: 20)) + .foregroundColor(.white) + .background(Color.black.opacity(0.6)) + .clipShape(Circle()) + } + .padding(4) + } + } + if canAddMore { + PhotosPicker( + selection: .init( + get: { selectedItems }, + set: { items in DispatchQueue.main.async { onItemsChanged(items) } } + ), + maxSelectionCount: 9 - images.count, + matching: .images + ) { + RoundedRectangle(cornerRadius: 12) + .fill(Color(hexString: "1C143A")) + .frame(width: gridItemSize, height: gridItemSize) + .overlay( + Image("add photo") + .resizable() + .frame(width: 40, height: 40) + .opacity(0.6) + ) + } + } + } + } + } + // 移除gridItemSize属性,全部逻辑已移入body +}