我想創建一個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