2012-11-25 48 views
4

拖动一个按钮,我开始使用Objective-C的今天为了开发OSX(山狮)的应用程序。 我有一堆按钮,我想将它们拖入其他对象,例如文本字段。我跟着苹果的开发网站上的教程,但我无法得到拖动部分工作(拖放部分工作,例如,我可以从查找器拖动文件到文本文件并显示其路径)。在OSX 10.8 SDK/Objective-C的

我开始通过创建NSButton子类: @interface mp3OCDDraggableButton : NSButton

,并在描述实现的方法: https://developer.apple.com/library/mac/#samplecode/CocoaDragAndDrop/Introduction/Intro.html

但事行得动!

我把鼠标按下一些日志消息:,我可以看到,但如果我用的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 
+0

请发表您的代码。不知道自己写了什么,几乎不可能知道从哪里开始。 –

+2

你还没有实现'mouseDown:'方法。你已经实现的事件处理器方法是'mouseDown ::',它带有两个参数,你标记为'theEvent'和'event'的变量。没有什么会给你发送这样的信息,所以这个方法永远不会被调用。相反,你会收到一个'mouseDown:'消息(带有一个参数),但是你没有实现响应它的方法。删除“'theEvent:(NSEvent *)'”部分(留下“mouseDown:(NSEvent *)event'”)来解决这个问题。 –

回答

2

可能的问题是,我们在调用-setDataProvider:forTypes:NSPasteboardTypeString但如果通过这种类型的-pasteboard:item:provideDataForType:什么都不做?

3

我的巫术道歉,但我碰到这个问题,跌跌撞撞试图此工具自己和想和大家分享的答案,因为它可能是有用的人。

此解决方案使用NSActionCellNSControl上的类别,因为我需要能够拖动多种控件类型,而不仅仅是按钮。你可以根据你的需求/课程来调整。

我已注释了隐藏/取消隐藏的控件当与一种解决方法为黑客不希望的褪色动画代码。我用隐含的动画和类似的东西摆弄,但无法找出更好的方法。黑客确实很好地工作,但我离开了窗口实现代码。

@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 
+0

我已经在[Vindo]中使用过您的代码(https://github.com/vindo-app/vindo/blob/7f91c0acb6e3def634a27fced2c12e60e64cc9b9/Vindo/StartMenuButtonCell.m#L56-L122)。以为你想知道。 – tbodt

+0

@tbodt很好!当我做的事情对别人有帮助时,我总是很高兴。当它很难找到信息时更好。酷项目BTW。 –