UIWebView Bug: – :无法识别的select器发送到实例

UIWebView ,如果包含文本的input元素具有焦点,并按下导致input失去焦点的button,则随后双击input以重新获得焦点并从popup栏中select剪切(或复制或粘贴)看起来会导致UIWebView崩溃的错误:

 -[UIWebView cut:]: unrecognized selector sent to instance 0x10900ca60 

演示项目: https : //github.com/guarani/WebViewDoubleTapTestTests.git

我认为这一定是一个UIWebView错误,有什么想法?

为了完整,这里是我的networking视图的内容,

 <html> <head> </head> <body> <br><br> <input type="text"> <input type="button"> </body> </html> 

在苹果提交了一个错误报告:15894403

更新2014/10/15:iOS 8.0.2中仍存在问题(12A405)

这是一个苹果的错误。 问题在于cut: action在响应者链中被错误地发送,最终被发送到UIWebView实例,而不是实现该方法的内部UIWebDocumentView

在Apple修复这个bug之前,让我们来看一下Objective C运行时的一些乐趣。

在这里,我将UIWebView的子类UIWebView为支持所有的UIResponderStandardEditActions方法,通过转发它们到正确的内部实例。

 @import ObjectiveC; @interface CutCopyPasteFixedWebView : UIWebView @end @implementation CutCopyPasteFixedWebView - (UIView*)_internalView { UIView* internalView = objc_getAssociatedObject(self, "__internal_view_key"); if(internalView == nil && self.subviews.count > 0) { for (UIView* view in self.scrollView.subviews) { if([view.class.description hasPrefix:@"UIWeb"]) { internalView = view; objc_setAssociatedObject(self, "__internal_view_key", view, OBJC_ASSOCIATION_ASSIGN); break; } } } return internalView; } void webView_implement_UIResponderStandardEditActions(id self, SEL selector, id param) { void (*method)(id, SEL, id) = (void(*)(id, SEL, id))[[self _internalView] methodForSelector:selector]; //Call internal implementation. method([self _internalView], selector, param); } - (void)_prepareForNoCrashes { NSArray* selectors = @[@"cut:", @"copy:", @"paste:", @"select:", @"selectAll:", @"delete:", @"makeTextWritingDirectionLeftToRight:", @"makeTextWritingDirectionRightToLeft:", @"toggleBoldface:", @"toggleItalics:", @"toggleUnderline:", @"increaseSize:", @"decreaseSize:"]; for (NSString* selName in selectors) { SEL selector = NSSelectorFromString(selName); //This is safe, the method will fail if there is already an implementation. class_addMethod(self.class, selector, (IMP)webView_implement_UIResponderStandardEditActions, ""); } } - (void)awakeFromNib { [self _prepareForNoCrashes]; [super awakeFromNib]; } @end 

在故事板中使用这个子类。

玩的开心。

如果你不介意没有剪切/粘贴/等的标注。 在这种情况下,当UIWebview错误地成为第一响应者,那么你也可以用这个类别修复它。 这并不禁止剪切/粘贴/等。 当UIWebDocumentView(正确)成为第一响应者。

 @implementation UIWebView (NoWrongPerformWebview) - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { return NO; } @end 

// Swift 4兼容版本

 import UIKit extension UIWebView { override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { // Should be respond to a certain Selector ?? return responds(to: action) } } 

如果有人感兴趣,下面是Leo Natans方法的快速版本:

 import Foundation import ObjectiveC var AssociatedObjectHandle: UInt8 = 0 class CustomWebView: UIWebView { func _internalView() -> UIView? { var internalView:UIView? = objc_getAssociatedObject(self, "__internal_view_key") as? UIView if internalView == nil && self.subviews.count > 0 { for view: UIView in self.scrollView.subviews { if view.self.description.hasPrefix("UIWeb") { internalView = view objc_setAssociatedObject(self, "__internal_view_key", view, objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN) } } } return internalView } override func awakeFromNib() { super.awakeFromNib() self._prepareForNoCrashes() } func _prepareForNoCrashes() { let selectors = ["cut:", "copy:", "paste:", "select:", "selectAll:", "delete:", "makeTextWritingDirectionLeftToRight:", "makeTextWritingDirectionRightToLeft:", "toggleBoldface:", "toggleItalics:", "toggleUnderline:", "increaseSize:", "decreaseSize:"] for selName: String in selectors { let selector = NSSelectorFromString(selName) //This is safe, the method will fail if there is already an implementation. let swizzledMethod:IMP = class_getInstanceMethod(CustomWebView.self, #selector(CustomWebView.webView_implement_UIResponderStandardEditActions)) class_addMethod(CustomWebView.self, selector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)) } } func webView_implement_UIResponderStandardEditActions(this:AnyObject, selector:Selector, param:AnyObject) { let method = {(val1: UIView?, val2: Selector, val3: AnyObject) -> Void in self._internalView()?.methodForSelector(selector) } method(self._internalView(), selector, param); } }