Проблема с отслеживанием события мыши при прокрутке в NSTableCellView

У меня есть поповерные кнопки в пользовательском представлении ячейки таблицы, и когда курсор мыши перемещается над одной ячейкой, эти кнопки ячейки будут отображаться, и только эта ячейка должна отображать кнопки. Если я медленно перемещаю курсор мыши, все работает правильно, но когда я прокручиваю табличное представление с помощью средней мыши, быстрее появляется слишком много ячеек с попсовыми кнопками, чего на самом деле следует избегать. Каким-то образом событие мыши не отслеживается правильно при прокрутке. Я получил этот код отслеживания из библиотеки примеров Apple. Не могли бы вы дать предложение по этому вопросу?

#import "BasisCellView.h"

@implementation BasisCellView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
    // Drawing code here.
    [[NSImage imageNamed:@"background"] drawInRect:dirtyRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:0.1];
}

- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle {
    [super setBackgroundStyle: NSBackgroundStyleLight];
}

- (void)setMouseInside:(BOOL)value {
    if (mouseInside != value) {
        mouseInside = value;
        [self.deleteButton setHidden:!value];
        [self.bookmarkButton setHidden:!value];
        [self setNeedsDisplay:YES];
        NSLog(@"redrawn");
    }
}

- (BOOL)mouseInside {
    return mouseInside;
}

- (void)ensureTrackingArea {
    if (trackingArea == nil) {
        trackingArea = [[NSTrackingArea alloc] initWithRect:NSZeroRect options:NSTrackingInVisibleRect | NSTrackingActiveAlways | NSTrackingMouseEnteredAndExited owner:self userInfo:nil];
    }
}

- (void)updateTrackingAreas {
    [super updateTrackingAreas];
    [self ensureTrackingArea];
    if (![[self trackingAreas] containsObject:trackingArea]) {
        [self addTrackingArea:trackingArea];
    }
}

- (void)mouseEntered:(NSEvent *)theEvent {
    NSLog(@"1");
    self.mouseInside = YES;
}

- (void)mouseExited:(NSEvent *)theEvent {
    NSLog(@"0");
    self.mouseInside = NO;
}

@end

А вот распечатанный журнал:

2015-02-05 08:59:33.267 Clever[1286:25969] 1
2015-02-05 08:59:33.267 Clever[1286:25969] redrawn
2015-02-05 08:59:33.299 Clever[1286:25969] 0
2015-02-05 08:59:33.299 Clever[1286:25969] redrawn
2015-02-05 08:59:33.333 Clever[1286:25969] 1
2015-02-05 08:59:33.333 Clever[1286:25969] redrawn
2015-02-05 08:59:33.350 Clever[1286:25969] 0
2015-02-05 08:59:33.350 Clever[1286:25969] redrawn
2015-02-05 08:59:33.382 Clever[1286:25969] 1
2015-02-05 08:59:33.383 Clever[1286:25969] redrawn
2015-02-05 08:59:33.669 Clever[1286:25969] 1
2015-02-05 08:59:33.669 Clever[1286:25969] redrawn
2015-02-05 08:59:33.736 Clever[1286:25969] 1
2015-02-05 08:59:33.736 Clever[1286:25969] redrawn
2015-02-05 08:59:33.769 Clever[1286:25969] 0
2015-02-05 08:59:33.769 Clever[1286:25969] redrawn
2015-02-05 08:59:33.769 Clever[1286:25969] 1
2015-02-05 08:59:33.770 Clever[1286:25969] redrawn
2015-02-05 08:59:34.101 Clever[1286:25969] 1
2015-02-05 08:59:34.101 Clever[1286:25969] redrawn
2015-02-05 08:59:34.102 Clever[1286:25969] 0
2015-02-05 08:59:34.102 Clever[1286:25969] redrawn
2015-02-05 08:59:34.136 Clever[1286:25969] 0
2015-02-05 08:59:34.136 Clever[1286:25969] redrawn
2015-02-05 08:59:34.150 Clever[1286:25969] 1
2015-02-05 08:59:34.150 Clever[1286:25969] redrawn
2015-02-05 08:59:34.187 Clever[1286:25969] 1
2015-02-05 08:59:34.187 Clever[1286:25969] redrawn
2015-02-05 08:59:34.235 Clever[1286:25969] 1
2015-02-05 08:59:34.272 Clever[1286:25969] 0

3 ответа

Вот быстрая версия кода, необходимого для настройки области отслеживания:

class MyCustomTableCellView: NSTableCellView {

    func setUpTrackingArea()
    {
        let trackingArea = NSTrackingArea(rect: self.frame, options: [NSTrackingAreaOptions.MouseEnteredAndExited, NSTrackingAreaOptions.ActiveAlways], owner: self, userInfo: nil)
        self.addTrackingArea(trackingArea)
    }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)

        setUpTrackingArea()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)

        setUpTrackingArea()
    }

    override func mouseEntered(theEvent: NSEvent) {
        Swift.print("mouse Entered")
    }

    override func mouseExited(theEvent: NSEvent) {
        Swift.print("mouse exited")
    }
}

Reminders.app использует NSTrackingArea для cellView +, контроллер наблюдает за NSScrollViewWillStartLiveScrollNotification и просматривает видимые ячейки, чтобы скрыть кнопку.

Если вам не нужны обновления в реальном времени, и вы можете скрыть представления / де-подсветку, немедленно используйте NSScrollViewWillStartLiveScrollNotification

Для живых обновлений:

- (void)touchesMovedWithEvent:(NSEvent *)event;
[self setAcceptsTouchEvents:YES];

Все остальное настраивается с помощью нескольких решений: например, использование NSScrollViewWillStartLiveScrollNotification + NSScrollViewDidEndLiveScrollNotification в вашем контроллере или переопределение метода scrollWheel и запуск событий мыши, как вам нужно:

CustomScrollView - это тот, который отправляет mouseEvents в CustomTableRowView, а CustomTableRowView пересылает его в свои подпредставления.

#import <Cocoa/Cocoa.h>

@interface CustomScrollView : NSScrollView

@end

#import "CustomScrollView.h"

@implementation CustomScrollView

- (void)scrollWheel:(NSEvent *)theEvent
{
    NSPoint mouseLocation;
    NSInteger rowBefore = -1, rowAfter = -1;
    mouseLocation = [[self documentView] convertPoint:[theEvent locationInWindow] fromView:nil];
    rowBefore = [(NSTableView *)[self documentView] rowAtPoint:mouseLocation];

    @autoreleasepool {
        while ((theEvent = [[self window] nextEventMatchingMask:(NSScrollWheelMask)
                                                      untilDate:[NSDate distantFuture]
                                                         inMode:NSEventTrackingRunLoopMode
                                                        dequeue:YES]) &&
               !(([theEvent phase] & NSEventPhaseCancelled) || ([theEvent phase] & NSEventPhaseEnded))) {
            [super scrollWheel:theEvent];
        }
    }
    [super scrollWheel:theEvent];

    mouseLocation = [[self documentView] convertPoint:[theEvent locationInWindow] fromView:nil];
    rowAfter = [(NSTableView *)[self documentView] rowAtPoint:mouseLocation];
        if (rowBefore != -1) {
            NSTableRowView *rowViewBefore = [(NSTableView *)[self documentView] rowViewAtRow:rowBefore makeIfNecessary:NO];
            [rowViewBefore mouseExited:[NSApp currentEvent]];
        }
        if (rowAfter != -1) {
            NSTableRowView *rowViewAfter = [(NSTableView *)[self documentView] rowViewAtRow:rowAfter makeIfNecessary:NO];
            [rowViewAfter mouseEntered:[NSApp currentEvent]];
        }
}

@end

CustomTableRowView:

- (void)mouseEntered:(NSEvent *)event
{
    if (_inMouseEntered == NO) {
        _inMouseEntered = YES;
        [self setHighlighted:YES];
        for (NSView *view in [self subviews]) {
            if ([view isKindOfClass:[NSTableCellView class]]) {
                [view mouseEntered:event];
            }
        }
        [self setNeedsDisplay:YES];
        _inMouseEntered = NO;
    }
}

- (void)mouseExited:(NSEvent*)event
{
    if (_inMouseExited == NO) {
        _inMouseExited = YES;
        [self setHighlighted:NO];
        for (NSView *view in [self subviews]) {
            if ([view isKindOfClass:[NSTableCellView class]]) {
                [(NSTableCellView *)view mouseExited:event];
            }
        }
        [self setNeedsDisplay:YES];
        _inMouseExited = NO;
    }
}

Не забудьте NSTrackingArea, чтобы получить оригинальные mouseEvents

Этот код делает именно то, что вы хотите в приложении Apple для напоминаний. Если внимательно посмотреть в приложении "Напоминания", они задерживают, прежде чем сделать кнопку видимой. Я добавляю много строк в таблицу и тестирую при прокрутке.

  #import "OTratingListTableCellView.h"

    @implementation OTratingListTableCellView

    @synthesize boatNameTextField,boatRatingTextField,boatStartTimeTextField,boatFinishTimeTextField,classTextField,popUpButton;

    - (void)drawRect:(NSRect)dirtyRect {
        [super drawRect:dirtyRect];


        NSTrackingArea *trackingArea = [[NSTrackingArea alloc] initWithRect:self.frame
                                                                    options: (NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow )
                                                                      owner:self userInfo:nil];
        [self addTrackingArea:trackingArea];
        // Drawing code here.
    }

    - (void)mouseEntered:(NSEvent *)theEvent
    {
        NSLog(@"mouseEntered");
        popUpButton.hidden=false;

    }
    - (void)mouseExited:(NSEvent *)theEvent
    {
        popUpButton.hidden=true;

        NSLog(@"mouseExited");  
    }

    @end
Другие вопросы по тегам