【问题标题】:Issue with tracking on mouse event while scrolling in NSTableCellView在 NSTableCellView 中滚动时跟踪鼠标事件的问题
【发布时间】:2015-04-05 00:35:19
【问题描述】:

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

【问题讨论】:

  • 我可以建议您解决此问题。当鼠标进入一个单元格视图时,你可以让其他视图鼠标在NO内。
  • 感谢您的建议,现在您的解决方案已解决,但它是唯一的解决方案吗?我考虑性能,因为我需要嵌套循环从表格视图( TableView -> RowView -> CellView )获取单元格视图。
  • 否则您可以跟踪表格视图的矩形,并使用 mouseMoved 事件将值分配给 mouseInside 以进行单元格视图。

标签: objective-c cocoa nsevent nstablecellview nstrackingarea


【解决方案1】:

Reminders.app 为 cellView 使用 NSTrackingArea + 控制器观察 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

自定义表格行视图:

- (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

【讨论】:

  • 存在未记录的 NSTrackingRollover (NSTrackingArea),值为 0x800,但我无法用它触发 mouseExit 事件。
【解决方案2】:

这是设置跟踪区域所需的快速版本代码:

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

【讨论】:

  • 如果视图被滚动,这将不起作用 - 你永远不会得到鼠标退出事件
【解决方案3】:

此代码完全符合您在 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

【讨论】:

  • 欢迎来到 SO。请解释您的答案,而不是仅发布一些代码。
  • 您的代码示例中存在性能错误,您可以通过drawRect: 函数(经常调用该函数)一遍又一遍地添加跟踪区域。最好只创建一次 trackingArea(例如在 init 中)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-06
  • 2014-08-29
  • 2013-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多