我想创建一个NSButton
发送被点击时它的动作,但是当它被按下1两秒它示出了NSMenu。与此问题here完全相同,但由于该答案无法解决我的问题,所以我决定再次提问。NSButton迟发NSMenu - 目标C /可可
举个例子,去查找,打开一个新的窗口,通过一些浏览文件夹,然后单击后退按钮:你去到一个文件夹。现在点击并按住后退按钮:显示一个菜单。我不知道如何用NSPopUpButton
来做到这一点。
我想创建一个NSButton
发送被点击时它的动作,但是当它被按下1两秒它示出了NSMenu。与此问题here完全相同,但由于该答案无法解决我的问题,所以我决定再次提问。NSButton迟发NSMenu - 目标C /可可
举个例子,去查找,打开一个新的窗口,通过一些浏览文件夹,然后单击后退按钮:你去到一个文件夹。现在点击并按住后退按钮:显示一个菜单。我不知道如何用NSPopUpButton
来做到这一点。
使用NSSegmentedControl
。
通过发送setMenu:forSegment:
的控制(任何连接到menu
出口在IB不会做的伎俩)添加菜单。有一个行动连接到控制(这很重要)。
应完全按照您所描述的方式工作。
创建NSPopUpButton
的子类并覆盖mouseDown
/mouseUp
事件。
在调用super
的实现并且只有在鼠标仍在按住之前,才有mouseDown
事件延迟片刻。
有mouseUp
事件触发按钮的target
/action
前将selectedMenuItem
到nil
(因此selectedMenuItemIndex
会-1
)。
唯一的另一个问题是处理快速点击,其中对于点击定时器可能在瞬间火的时候,按下鼠标时对一些未来的点击。我没有使用NSTimer
并使其无效,而是选择了一个简单的mouseDown
事件计数器,并在计数器发生变化时进行救助。
下面是我用我的子类代码:
// MyClickAndHoldPopUpButton.h
@interface MyClickAndHoldPopUpButton : NSPopUpButton
@end
// MyClickAndHoldPopUpButton.m
@interface MyClickAndHoldPopUpButton()
@property BOOL mouseIsDown;
@property BOOL menuWasShownForLastMouseDown;
@property int mouseDownUniquenessCounter;
@end
@implementation MyClickAndHoldPopUpButton
// highlight the button immediately but wait a moment before calling the super method (which will show our popup menu) if the mouse comes up
// in that moment, don't tell the super method about the mousedown at all.
- (void)mouseDown:(NSEvent *)theEvent
{
self.mouseIsDown = YES;
self.menuWasShownForLastMouseDown = NO;
self.mouseDownUniquenessCounter++;
int mouseDownUniquenessCounterCopy = self.mouseDownUniquenessCounter;
[self highlight:YES];
float delayInSeconds = [NSEvent doubleClickInterval];
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if (self.mouseIsDown && mouseDownUniquenessCounterCopy == self.mouseDownUniquenessCounter) {
self.menuWasShownForLastMouseDown = YES;
[super mouseDown:theEvent];
}
});
}
// if the mouse was down for a short enough period to avoid showing a popup menu, fire our target/action with no selected menu item, then
// remove the button highlight.
- (void)mouseUp:(NSEvent *)theEvent
{
self.mouseIsDown = NO;
if (!self.menuWasShownForLastMouseDown) {
[self selectItem:nil];
[self sendAction:self.action to:self.target];
}
[self highlight:NO];
}
@end
美丽!这正是我所期待的。太糟糕了,在App Kit中没有这样的标准控件(这很奇怪,因为Apple在其自己的应用程序中使用了这种UI惯例)。 – aapierce 2014-03-12 19:24:42
对于'delayInSeconds'考虑使用'NSEvent。doubleClickInterval'而不是常量'0.2'。这将根据用户的鼠标处理偏好来调整延迟。对于双击时间短的用户,延迟时间更短,延迟时间更短,对于双击时间更长的用户,延迟时间更短,延迟时间更短。 – 2015-07-13 10:20:37
谢谢@GrahamMiln,我已经更新了我的答案。 – 2015-07-13 11:24:15
如果有人仍然需要这一点,这里是基于一个普通NSButton,不分段控制我的解决方案。
子类NSButton和实施开始当前运行环内的计时器自定义mouseDown
。在mouseUp
中,检查定时器是否未被触发。在这种情况下,取消它并执行默认操作。
这是一个非常简单的方法,它适用于任何NSButton您可以在IB使用。下面
代码:
- (void)mouseDown:(NSEvent *)theEvent {
[self setHighlighted:YES];
[self setNeedsDisplay:YES];
_menuShown = NO;
_timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(showContextMenu:) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSDefaultRunLoopMode];
}
- (void)mouseUp:(NSEvent *)theEvent {
[self setHighlighted:NO];
[self setNeedsDisplay:YES];
[_timer invalidate];
_timer = nil;
if(!_menuShown) {
[NSApp sendAction:[self action] to:[self target] from:self];
}
_menuShown = NO;
}
- (void)showContextMenu:(NSTimer*)timer {
if(!_timer) {
return;
}
_timer = nil;
_menuShown = YES;
NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@"Contextual Menu"];
[[theMenu addItemWithTitle:@"Beep" action:@selector(beep:) keyEquivalent:@""] setTarget:self];
[[theMenu addItemWithTitle:@"Honk" action:@selector(honk:) keyEquivalent:@""] setTarget:self];
[theMenu popUpMenuPositioningItem:nil atLocation:NSMakePoint(self.bounds.size.width-8, self.bounds.size.height-1) inView:self];
NSWindow* window = [self window];
NSEvent* fakeMouseUp = [NSEvent mouseEventWithType:NSLeftMouseUp
location:self.bounds.origin
modifierFlags:0
timestamp:[NSDate timeIntervalSinceReferenceDate]
windowNumber:[window windowNumber]
context:[NSGraphicsContext currentContext]
eventNumber:0
clickCount:1
pressure:0.0];
[window postEvent:fakeMouseUp atStart:YES];
[self setState:NSOnState];
}
我已经贴在我的GitHub上一个working sample。
太糟糕了,您无法为NSSegmentedControl设置自定义高度 - 我需要将该菜单附加到大按钮上。 – zrxq 2012-03-20 04:47:45
工作完美!谢谢! – Alex 2012-03-28 10:01:04
不错的诀窍,在IB玩一玩就可以得到像这样的优雅控制。 – 2012-05-08 15:16:45