由于我是一个非常懒惰的人,所以我基于Pierre Bernard的版本提出了以下解决方案
#include <objc/runtime.h>
IMP classSwizzleMethod(Class cls, Method method, IMP newImp)
{
auto methodReplacer = class_replaceMethod;
auto methodSetter = method_setImplementation;
IMP originalImpl = methodReplacer(cls, method_getName(method), newImp, method_getTypeEncoding(method));
if (originalImpl == nil)
originalImpl = methodSetter(method, newImp);
return originalImpl;
}
@interface NSResponder (Utils)
@end
@implementation NSResponder (Utils)
static IMP originalSupplementalTargetForActionSender;
static id newSupplementalTargetForActionSenderImp(id self, SEL _cmd, SEL action, id sender)
{
assert([NSStringFromSelector(_cmd) isEqualToString:@"supplementalTargetForAction:sender:"]);
if ([self isKindOfClass:[NSWindowController class]] || [self isKindOfClass:[NSViewController class]]) {
id target = ((id(*)(id, SEL, SEL, id)) originalSupplementalTargetForActionSender)(self, _cmd, action, sender);
if (target != nil)
return target;
id childViewControllers = nil;
if ([self isKindOfClass:[NSWindowController class]])
childViewControllers = [[(NSWindowController*) self contentViewController] childViewControllers];
if ([self isKindOfClass:[NSViewController class]])
childViewControllers = [(NSViewController*) self childViewControllers];
for (NSViewController *childViewController in childViewControllers) {
target = [NSApp targetForAction:action to:childViewController from:sender];
if (NO == [target respondsToSelector:action])
target = [target supplementalTargetForAction:action sender:sender];
if ([target respondsToSelector:action])
return target;
}
}
return nil;
}
+ (void) load
{
Method m = nil;
m = class_getInstanceMethod([NSResponder class], NSSelectorFromString(@"supplementalTargetForAction:sender:"));
originalSupplementalTargetForActionSender = classSwizzleMethod([self class], m, (IMP)newSupplementalTargetForActionSenderImp);
}
@end
这样做的好处是您不必将转发器代码添加到窗口控制器和所有视图控制器中(尽管子类化会使这个过程变得更容易),如果您有一个用于窗口内容视图的视图控制器,那么魔法就会自动发生。
交换方法总是有一定风险的,因此它并不是完美的解决方案,但我已经尝试过使用包含容器视图的非常复杂的视图/视图控制器层次结构,效果很好。
addLayer
操作的对象时,该操作将不会被调用。例如,当您有一个SplitViewController并选择其中一个项目时,就是这种情况。其他项不再在所选项目的链中。 - JanApotheker