为NSView`setHidden:`禁用NSView淡入淡出动画

时间:2013-11-08 21:58:14

标签: objective-c cocoa core-animation nsview

我正在开发一个具有可拖动控件概念的项目,除了在调用setHidden:时NSView似乎采用淡入/淡出动画外,一切正常。

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

我试过了:

  1. NSAnimationContext来电
  2. 周围使用setHidden:分组,持续时间为0
  3. 使用控件和superview上的各种键(alpha,hidden,isHidden)设置视图动画字典
  4. 覆盖控件及其超级视图的animationForKey:
  5. 我没有使用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
    

3 个答案:

答案 0 :(得分:1)

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

@interface NoAnimationImageView : NSImageView

@end

@implementation NoAnimationImageView

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

@end

答案 1 :(得分: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];

答案 2 :(得分:0)

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

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

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

看起来我的解决方法可能是最好的方法,毕竟它与AppKit为你所做的一切都不远。

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