0

我正在开发一个具有可拖动控件概念的项目,一切正常,除了 NSView 在调用setHidden:.

我已经能够通过将行更改session.animatesToStartingPositionsOnCancelOrFail = YES;为 NO 并使用自定义动画NSWindow子类自己实现图像快照来解决该问题。它看起来很棒,但我知道必须有更简单的方法。

我努力了:

  1. 在呼叫周围使用NSAnimationContext持续时间为 0 的分组setHidden:
  2. 使用控件和超级视图上的各种键(alpha、hidden、isHidden)设置视图动画字典
  3. 覆盖animationForKey:控件及其父视图

我没有使用 CALayers,甚至尝试过明确设置wantsLayer:为 NO。

有人知道如何禁用此动画,或者有比我的动画更简单的解决方案NSWindow吗?

这是我精简后的修改代码,只看我在说什么。

@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

- (void)draggingEnded:(id < NSDraggingInfo >)sender
{
    [self setHidden:NO];
}

@end

@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 bit-o-magic executes on either a drag event or immidiately following timer expiration
                // this initiates the control drag event using NSDragging protocols
                NSControl* cv = (NSControl*)self.controlView;
                NSDraggingSession* session = [cv beginDraggingSessionWithDraggingCell:self
                                                                                event:theEvent];
                // 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 endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
{
    // 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
4

3 回答 3

1

在您的视图层次结构中的某处必须启用一个图层,否则不会有淡入淡出动画。这是我禁用此类动画的方法:

@interface NoAnimationImageView : NSImageView

@end

@implementation NoAnimationImageView

+ (id)defaultAnimationForKey: (NSString *)key
{
    return nil;
}

@end
于 2013-11-10T10:21:45.420 回答
0

您已经通过设置视图动画字典尝试的解决方案应该可以工作。但不是针对您提到的键,而是针对以下键。在第一次触发动画之前的某个地方使用它。如果您必须在窗口或视图或两者上进行,我不知道。

NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderIn];
[animations setObject:[NSNull null] forKey: NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];

或者如果它们在那里也只是删除键(可能不是这种情况,因为它们是隐式/默认的):

NSMutableDictionary *animations = [NSMutableDictionary dictionaryWithDictionary:[[theViewOrTheWindow animator] animations];
[animations removeObjectForKey:NSAnimationTriggerOrderIn];
[animations removeObjectForKey:NSAnimationTriggerOrderOut];
[[theViewOrTheWindow animator] setAnimations:animations];
于 2013-11-11T17:52:00.023 回答
0

好的。我发现我看到的动画不是控件、超级视图,也不是控件的窗口。似乎animatesToStartingPositionsOnCancelOrFail导致创建一个窗口(使用 QuartzDebug 观察)并将拖动图像放入其中,正是这个窗口在调用执行之前(即在拖动操作结束之前)NSDraggingSession动画回原点并淡出。setHidden:

不幸的是,它创建的窗口不是这样,NSWindow因此在其上创建类别NSWindow不会禁用淡入淡出动画。

其次,我知道没有公开的方式来获取窗口的句柄,所以我不能尝试直接操作窗口实例。

看起来我的解决方法可能是最好的方法,毕竟它离 AppKit 为你做的事情不远了。

如果有人知道如何处理这个窗口,或者我有兴趣知道它是什么类。

于 2013-11-12T00:08:45.403 回答