Chris Knowles
Chris Knowles

Reputation: 33

Implementing Drag & Drop for Image Well

I am somewhat of a newbie, working on my first Mac app, an element of the functionality of which is that it allows users to drop an image into an image well, the app then needs to perform certain actions on the dropped image.

I have created my image well, and have made it editable, as such I can drop an image onto my image well, and it appears within my app.

Where I'm running into trouble is implementing event handlers so that I carry out actions when an image is dropped onto the image well. Specifically I need to load the image into an NSImage object.

I have had a read through this Mac Developer Library article: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/DragandDrop/DragandDrop.html but can't quite get my head around it.

I would very much appreciate if someone could give me an example of how I would go about achieving this.

Many thanks in anticipation.

Upvotes: 2

Views: 2918

Answers (3)

seanmakesgames
seanmakesgames

Reputation: 771

If you just want an event when the image is dropped on your well:

Hook up the 'selected' sent action on the outlets panel. This will send the action to your associated controller. selected outlet

If you hook up the imagewell as an outlet on the controller, you can access the image that it was switched to. Alternatively, you can access the 'sender' parameter of the generated method, which will be the NSImageView.

- (IBAction)selector:(id)sender {

}

If you are looking for the filename

Then you will need create a subclass of NSImageView like in this gist

header:

#import <Cocoa/Cocoa.h>

@interface KSImageView : NSImageView

@end

implementation:

#import "KSImageView.h"

@implementation KSImageView

- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
  BOOL acceptsDrag = [super performDragOperation:sender];

    if (acceptsDrag) {
        NSPasteboard *pboard = [sender draggingPasteboard];
        NSString *plist = [pboard stringForType:NSFilenamesPboardType];

        if (plist) {

            NSArray *files = [NSPropertyListSerialization propertyListFromData:[plist dataUsingEncoding:NSUTF8StringEncoding]
                                                              mutabilityOption:NSPropertyListImmutable
                                                                        format:nil
                                                              errorDescription:nil];

            if ([files count] == 1) {
                NSDictionary *userInfo = @{@"imageFileName" : [[files objectAtIndex: 0] lastPathComponent]};

                [[NSNotificationCenter defaultCenter] postNotificationName:@"KSImageDroppedNotification"
                                                                    object:nil
                                                                  userInfo:userInfo];
            }
        }
    }

    return acceptsDrag;
}

- (void) delete:(id)sender {
}

- (void) cut:(id)sender {
}

@end

Register for the notification:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(imageSelected:) name:@"KSImageDroppedNotification" object:nil];

Handle it here:

- (void)imageSelected:(NSNotification *)notification {

}

Upvotes: 2

green_knight
green_knight

Reputation: 1385

(This solves the problem that seanmakesgames addresses above, only in Swift 4.2 and more directly.)

import Cocoa

class PathImageWell: NSImageView {

    var draggedFileURL: URL? = nil

    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        let dragSucceeded = super.performDragOperation(sender)
        if dragSucceeded == true {
            let filenameURL = NSURL(from: sender.draggingPasteboard) as URL?
            draggedFileURL = filenameURL
            return true
        }
        return false

    }
}

It's important to go via NSURL since URL does not have an init(NSPasteboard) method; you can retrieve the filename from the URL with

func imageNameFromURL(_ url: URL) -> String {
        return url.deletingPathExtension().lastPathComponent
    } 

If you want to store the image name along with the image, you need to wrap it into a different struct/class with a 'name' property: do not attempt to use NSImage.name, which is a property strictly for use with NSImage(named:) and which shows peculiar behaviours.

Upvotes: 0

kilik52
kilik52

Reputation: 664

In Your nib init function, such as awakeFromNib or windowControllerDidLoadNib, add an Observer:

[self.imageView addObserver:self forKeyPath:@"image" options:NSKeyValueObservingOptionNew context:nil];

And Implement a delegate function:

-(void)observeValueForKeyPath:(NSString *)keyPath
                     ofObject:(id)object
                       change:(NSDictionary *)change
                      context:(void *)context
{
   if(object == self.imageView && [keyPath isEqualToString:@"image"])
   {
      // image changed, do anything your want
   }
}

That's it.

Upvotes: 1

Related Questions