我有一个自定义的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()
?双击“撤消”选择吗?我想不出可可惯用的方法。
根据我所见,延迟选择状态的更改是这样做的推荐方法。
实现起来非常简单:
- (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
上的类方法进行访问]
如果您的单击和双击操作确实是分开且无关的,则需要在第一次单击时使用计时器,并等待以查看是否会发生双击。在任何平台上都是如此。
但是这会导致用户通常不喜欢的单击操作中尴尬的延迟。因此,您不会看到这种方法经常使用。
一种更好的方法是使单击和双击操作相互关联和互补。例如,如果您在Finder中单击一个图标,则该图标被选中(立即),如果您双击了一个图标,则该图标被选中并打开(立即)。这就是您应该针对的行为。
换句话说,单击的后果应与双击命令有关。这样,您就可以处理双击处理程序中的单击效果,而不必诉诸使用计时器。
个人,我认为您需要问自己为什么要这种非标准行为。
您能否指向其他将双击中的第一次单击与单击不同的应用程序?我想不到...
向自定义视图添加两个属性。
// 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];
}
}
}
用target
将控制器注册为doubleAction
。
// 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.
}
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
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(在点:CGPoint)并用以下方法调用它的原因是:event.location(in:self)是我传递的任何点-包括CGPoint.zero-最终都以(0.0,9.223372036854776e + 18)。我将为此提起诉讼,但就目前而言,绕过执行是必经之路。 (其他对象似乎可以正常工作,但CGPoints不能。)