Skip to content

Instantly share code, notes, and snippets.

@raphaelhanneken
Last active February 15, 2023 13:29
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save raphaelhanneken/d77b6f9b01bef35709da to your computer and use it in GitHub Desktop.
Save raphaelhanneken/d77b6f9b01bef35709da to your computer and use it in GitHub Desktop.
NSImageView with drag and drop capabilities written in Swift.
//
// DragAndDropImageView.swift
// Iconizer
// https://github.com/raphaelhanneken/iconizer
//
import Cocoa
class DragDropImageView: NSImageView, NSDraggingSource {
/// Holds the last mouse down event, to track the drag distance.
var mouseDownEvent: NSEvent?
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
// Assure editable is set to true, to enable drop capabilities.
isEditable = true
}
required init?(coder: NSCoder) {
super.init(coder: coder)
// Assure editable is set to true, to enable drop capabilities.
isEditable = true
}
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
}
// MARK: - NSDraggingSource
// Since we only want to copy/delete the current image we register ourselfes
// for .Copy and .Delete operations.
func draggingSession(_: NSDraggingSession,
sourceOperationMaskFor _: NSDraggingContext) -> NSDragOperation {
return NSDragOperation.copy.union(.delete)
}
// Clear the ImageView on delete operation; e.g. the image gets
// dropped on the trash can in the dock.
func draggingSession(_: NSDraggingSession, endedAt _: NSPoint,
operation: NSDragOperation) {
if operation == .delete {
image = nil
}
}
// Track mouse down events and safe the to the poperty.
override func mouseDown(with theEvent: NSEvent) {
mouseDownEvent = theEvent
}
// Track mouse dragged events to handle dragging sessions.
override func mouseDragged(with event: NSEvent) {
// Calculate the dragging distance...
let mouseDown = mouseDownEvent!.locationInWindow
let dragPoint = event.locationInWindow
let dragDistance = hypot(mouseDown.x - dragPoint.x, mouseDown.y - dragPoint.y)
// Cancel the dragging session in case of an accidental drag.
if dragDistance < 3 {
return
}
guard let image = self.image else {
return
}
// Do some math to properly resize the given image.
let size = NSSize(width: log10(image.size.width) * 30, height: log10(image.size.height) * 30)
if let draggingImage = image.resize(toSize: size, aspectMode: .fit) {
// Create a new NSDraggingItem with the image as content.
let draggingItem = NSDraggingItem(pasteboardWriter: image)
// Calculate the mouseDown location from the window's coordinate system to the
// ImageView's coordinate system, to use it as origin for the dragging frame.
let draggingFrameOrigin = convert(mouseDown, from: nil)
// Build the dragging frame and offset it by half the image size on each axis
// to center the mouse cursor within the dragging frame.
let draggingFrame = NSRect(origin: draggingFrameOrigin, size: draggingImage.size)
.offsetBy(dx: -draggingImage.size.width / 2, dy: -draggingImage.size.height / 2)
// Assign the dragging frame to the draggingFrame property of our dragging item.
draggingItem.draggingFrame = draggingFrame
// Provide the components of the dragging image.
draggingItem.imageComponentsProvider = {
let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon)
component.contents = image
component.frame = NSRect(origin: NSPoint(), size: draggingFrame.size)
return [component]
}
// Begin actual dragging session. Woohow!
beginDraggingSession(with: [draggingItem], event: mouseDownEvent!, source: self)
}
}
}
@danieljfarrell
Copy link

Thanks for the fantastic Swift code!

On line 100:
Replace rectByOffsetting with offsetBy to make compatible with Swift 2.0.

Also this requires copyWithSize from, https://gist.github.com/behoernchen/cb924aa280f4b9dbb480

@raphaelhanneken
Copy link
Author

Thanks! I'm glad, someone could use this.

@iby
Copy link

iby commented Jul 12, 2017

Apple could use it… 😂

@DesWurstes
Copy link

Line 66

Value of type 'NSImage' has no member 'resize'

Apple's documentation (https://developer.apple.com/documentation/appkit/nsimage) confirms this. Has this been tested with Swift 3 / 4?

@raphaelhanneken
Copy link
Author

Line 66

Value of type 'NSImage' has no member 'resize'

Apple's documentation (https://developer.apple.com/documentation/appkit/nsimage) confirms this. Has this been tested with Swift 3 / 4?

@DesWurstes
I'am using this NSImageExtension, to resize the image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment