我今天开始使用objective-c 为OSX (mountain lion) 开发一个应用程序。
我有一堆按钮,我想将它们拖到其他对象中,例如文本字段。我遵循了苹果开发网站上的教程,但我无法让拖动部分工作(例如,我可以将文件从 finder 拖动到文本文件中并显示其路径)。
我首先创建了一个 NSButton 子类:@interface mp3OCDDraggableButton : NSButton
并实现了如下所述的方法:
https://developer.apple.com/library/mac/#samplecode/CocoaDragAndDrop/Introduction/Intro.html
但那东西不动!
我在 mouseDown: 中放入了一些日志消息,我可以在其中看到,但如果我用 mouseDragged 替换它就不行: - 这是否告诉我什么?
任何人都可以发布具有此功能的简单示例吗?我找不到任何有效的东西:\
提前谢谢了!
这是我迄今为止的可拖动按钮的代码。与教程中的几乎相同。
//myDraggableButton.h
@interface myDraggableButton : NSButton <NSDraggingSource, NSPasteboardItemDataProvider>
@end
和
//myDraggableButton.m
#import "myDraggableButton.h"
@implementation myDraggableButton
- (void)mouseDown:(NSEvent *)theEvent:(NSEvent*)event
{
NSLog(@"mouseDown");
NSPasteboardItem *pbItem = [NSPasteboardItem new];
[pbItem setDataProvider:self forTypes:[NSArray arrayWithObjects:NSPasteboardTypeString, nil]];
NSDraggingItem *dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem];
NSRect draggingRect = self.bounds;
[dragItem setDraggingFrame:draggingRect contents:[self image]];
NSDraggingSession *draggingSession = [self beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem] event:event source:self];
draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
draggingSession.draggingFormation = NSDraggingFormationNone;
}
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
switch (context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationCopy;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationCopy;
break;
}
}
- (BOOL)acceptsFirstMouse:(NSEvent *)event
{
return YES;
}
- (void)pasteboard:(NSPasteboard *)sender item:(NSPasteboardItem *)item provideDataForType:(NSString *)type
{
if ( [type compare: NSPasteboardTypeTIFF] == NSOrderedSame ) {
[sender setData:[[self image] TIFFRepresentation] forType:NSPasteboardTypeTIFF];
} else if ( [type compare: NSPasteboardTypePDF] == NSOrderedSame ) {
[sender setData:[self dataWithPDFInsideRect:[self bounds]] forType:NSPasteboardTypePDF];
}
}
@end
最佳答案
我为死灵法术道歉,但我在尝试自己实现时偶然发现了这个问题,并想分享答案,因为它可能对其他人有用。
此解决方案在 NSActionCell
和 NSControl
上使用类别,因为我需要能够拖动多种控件类型,而不仅仅是按钮。您可以根据自己的需要/类(class)进行调整。
我已经注释掉了与隐藏/取消隐藏控件时不需要的淡入淡出动画的解决方法黑客相关的代码。我摆弄了隐式动画等,但找不到更好的方法。 hack 确实工作得很好,但我忽略了窗口实现代码。
@implementation NSControl (DragControl)
- (NSDraggingSession*)beginDraggingSessionWithDraggingCell:(NSActionCell <NSDraggingSource> *)cell event:(NSEvent*) theEvent
{
NSImage* image = [self imageForCell:cell];
NSDraggingItem* di = [[NSDraggingItem alloc] initWithPasteboardWriter:image];
NSRect dragFrame = [self frameForCell:cell];
dragFrame.size = image.size;
[di setDraggingFrame:dragFrame contents:image];
NSArray* items = [NSArray arrayWithObject:di];
[self setHidden:YES];
return [self beginDraggingSessionWithItems:items event:theEvent source:cell];
}
- (NSRect)frameForCell:(NSCell*)cell
{
// override in multi-cell cubclasses!
return self.bounds;
}
- (NSImage*)imageForCell:(NSCell*)cell
{
return [self imageForCell:cell highlighted:[cell isHighlighted]];
}
- (NSImage*)imageForCell:(NSCell*)cell highlighted:(BOOL) highlight
{
// override in multicell cubclasses to just get an image of the dragged cell.
// for any single cell control we can just make sure that cell is the controls cell
if (cell == self.cell || cell == nil) { // nil signifies entire control
// basically a bitmap of the control
// NOTE: the cell is irrelevant when dealing with a single cell control
BOOL isHighlighted = [cell isHighlighted];
[cell setHighlighted:highlight];
NSRect cellFrame = [self frameForCell:cell];
// We COULD just draw the cell, to an NSImage, but button cells draw their content
// in a special way that would complicate that implementation (ex text alignment).
// subclasses that have multiple cells may wish to override this to only draw the cell
NSBitmapImageRep* rep = [self bitmapImageRepForCachingDisplayInRect:cellFrame];
NSImage* image = [[NSImage alloc] initWithSize:rep.size];
[self cacheDisplayInRect:cellFrame toBitmapImageRep:rep];
[image addRepresentation:rep];
// reset the original cell state
[cell setHighlighted:isHighlighted];
return image;
}
// cell doesnt belong to this control!
return nil;
}
#pragma mark NSDraggingDestination
// message forwarding doesnt work for NSDraggingDestination methods
// because NSView implements empty methods for the protocol
/*
- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender
{
return [self.cell draggingEntered:sender];
}
- (void)draggingExited:(id < NSDraggingInfo >)sender
{
[self.cell draggingExited:sender];
}
- (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender
{
return [self.cell prepareForDragOperation:sender];
}
- (BOOL)performDragOperation:(id < NSDraggingInfo >)sender
{
return [self.cell performDragOperation:sender];
}
- (void)concludeDragOperation:(id < NSDraggingInfo >)sender
{
return [self.cell concludeDragOperation:sender];
}
*/
- (void)draggingEnded:(id < NSDraggingInfo >)sender
{
// implement whatever you want to do here.
[self setHidden:NO];
}
@end
static NSPoint _dragImageOffset;
@implementation NSActionCell (DragCell)
- (void)setControlView:(NSView *)view
{
// this is a bit of a hack, but the easiest way to make the control dragging work.
// force the control to accept image drags.
// the control will forward us the drag destination events via our DragControl category
[view registerForDraggedTypes:[NSImage imagePasteboardTypes]];
[super setControlView:view];
}
- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp
{
BOOL result = NO;
NSPoint currentPoint = theEvent.locationInWindow;
BOOL done = NO;
BOOL trackContinously = [self startTrackingAt:currentPoint inView:controlView];
BOOL mouseIsUp = NO;
NSEvent *event = nil;
while (!done)
{
NSPoint lastPoint = currentPoint;
event = [NSApp nextEventMatchingMask:(NSLeftMouseUpMask|NSLeftMouseDraggedMask)
untilDate:[NSDate distantFuture]
inMode:NSEventTrackingRunLoopMode
dequeue:YES];
if (event)
{
currentPoint = event.locationInWindow;
// Send continueTracking.../stopTracking...
if (trackContinously)
{
if (![self continueTracking:lastPoint
at:currentPoint
inView:controlView])
{
done = YES;
[self stopTracking:lastPoint
at:currentPoint
inView:controlView
mouseIsUp:mouseIsUp];
}
if (self.isContinuous)
{
[NSApp sendAction:self.action
to:self.target
from:controlView];
}
}
mouseIsUp = (event.type == NSLeftMouseUp);
done = done || mouseIsUp;
if (untilMouseUp)
{
result = mouseIsUp;
} else {
// Check if the mouse left our cell rect
result = NSPointInRect([controlView
convertPoint:currentPoint
fromView:nil], cellFrame);
if (!result)
done = YES;
}
if (done && result && ![self isContinuous])
[NSApp sendAction:self.action
to:self.target
from:controlView];
else {
done = YES;
result = YES;
// this initiates the control drag event using NSDragging protocols
NSControl* cv = (NSControl*)self.controlView;
NSDraggingSession* session = [cv beginDraggingSessionWithDraggingCell:self
event:theEvent];
// _dragImageOffset = [cv convertPoint:[theEvent locationInWindow] fromView:nil];
// Note that you will get an ugly flash effect when the image returns if this is set to yes
// you can work around it by setting NO and faking the release by animating an NSWindowSubclass with the image as the content
// create the window in the drag ended method for NSDragOperationNone
// there is [probably a better and easier way around this behavior by playing with view animation properties.
session.animatesToStartingPositionsOnCancelOrFail = YES;
}
}
}
return result;
}
#pragma mark - NSDraggingSource Methods
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
{
switch(context) {
case NSDraggingContextOutsideApplication:
return NSDragOperationNone;
break;
case NSDraggingContextWithinApplication:
default:
return NSDragOperationPrivate;
break;
}
}
/*
- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint
{
DragAnimationWindow* dw = [DragAnimationWindow sharedAnimationWindow];
NSControl* cv = (NSControl*)self.controlView;
NSImage* image = [[NSImage alloc] initWithPasteboard:session.draggingPasteboard];
[dw setupDragAnimationWith:cv usingDragImage:image];
[image release];
NSRect frame = [cv frameForCell:self];
frame = [cv convertRect:frame toView:nil];
[dw setFrame:[cv.window convertRectToScreen:frame] display:NO];
}
*/
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
/*
if (operation == NSDragOperationNone) {
DragAnimationWindow* dw = [DragAnimationWindow sharedAnimationWindow];
NSRect frame = dw.frame;
NSPoint start = screenPoint;
start.y += _dragImageOffset.y;
start.x -= _dragImageOffset.x;
[dw setFrameTopLeftPoint:start];
[dw animateToFrame:frame];
}*/
// now tell the control view the drag ended so it can do any cleanup it needs
// this is somewhat hackish
[self.controlView draggingEnded:nil];
}
@end
关于objective-c - 在 OSX 10.8 SDK/objective-c 中拖动按钮,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13554681/