2013-01-14 76 views
1

我遵循一些例子,使NSButton子類作爲NSColorWell(因爲我們的NSButton子類已經給我們我們需要的外觀行爲),但我注意到後使用按鈕調用面板並更改顏色,它也會更改文檔中選定文本的顏色。如果我將NSColorWell與我們的外觀定製區分開來,它會不會有這個問題?NSButton子類作爲colorwell&防止NSColorPanel觸摸第一響應者

但是,我仍然希望能夠避免&仍然讓我們使用我們的按鈕子類。我已經看到討論線程建議讓按鈕本身成爲第一響應者,但是當按鈕處於單獨的調色板中時,我無法使其工作。此外,我不想改變響應者鏈或調色板成爲關鍵窗口。 NSColorPanel上的一個類別如何超過setColor:,使其發佈預期通知但不觸及第一響應者?

(注意,而不是簡單地打開彩色面板,我目前通過DrummerB https://github.com/DrummerB/BFColorPickerPopover利用的BFColorPickerPopover,但我不認爲這是多大的併發症。我之前整合有同樣的NSColorPanel /急救員問題它)。

被要求發佈源代碼,所以這裏從我NSButton子類的相關位(注意,用途,而不是NSColorPanel直接上述選擇器酥料餅):

.H:

@interface ... 
@property (nonatomic, strong) NSColor *color; 
@property (nonatomic, assign) BOOL active; 
@property (nonatomic, strong) NSColor *buttonColor; 
@property (nonatomic, weak) BFColorPickerPopover *popover; 
- (void)activate:(BOOL)exclusive; // param ignored, always exclusive 
- (void)activate; 
- (void)deactivate; 
- (void)takeColorFrom:(id)sender; 
@end 

。米:

@implementation ... 
@dynamic color; 
- (NSColor *)color 
{ 
    return self.buttonColor; 
} 
- (void)setColor:(NSColor *)newColor 
{ 
    self.buttonColor = newColor; 
    [self generateSwatch]; 
    self.needsDisplay = YES; 
    self.popover.color = newColor; 
} 
- (void)activate:(BOOL)exclusive 
{ 
    [self activate]; // always exclusive 
} 
- (void)activate 
{ 
    self.popover = [BFColorPickerPopover sharedPopover]; 
    self.popover.color = self.buttonColor; 
    [self.popover showRelativeToRect:self.frame ofView:self.superview 
          preferredEdge:self.preferredEdgeForPopover]; 
    [[NSNotificationCenter defaultCenter] addObserver:self 
         selector:@selector(popoverDidClose:) 
          name:NSPopoverDidCloseNotification 
          object:self.popover]; 
    [[NSNotificationCenter defaultCenter] addObserver:self 
         selector:@selector(colorDidChange:) 
          name:NSColorPanelColorDidChangeNotification 
          object:self.popover.colorPanel]; 
    activeButton = self; 
    self.active = YES; 
} 
- (void)deactivate 
{ 
    if (self.popover) 
    { 
     [self.popover close]; 
     self.popover = nil; 
    } 
    [[NSNotificationCenter defaultCenter] removeObserver:self 
      name:NSPopoverDidCloseNotification object:self.popover]; 
    [[NSNotificationCenter defaultCenter] removeObserver:self 
         name:NSColorPanelColorDidChangeNotification 
        object:self.popover.colorPanel]; 
    if (activeButton == self) activeButton = nil; 
    self.active = NO; 
} 
- (void)popoverDidClose:(NSNotification *)notification 
{ 
    self.popover = nil; // don't let deactivate ask it to close again 
    [self deactivate]; 
} 
- (void)colorDidChange:(NSNotification *)notification 
{ 
    self.buttonColor = self.popover.colorPanel.color; 
    [self generateSwatch]; 
    self.needsDisplay = YES; 
    [self sendAction:self.action to:self.target]; 
} 
- (void)mouseDown:(NSEvent *)theEvent 
{ 
    if (self.isEnabled && !self.active) 
     [self activate]; 
    else if (self.active) 
     [self deactivate]; 
} 
- (void)takeColorFrom:(id)sender 
{ 
    if ([sender respondsToSelector:@selector(color)]) 
     self.color = [sender color]; 
} 
@end 

附錄:

我嘗試使用正常的NSColorWell來代替我的NSButton子類和相同的問題。除了調用操作方法外,面板中選擇的顏色還會調用第一響應者的changeColor:。因此,在我的問題中忘記了關於NSButton的一切,一般來說,如何使用NSColorWell,其顏色不得也被推送到第一響應者身上?必須要求定製預期的第一響應者,以選擇性地忽略changeColor:,或者使第一響應者真的是該做的事情或其他事情?

+0

發佈您的代碼。 –

回答

2

是的,更好的網絡搜索詞(NSColorWell「第一反應者」),我看別人都在努力與NSColorWell和長一段時間同樣的問題。許多老的郵件列表線程都涵蓋這一點,看到3級的解決方案:

  1. http://www.cocoabuilder.com/archive/cocoa/82832-nscolorwell-changecolor-and-first-responder.html道格拉斯·戴維森提出繼承的潛在第一響應者(S),使他們忽略changeColor:(可能我會做什麼)

  2. http://www.cocoabuilder.com/archive/cocoa/3263-your-nscolorwell-got-in-my-nstext.html Guy English建議暫時將顏色做成第一響應者(我嘗試過,但由於顏色不錯,因此我不想成爲關鍵的面板)

  3. http://www.cocoabuilder.com/archive/cocoa/180323-detecting-currently-active-nscolorwell.html Martin建議切割防止0首先電話冒充NSColorPanel並重寫一個私有方法(最接近我想要的東西,但應用商店拒絕更多的風險比我舒服)

更新:我試過# 1但事實證明,我無法覆蓋第一響應者(WebView/WebHTMLView grrr)。與3號去,我把一個NSColorPanel類別下面,讓我的顏色按鈕設置panel.avoidsChangingFirstResponder=YES,它似乎工作:

static char changeColorPatchAssociatedObjectKey; // address of this is used as a unique runtime value 

- (BOOL)avoidsChangingFirstResponder 
{ 
    NSNumber *changeColorPatchFlag = (NSNumber *)objc_getAssociatedObject(self, &changeColorPatchAssociatedObjectKey); 
    return changeColorPatchFlag && changeColorPatchFlag.boolValue; 
} 

- (void)setAvoidsChangingFirstResponder:(BOOL)enablePatch 
{ 
    NSNumber *changeColorPatchFlag = (NSNumber *)objc_getAssociatedObject(self, &changeColorPatchAssociatedObjectKey); 
    if ((!changeColorPatchFlag && enablePatch) || (changeColorPatchFlag && changeColorPatchFlag.boolValue != enablePatch)) 
     objc_setAssociatedObject(self, &changeColorPatchAssociatedObjectKey, @(enablePatch), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 

+ (void)load 
{ 
    if (self == [NSColorPanel class]) 
    { 
     // patch implementation of _forceSendAction:notification:firstResponder: (use swizzle technique from MAKVONotificationCenter.m) 
     // for one that calls original but with the last BOOL parameter conditionally changed to NO 
     SEL methodSel = NSSelectorFromString(@"_forceSendAction:notification:firstResponder:"); 
     Method method = class_getInstanceMethod(self, methodSel); 
     IMP origImpl = method_getImplementation(method); 
     IMP newImpl = imp_implementationWithBlock(^(void *obj, SEL s, BOOL isAct, BOOL isNotif, BOOL isFirstResp) { 
      NSNumber *changeColorPatchFlag = (NSNumber *)objc_getAssociatedObject((__bridge id)(obj), &changeColorPatchAssociatedObjectKey); 
      if (changeColorPatchFlag && changeColorPatchFlag.boolValue) 
       isFirstResp = NO; 
      ((void (*)(void *, SEL, BOOL, BOOL, BOOL))origImpl)(obj, s, isAct, isNotif, isFirstResp); 
     }); 
     class_replaceMethod(self, methodSel, newImpl, method_getTypeEncoding(method)); 
    } 
} 

我希望別人認爲這很有用。

+0

有抱負的應用程序開發人員在這裏......你的代碼調用任何私有方法嗎? –

+1

如前所述,它覆蓋了未記錄的'NSColorPanel'方法'_forceSendAction:notification:firstResponder:'。我這樣做的應用程序最終甚至從未試圖進入Mac App Store,所以這對我們來說不是問題。 –

+0

啊,謝謝。我將與子類解決方案一起去。 –

相關問題