Created
August 29, 2023 17:27
-
-
Save jb-apps/d8e772451f3b53ed982923786994ad90 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ImageDraggableModifier.swift | |
// Pictura | |
// | |
// Created by Jonathan Benavides Vallejo on 25.08.23. | |
// | |
import SwiftUI | |
import AppKit | |
struct ImageDraggableModifier: ViewModifier { | |
/// URL to where the image is located | |
let fileURL: URL | |
/// Useful when dealing with big images, Pictura images are huge so the preview is a bit wonky, by using thumbnails everything looks smooth | |
var thumbnail: NSImage? | |
/// The drag operation to perform, i.e: .copy or .move | |
let dragOperation: NSDragOperation | |
/// The drag operation has completed, this does not mean the image has been dragged outside. | |
/// For example if you are performing a ".move" you must check if the image still exists. | |
var dragOperationCompleted: (() -> Void)? | |
class DraggableView: NSView, NSDraggingSource { | |
var fileURL: URL! | |
var dragOperation: NSDragOperation! | |
var thumbnail: NSImage? | |
var dragOperationCompleted: (() -> Void)? | |
private var previewImage: NSImage? { | |
thumbnail ?? NSImage(contentsOf: fileURL) | |
} | |
override func mouseDown(with event: NSEvent) { | |
let pasteboardItem = NSPasteboardItem() | |
pasteboardItem.setString(fileURL.absoluteString, forType: .fileURL) | |
let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem) | |
draggingItem.setDraggingFrame(self.bounds, contents: previewImage) | |
beginDraggingSession(with: [draggingItem], event: event, source: self) | |
} | |
func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation { | |
return dragOperation | |
} | |
func draggingSession(_ session: NSDraggingSession, endedAt screenPoint: NSPoint, operation: NSDragOperation) { | |
dragOperationCompleted?() | |
} | |
} | |
func body(content: Content) -> some View { | |
content | |
.overlay { | |
RepresentedView( | |
fileURL: fileURL, | |
dragOperation: dragOperation, | |
thumbnail: thumbnail, | |
dragOperationCompleted: dragOperationCompleted | |
) | |
.frame(maxWidth: .infinity, maxHeight: .infinity) | |
} | |
} | |
struct RepresentedView: NSViewRepresentable { | |
let fileURL: URL | |
var dragOperation: NSDragOperation | |
var thumbnail: NSImage? | |
var dragOperationCompleted: (() -> Void)? | |
func makeNSView(context: Context) -> DraggableView { | |
let view = DraggableView() | |
view.fileURL = fileURL | |
view.dragOperation = dragOperation | |
view.thumbnail = thumbnail | |
view.dragOperationCompleted = dragOperationCompleted | |
return view | |
} | |
func updateNSView(_ nsView: DraggableView, context: Context) { } | |
} | |
} | |
extension View { | |
/// Makes the view draggable with the file at the provided URL. | |
/// | |
/// - Parameters: | |
/// - fileURL: The file URL of the image that you want to be draggable from the view. | |
/// - dragOperation: The drag operation to perform when dragging. Defaults to `.copy`. | |
/// - thumbnail: An optional thumbnail to display when dragging, otherwise the image at fileURL will be shown. | |
/// - dragOperationCompleted: An optional closure that gets called when the drag operation is completed. | |
/// | |
/// ``` | |
/// // Usage example: | |
/// Image("background") | |
/// .resizable() | |
/// .draggableImage(fileURL: URL(string: "path_to_your_file")!) | |
/// ``` | |
func draggableImage( | |
fileURL: URL, | |
dragOperation: NSDragOperation = .copy, | |
thumbnail: NSImage? = nil, | |
dragOperationCompleted: (() -> Void)? = nil | |
) -> some View { | |
self.modifier( | |
ImageDraggableModifier( | |
fileURL: fileURL, | |
thumbnail: thumbnail, | |
dragOperation: dragOperation, | |
dragOperationCompleted: dragOperationCompleted | |
) | |
) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The default
onDrag
view modifier in SwiftUI only supports amove
drag operation.This ImageDraggableModifier allows custom drag operations on macOS like
.copy
using pure SwiftUI.Usage: