在NSTableCellView中滚动时跟踪鼠标事件的问题

时间:2015-02-05 08:17:00

标签: objective-c cocoa nsevent nstablecellview nstrackingarea

我在自定义表格单元格视图上有弹出按钮,当鼠标光标移动到一个单元格上时,将显示单元格的这些按钮,并且只有这一个单元格应显示按钮。如果我慢慢地移动鼠标光标,一切都正常工作,但当我用中间鼠标rad滚动表视图时,有太多的单元格显示弹出按钮,真正应该避免的。不知何故,滚动时没有正确跟踪鼠标事件。我从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 个答案:

答案 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")
    }
}

答案 1 :(得分:0)

此代码在Apple的Reminders App中完全符合您的要求。如果仔细查看提醒应用程序,他们会在按钮可见之前放一些延迟。我向表中添加了许多行并在滚动时进行测试。

  #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

答案 2 :(得分:0)

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