SwiftUI中为UIImage实现.draggable与.dropDestination拖拽功能
实现SwiftUI照片拼贴的拖拽交换功能
针对你的需求,我们可以通过自定义Transferable数据类型结合.draggable和.dropDestination来实现照片插槽间的拖拽交换,比直接传递UIImage更高效且符合MVVM架构。以下是分步实现方案:
1. 定义拖拽数据类型
创建一个自定义结构体,用来传递拖拽的源位置信息(页面ID和图片索引),避免直接传递大体积的UIImage:
import SwiftUI import UniformTypeIdentifiers struct DraggablePhotoInfo: Transferable { let pageID: Int let photoIndex: Int static var transferRepresentation: some TransferRepresentation { // 使用自定义UTI标识数据类型,避免与系统类型冲突 DataRepresentation(exportedContentType: .init("com.yourapp.draggable-photo-info")) { info in try JSONEncoder().encode(info) } importing: { data in try JSONDecoder().decode(Self.self, from: data) } } }
2. 修改PhotoPlaceholderView添加拖拽交互
更新占位视图,添加.draggable(仅当有图片时启用)和.dropDestination修饰符:
struct PhotoPlaceholderView: View { @ObservedObject var vm: EditorViewModel var pageID: Int var photoIndex: Int let width: CGFloat let height: CGFloat private var hasImage: Bool { vm.pages[pageID]?.images[photoIndex] != nil } var body: some View { Rectangle() .fill(hasImage ? Color.clear : Color.gray) .frame(width: width, height: height) .overlay( Group { if hasImage { Image(uiImage: vm.pages[pageID]!.images[photoIndex]!) .resizable() .aspectRatio(contentMode: .fill) } else { Image(systemName: "photo") .foregroundColor(.white) } } ) .clipped() .onTapGesture { vm.activeImageIndex = photoIndex vm.isMultipleSelection = false vm.isImagePickerPresented = true } // 仅当有图片时允许拖拽 .draggable(hasImage ? DraggablePhotoInfo(pageID: pageID, photoIndex: photoIndex) : nil) // 接收拖拽的目标处理 .dropDestination(for: DraggablePhotoInfo.self) { items, location in guard let sourceInfo = items.first else { return false } // 避免拖拽到自身位置 guard sourceInfo.pageID != pageID || sourceInfo.photoIndex != photoIndex else { return false } // 调用ViewModel的交换逻辑 vm.swapImages(fromPage: sourceInfo.pageID, fromIndex: sourceInfo.photoIndex, toPage: pageID, toIndex: photoIndex) return true } } }
3. 在ViewModel中实现图片交换逻辑
给EditorViewModel添加交换图片的方法,负责处理数据层的更新:
class EditorViewModel: ObservableObject { @Published var pages: [Int: CollagePage] = [:] @Published var activeImageIndex: Int = 0 @Published var isMultipleSelection: Bool = false @Published var isImagePickerPresented: Bool = false func addPage() { let newID = (pages.keys.max() ?? 0) + 1 pages[newID] = CollagePage(id: newID) } // 新增图片交换方法 func swapImages(fromPage sourcePageID: Int, fromIndex sourceIndex: Int, toPage targetPageID: Int, toIndex targetIndex: Int) { guard var sourcePage = pages[sourcePageID], var targetPage = pages[targetPageID], let sourceImage = sourcePage.images[sourceIndex] else { return } // 交换源和目标位置的图片(支持目标位置为空的情况) let targetImage = targetPage.images[targetIndex] sourcePage.images[sourceIndex] = targetImage targetPage.images[targetIndex] = sourceImage // 更新ViewModel中的页面数据 pages[sourcePageID] = sourcePage pages[targetPageID] = targetPage } }
可选:让UIImage支持Transferable(用于外部图片拖拽)
如果需要支持从系统相册或其他App拖拽图片到你的拼贴插槽,可以扩展UIImage遵循Transferable:
extension UIImage: Transferable { public static var transferRepresentation: some TransferRepresentation { DataRepresentation(exportedContentType: .image) { image in guard let data = image.pngData() else { throw TransferError.exportFailed } return data } importing: { data in guard let image = UIImage(data: data) else { throw TransferError.importFailed } return image } } enum TransferError: Error { case exportFailed case importFailed } }
此时你可以在PhotoPlaceholderView的.dropDestination中添加对UIImage.self的支持,实现外部图片导入功能。
内容的提问来源于stack exchange,提问作者user1207524
相关产品推荐
相关产品推荐

