修复相机历史导入页网格缩略图重叠与溢出问题。
采用 1:1 方形容器约束单元格布局,并统一网格间距与边距常量。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -0,0 +1,11 @@
|
|||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
/// 相机历史导入页布局常量。
|
||||||
|
enum CameraHistoryImportDesign {
|
||||||
|
static let gridColumns = 3
|
||||||
|
static let gridHorizontalSpacing: CGFloat = 2
|
||||||
|
static let gridVerticalSpacing: CGFloat = 2
|
||||||
|
static let contentHorizontalPadding: CGFloat = 12
|
||||||
|
static let sectionSpacing: CGFloat = 12
|
||||||
|
static let thumbnailCornerRadius: CGFloat = 4
|
||||||
|
}
|
||||||
@ -11,51 +11,61 @@ struct CameraHistoryImportPhotoCell: View {
|
|||||||
@State private var thumbnail: UIImage?
|
@State private var thumbnail: UIImage?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack(alignment: .topTrailing) {
|
Color.clear
|
||||||
Group {
|
.aspectRatio(1, contentMode: .fit)
|
||||||
if let thumbnail {
|
.overlay {
|
||||||
Image(uiImage: thumbnail)
|
thumbnailContent
|
||||||
.resizable()
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
.scaledToFill()
|
}
|
||||||
} else {
|
.clipShape(RoundedRectangle(cornerRadius: CameraHistoryImportDesign.thumbnailCornerRadius))
|
||||||
Color.gray.opacity(0.15)
|
.overlay(alignment: .topTrailing) {
|
||||||
.overlay {
|
statusBadge
|
||||||
ProgressView()
|
}
|
||||||
.scaleEffect(0.7)
|
.contentShape(Rectangle())
|
||||||
}
|
.onTapGesture {
|
||||||
|
guard !isImported else { return }
|
||||||
|
onToggle()
|
||||||
|
}
|
||||||
|
.task(id: photo.id) {
|
||||||
|
guard thumbnail == nil else { return }
|
||||||
|
if let data = await thumbnailProvider(), let image = UIImage(data: data) {
|
||||||
|
thumbnail = image
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.frame(maxWidth: .infinity)
|
}
|
||||||
.aspectRatio(1, contentMode: .fit)
|
|
||||||
.clipped()
|
|
||||||
|
|
||||||
if isImported {
|
@ViewBuilder
|
||||||
Text("已导入")
|
private var thumbnailContent: some View {
|
||||||
.font(.system(size: 10, weight: .medium))
|
if let thumbnail {
|
||||||
.foregroundStyle(.white)
|
Image(uiImage: thumbnail)
|
||||||
.padding(.horizontal, 6)
|
.resizable()
|
||||||
.padding(.vertical, 3)
|
.scaledToFill()
|
||||||
.background(Color.black.opacity(0.55))
|
} else {
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 4))
|
Color.gray.opacity(0.15)
|
||||||
.padding(6)
|
.overlay {
|
||||||
} else {
|
ProgressView()
|
||||||
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
|
.scaleEffect(0.7)
|
||||||
.font(.system(size: 22))
|
}
|
||||||
.foregroundStyle(isSelected ? AppDesign.primary : .white)
|
|
||||||
.shadow(radius: 2)
|
|
||||||
.padding(6)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
}
|
||||||
.onTapGesture {
|
|
||||||
guard !isImported else { return }
|
@ViewBuilder
|
||||||
onToggle()
|
private var statusBadge: some View {
|
||||||
}
|
if isImported {
|
||||||
.task(id: photo.id) {
|
Text("已导入")
|
||||||
guard thumbnail == nil else { return }
|
.font(.system(size: 10, weight: .medium))
|
||||||
if let data = await thumbnailProvider(), let image = UIImage(data: data) {
|
.foregroundStyle(.white)
|
||||||
thumbnail = image
|
.padding(.horizontal, 6)
|
||||||
}
|
.padding(.vertical, 3)
|
||||||
|
.background(Color.black.opacity(0.55))
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 4))
|
||||||
|
.padding(6)
|
||||||
|
} else {
|
||||||
|
Image(systemName: isSelected ? "checkmark.circle.fill" : "circle")
|
||||||
|
.font(.system(size: 22))
|
||||||
|
.foregroundStyle(isSelected ? AppDesign.primary : .white)
|
||||||
|
.shadow(color: .black.opacity(0.35), radius: 2, x: 0, y: 1)
|
||||||
|
.padding(6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,8 +21,9 @@ struct CameraHistoryImportSectionHeader: View {
|
|||||||
.foregroundStyle(AppDesign.primary)
|
.foregroundStyle(AppDesign.primary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, CameraHistoryImportDesign.contentHorizontalPadding)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.background(WiredTransferDesign.pageBackground)
|
.background(WiredTransferDesign.pageBackground)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,10 +70,10 @@ struct CameraHistoryImportView: View {
|
|||||||
|
|
||||||
private func photoList(sections: [CameraPhotoSection]) -> some View {
|
private func photoList(sections: [CameraPhotoSection]) -> some View {
|
||||||
ScrollView {
|
ScrollView {
|
||||||
LazyVStack(spacing: 12, pinnedViews: [.sectionHeaders]) {
|
LazyVStack(spacing: CameraHistoryImportDesign.sectionSpacing, pinnedViews: [.sectionHeaders]) {
|
||||||
ForEach(sections) { section in
|
ForEach(sections) { section in
|
||||||
Section {
|
Section {
|
||||||
LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible()), GridItem(.flexible())], spacing: 2) {
|
LazyVGrid(columns: gridColumns, spacing: CameraHistoryImportDesign.gridVerticalSpacing) {
|
||||||
ForEach(section.photos, id: \.id) { photo in
|
ForEach(section.photos, id: \.id) { photo in
|
||||||
CameraHistoryImportPhotoCell(
|
CameraHistoryImportPhotoCell(
|
||||||
photo: photo,
|
photo: photo,
|
||||||
@ -94,10 +94,18 @@ struct CameraHistoryImportView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 2)
|
.padding(.horizontal, CameraHistoryImportDesign.contentHorizontalPadding)
|
||||||
|
.padding(.bottom, 16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var gridColumns: [GridItem] {
|
||||||
|
Array(
|
||||||
|
repeating: GridItem(.flexible(), spacing: CameraHistoryImportDesign.gridHorizontalSpacing),
|
||||||
|
count: CameraHistoryImportDesign.gridColumns
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private func emptyView(message: String) -> some View {
|
private func emptyView(message: String) -> some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
Text(message)
|
Text(message)
|
||||||
|
|||||||
Reference in New Issue
Block a user