如何屏蔽UITextField的Paste气泡?canPerformAction未捕获paste:动作
我之前也踩过一模一样的坑!你用UIResponder分类全局管控的思路本身没问题,但iOS在显示Paste气泡时的动作检查逻辑不走常规的响应链流程——它会直接询问UITextField内部的文本交互对象,而不是顺着响应链往上调用canPerformAction:withSender:,所以你的分类根本捕获不到paste:这个动作请求。
下面给你几个亲测有效的解决方案,按优先级和上架安全性排序:
1. 直接给UITextField添加分类(最安全,推荐)
既然系统绕开了全局响应链,那我们直接在UITextField层面拦截动作检查,把你的禁止逻辑搬到这里来就行。既可以全局管控,又能准确捕获到paste:动作:
#import <objc/runtime.h> static char kProhibitedActionsKey; @implementation UITextField (ActionControl) // 关联禁止动作集合,和你之前的逻辑保持一致 - (NSMutableSet<NSString *> *)prohibitedActions { NSMutableSet *actions = objc_getAssociatedObject(self, &kProhibitedActionsKey); if (!actions) { actions = [NSMutableSet set]; objc_setAssociatedObject(self, &kProhibitedActionsKey, actions, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return actions; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { NSString *actionStr = NSStringFromSelector(action); // 检查当前动作是否在禁止列表中 if ([self.prohibitedActions containsObject:actionStr]) { return NO; } // 保留其他系统默认动作,调用父类实现 return [super canPerformAction:action withSender:sender]; } @end
使用的时候,只需要给目标TextField添加禁止动作:
[yourTextField.prohibitedActions addObject:NSStringFromSelector(@selector(paste:))];
2. 拦截内部文本交互对象的动作检查(慎用,涉及私有API)
如果必须用全局UIResponder的方式管控,那就要同时拦截UITextField内部的UITextInteraction对象的canPerformAction方法。这个类是iOS的私有类,提交App Store有被拒风险,但测试环境下可以验证:
#import <objc/runtime.h> @implementation UITextInteraction (DisablePaste) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ SEL originalSel = @selector(canPerformAction:withSender:); SEL swizzledSel = @selector(custom_canPerformAction:withSender:); Method originalMethod = class_getInstanceMethod(objc_getClass("UITextInteraction"), originalSel); Method swizzledMethod = class_getInstanceMethod(self, swizzledSel); if (originalMethod && swizzledMethod) { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } - (BOOL)custom_canPerformAction:(SEL)action withSender:(id)sender { NSString *actionStr = NSStringFromSelector(action); // 这里可以复用你之前的全局禁止逻辑,比如从UIResponder分类里取prohibitedActions UIResponder *responder = (UIResponder *)[self valueForKey:@"responder"]; if ([responder respondsToSelector:@selector(prohibitedActions)] && [[responder prohibitedActions] containsObject:actionStr]) { return NO; } return [self custom_canPerformAction:action withSender:sender]; } @end
3. 极端方案:禁用长按菜单(仅当不需要任何长按操作时使用)
如果你不需要UITextField的任何长按菜单(包括copy、cut等),可以直接禁用长按手势:
yourTextField.longPressGestureRecognizer.enabled = NO;
但这个方法会把所有长按相关的交互都去掉,只适合特殊场景。
为什么你的原方案捕获不到paste:?
再补充下原因:当用户点击空的UITextField时,系统会直接触发内部的文本交互逻辑,弹出Paste气泡,这个过程中不会向UITextField所在的响应链发送paste:的canPerformAction检查请求,而是直接询问内部的文本交互代理,所以你的UIResponder分类根本收不到这个动作。
内容的提问来源于stack exchange,提问作者Andrey Solovyov

