19

我有一个习俗NSView(这是众多习俗中的一个,它们都生活在一个NSCollectionView——我认为这无关紧要,但谁知道呢)。当我单击视图时,我希望它更改其选择状态(并相应地重绘自身);当我双击视图时,我希望它为刚刚双击的对象弹出一个更大的预览窗口。

我的第一个看起来像这样:

- (void)mouseUp: (NSEvent *)theEvent {
    if ([theEvent clickCount] == 1) [model setIsSelected: ![model isSelected]];
    else if ([theEvent clickCount] == 2) if ([model hasBeenDownloaded]) [mainWindowController showPreviewWindowForPicture:model];
}

大部分工作正常。除了,当我双击视图时,选择状态发生变化弹出窗口。这不正是我想要的。

好像我有两个选择。我可以在响应双击时恢复选择状态(撤消错误的单击),或者我可以找到某种NSTimer解决方案以在响应单击之前建立延迟。换句话说,我可以确保在更改选择状态之前不会再次单击。

这看起来更优雅,所以这是我一开始采用的方法。我从 Google 中找到的唯一真正的指导是在一个名称中带有连字符的未命名网站上。这种方法主要适用于一个很大的警告。

悬而未决的问题是“我应该NSTimer等多久?”。未命名的站点建议使用 Carbon 函数GetDblTime()。除了在 64 位应用程序中无法使用之外,我能找到的唯一文档说它正在返回时钟滴答声。而且我不知道如何将这些转换为秒数NSTimer

那么这里的“正确”答案是什么?摸索GetDblTime()?双击“撤消”选择?我无法弄清楚可可惯用的方法。

4

5 回答 5

42

延迟选择状态的改变是(据我所见)推荐的这样做方式。

实现起来非常简单:

- (void)mouseUp:(NSEvent *)theEvent
{
    if([theEvent clickCount] == 1) {
        [model performSelector:@selector(toggleSelectedState) afterDelay:[NSEvent doubleClickInterval]];
    }
    else if([theEvent clickCount] == 2)
    {
        if([model hasBeenDownloaded])
        {
                [NSRunLoop cancelPreviousPerformRequestsWithTarget: model]; 
                [mainWindowController showPreviewWindowForPicture:model];
        }
    }
}

(请注意,在 10.6 中,双击间隔可作为类方法访问NSEvent

于 2009-11-03T05:24:18.703 回答
8

如果你的单击和双击操作真的是分开的和不相关的,你需要在第一次单击时使用一个计时器,然后等待查看是否会发生双击。在任何平台上都是如此。

但这会在用户通常不喜欢的单击操作中引入尴尬的延迟。所以你看不到这种方法经常被使用。

更好的方法是让您的单击和双击操作相关且互补。例如,如果您在 Finder 中单击一个图标,它会被选中(立即),如果您双击一个图标,它会被选中并(立即)打开。这是你应该瞄准的行为。

换句话说,单击的后果应该与您的双击命令有关。这样,您可以在双击处理程序中处理单击的影响,而不必求助于使用计时器。

于 2009-11-03T17:40:23.130 回答
2

就个人而言,我认为您需要问自己为什么要这种非标准行为。

您能否指出任何其他将双击中的第一次单击视为与单击不同的应用程序?我想不出任何...

于 2009-11-03T05:26:15.550 回答
2

将两个属性添加到您的自定义视图。

// CustomView.h
@interface CustomView : NSView {
  @protected
    id      m_target;
    SEL     m_doubleAction;
}
@property (readwrite) id target;
@property (readwrite) SEL doubleAction;

@end

覆盖mouseUp:自定义视图中的方法。

// CustomView.m
#pragma mark - MouseEvents

- (void)mouseUp:(NSEvent*)event {
    if (event.clickCount == 2) {
        if (m_target && m_doubleAction && [m_target respondsToSelector:m_doubleAction]) {
            [m_target performSelector:m_doubleAction];
        }
    }
}

将您的控制器注册targetdoubleAction.

// CustomController.m
- (id)init {
    self = [super init];
    if (self) {
        // Register self for double click events.
        [(CustomView*)m_myView setTarget:self];
        [(CustomView*)m_myView setDoubleAction:@selector(doubleClicked:)];
    }
    return self;
}

实现双击发生时应该做的事情。

// CustomController.m
- (void)doubleClicked:(id)sender {
  // DO SOMETHING.
}
于 2011-12-19T17:12:48.640 回答
1

@Dave DeLong 在 Swift 4.2 (Xcode 10, macOS 10.13) 中的解决方案,修改为与 event.location(in: view) 一起使用

var singleClickPoint: CGPoint?

override func mouseDown(with event: NSEvent) {
singleClickPoint = event.location(in: self)
perform(#selector(GameScene.singleClickAction), with: nil, afterDelay: NSEvent.doubleClickInterval)
 if event.clickCount == 2 {
    RunLoop.cancelPreviousPerformRequests(withTarget: self)
    singleClickPoint = nil
//do whatever you want on double-click
}
}

@objc func singleClickAction(){
guard let singleClickPoint = singleClickPoint else {return}
//do whatever you want on single-click
}

我不使用 singleClickAction(at point: CGPoint) 并使用: event.location(in: self) 调用它的原因是我传入的任何点 - 包括 CGPoint.zero - 最终到达 singleClick Action 为 (0.0 , 9.223372036854776e+18)。我将为此提交一个雷达,但就目前而言,绕过执行是要走的路。(其他对象似乎工作得很好,但 CGPoints 不行。)

于 2019-10-03T12:37:36.367 回答