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

Ok。私が見ているアニメーションは、コントロール、スーパービュー、またはコントロールのウィンドウではないことがわかりました。animatesToStartingPositionsOnCancelOrFailウィンドウを作成し ( QuartzDebugで観察)、その中にドラッグ画像を配置するように見えます。呼び出しが実行される(つまり、ドラッグ操作が完了する前)にNSDraggingSession原点に戻り、フェードアウトするのはこのウィンドウです。setHidden:

残念ながら、それが作成するウィンドウは ではないため、NSWindowカテゴリを作成してNSWindowもフェード アニメーションは無効になりません。

第 2 に、ウィンドウのハンドルを取得するための一般的な方法を私が知っているわけではないため、ウィンドウ インスタンスを直接操作することはできません。

とにかく、AppKitがあなたのために行うことからそれほど遠くないので、おそらく私の回避策がこれを行うための最良の方法であるように見えます.

このウィンドウのハンドルを取得する方法、またはそれがどのクラスであるかを誰かが知っている場合は、知りたいと思います。

于 2013-11-12T00:08:45.403 に答える
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 に答える