2013-11-08 7 views
0

Я работаю над проектом, который имеет концепцию перетаскиваемых элементов управления, все работает отлично, за исключением того, что NSView, похоже, использует анимацию fade in/out при вызове setHidden:.Отключение анимации затухания NSView для NSView `setHidden:`

Я смог обойти проблему, изменив линию session.animatesToStartingPositionsOnCancelOrFail = YES; на НЕТ и реализуя изображение snapback самостоятельно с помощью специального анимированного подкласса NSWindow. он выглядит великолепно, но я знаю, что должен быть более простой способ.

Я попытался:

  1. использование NSAnimationContext группировки с длительностью 0 вокруг setHidden: вызовов
  2. настройки вида анимации словаря с использованием различных ключей (альфа, скрытый, IsHidden) по контролю и SuperView
  3. наиважнейшая animationForKey: как для контроля и его надтаблицы

Я не использую CALayers и есть е ven попытался явно установить 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 

ответ

0

Ok. Я понял, что анимация, которую я вижу, это не элемент управления, супервизор или окно управления. Похоже, что animatesToStartingPositionsOnCancelOrFail вызывает NSDraggingSession, чтобы создать окно (наблюдаемое с QuartzDebug) и поместить в него изображение перетаскивания, и это окно возвращается в исходное состояние и исчезает до выполняется вызов setHidden: (т. Е. До операции перетаскивания).

К сожалению, окно, которое он создает это неNSWindow поэтому создание категории на NSWindow не отключает выцветание анимации.

Во-вторых, нет открытого способа, которым я знаю, чтобы получить дескриптор окна, поэтому я не могу попытаться напрямую манипулировать экземпляром окна.

Похоже, что мой способ обхода - это лучший способ сделать это, в конце концов, недалеко от того, что AppKit делает для вас в любом случае.

Если кто-нибудь знает, как получить ручку в этом окне или какой класс мне будет интересно узнать.

1

Должен быть разрешен слой где-то в вашей иерархии представлений, иначе анимация затухания не будет. Вот мой способ отключения таких анимаций:

@interface NoAnimationImageView : NSImageView 

@end 

@implementation NoAnimationImageView 

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

@end 
+0

Спасибо, но это не сработает. Я уже пробовал переопределять как 'animationForKey:', так и 'defaultAnimationForKey:' в категории NSButton, а также категорию для 'NSView'. Вы тестировали это с помощью моего образца кода? –

+0

Невозможно переопределить метод в категории, вам потребуется подкласс. –

+0

@mahaltertin можно переопределить в категории. Я сделал это (я попробовал подкласс в любом случае, и это было не лучше). –

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]; 
Смежные вопросы