gpt4 book ai didi

objective-c - NSButton 子类为 colorwell 并防止 NSColorPanel 接触第一响应者

转载 作者:行者123 更新时间:2023-12-03 17:04:23 30 4
gpt4 key购买 nike

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

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

(注意,我目前正在使用 DrummerB https://github.com/DrummerB/BFColorPickerPopoverBFColorPickerPopover ,而不是简单地打开颜色面板。但是我认为这并不复杂。我有集成之前相同的 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

.m:

@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 作为第一响应者真正要做的事情,还是其他什么?

最佳答案

是的,一个更好的网络搜索术语(NSColorWell“第一响应者”),我看到其他人长期以来一直在努力解决 NSColorWell 和同样的问题。许多旧的邮件列表线程已经涵盖了这个问题并看到了 3 个解决方案:

  1. http://www.cocoabuilder.com/archive/cocoa/82832-nscolorwell-changecolor-and-first-responder.html Douglas Davidson 建议对潜在的第一响应者进行子类化,以便他们忽略 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 建议首先通过冒充 NSColorPanel 并覆盖私有(private)方法来阻止 changeColor: 调用(最接近我想要的,但应用商店拒绝的风险比我很满意)

更新:我尝试了#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));
}
}

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

关于objective-c - NSButton 子类为 colorwell 并防止 NSColorPanel 接触第一响应者,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14328386/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com