首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >NSButton子类为着色器&防止NSColorPanel接触第一个响应程序

NSButton子类为着色器&防止NSColorPanel接触第一个响应程序
EN

Stack Overflow用户
提问于 2013-01-14 23:00:16
回答 1查看 1.1K关注 0票数 1

我遵循了一些使NSButton子类作为NSColorWell工作的示例(因为我们的NSButton子类已经给出了我们需要的外观行为),但是我注意到,在使用按钮调用面板和更改颜色之后,它也改变了文档中选定文本的颜色。如果我用外观自定义来子类NSColorWell,它不会有这个问题吗?

但是,我仍然希望有一个避免这种情况的解决方案&仍然允许我们使用我们的按钮子类。我已经看到讨论线程建议让按钮本身成为第一个响应者,然而,由于按钮位于一个单独的调色板中,我很难让它工作。另外,我不希望改变响应链或使调色板成为关键窗口。NSColorPanel上的一个类别会有多邪恶?它凌驾于setColor:之上,它发布了预期的通知,但没有触及第一个响应者?

(请注意,与其简单地打开彩色面板,我目前正在使用BFColorPickerPopover,由DrummerB https://github.com/DrummerB/BFColorPickerPopover提供。不过,我认为这并不是什么复杂的事情。在集成之前,我遇到了同样的NSColorPanel /第一个响应者问题)。

被要求发布源代码,下面是我的NSButton子类中的相关部分(注意,使用上面提到的选择器popover而不是直接使用NSColorPanel ):

.h:

代码语言:javascript
复制
@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

.m:

代码语言:javascript
复制
@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:,还是让NSColorWell成为第一个响应者才是真正要做的事情,还是其他的事情?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-01-17 18:15:16

是的,一个更好的网络搜索术语(NSColorWell“第一个响应者”)和我看到其他人一直在与NSColorWell和这个同样的问题斗争了很长时间。许多旧的邮件列表线程已经讨论了这一点,并看到了3种解决方案:

  1. http://www.cocoabuilder.com/archive/cocoa/82832-nscolorwell-changecolor-and-first-responder.html Davidson建议将潜在的第一个应答者细分为子类,这样他们就忽略了changeColor: (可能我会这么做)。
  2. http://www.cocoabuilder.com/archive/cocoa/3263-your-nscolorwell-got-in-my-nstext.html Guy建议将颜色暂时作为第一个响应者(我尝试过,但由于颜色良好而出现了问题,我不想成为关键)
  3. http://www.cocoabuilder.com/archive/cocoa/180323-detecting-currently-active-nscolorwell.html Martin建议从一开始就通过伪装成NSColorPanel和重写一种私人方法来防止changeColor:调用(这与我想要的最接近,但应用程序商店被拒绝的风险要比我对此感到舒服)大。

UPDATE:我尝试了#1,但事实证明我无法覆盖第一个响应程序(WebView / WebHTMLView grrr)。使用#3,我将以下内容放入一个NSColorPanel类别,并将我的彩色按钮设置为panel.avoidsChangingFirstResponder=YES,这似乎是可行的:

代码语言:javascript
复制
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));
    }
}

我希望其他人发现这是有用的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14328386

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档