如何在Cordova iOS插件中实现UIWebViewDelegate?插件设置失效排查
解决Cordova插件中设置UIWebViewDelegate无效的问题
我之前也踩过这个坑!Cordova的CDVViewController本身就是UIWebViewDelegate的实现者,它会在初始化WebView时自动把自己设为代理,而且后续还会维护这个代理关系——直接给webView.delegate赋值的话,很快就会被Cordova的内部逻辑覆盖,导致你的自定义代理方法根本不会被调用。下面给你两种靠谱的解决方案:
方法一:代理转发(完整覆盖所有Delegate方法)
这种方式通过创建一个自定义代理类,把原有的CDVViewController代理“包裹”起来,既实现你的自定义逻辑,又不破坏Cordova原本的代理流程。
步骤1:在插件的.m文件中定义自定义代理类
// 自定义代理类,用于转发WebView代理方法 @interface CustomWebViewDelegate : NSObject <UIWebViewDelegate> @property (nonatomic, weak) id<UIWebViewDelegate> originalDelegate; // 持有原代理(弱引用避免循环引用) @end @implementation CustomWebViewDelegate // 重写你需要自定义的代理方法,比如拦截请求 - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { // 这里写你的自定义逻辑,比如判断请求URL做拦截 NSLog(@"自定义代理拦截请求:%@", request.URL.absoluteString); // 必须调用原代理的对应方法,保证Cordova的原有逻辑正常运行 if ([self.originalDelegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) { return [self.originalDelegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; } return YES; } // 重写其他你需要的代理方法,比如页面开始加载、加载完成等 - (void)webViewDidStartLoad:(UIWebView *)webView { NSLog(@"自定义代理:页面开始加载"); if ([self.originalDelegate respondsToSelector:@selector(webViewDidStartLoad:)]) { [self.originalDelegate webViewDidStartLoad:webView]; } } // 实现转发逻辑,把未重写的代理方法交给原代理处理 - (id)forwardingTargetForSelector:(SEL)aSelector { if ([self.originalDelegate respondsToSelector:aSelector]) { return self.originalDelegate; } return [super forwardingTargetForSelector:aSelector]; } - (BOOL)respondsToSelector:(SEL)aSelector { if ([self.originalDelegate respondsToSelector:aSelector]) { return YES; } return [super respondsToSelector:aSelector]; } @end
步骤2:在插件初始化时替换代理
首先在插件类的接口部分声明一个属性来持有自定义代理(避免被ARC自动释放):
@interface YourPluginName : CDVPlugin @property (nonatomic, strong) CustomWebViewDelegate* customDelegate; @end
然后在插件的pluginInitialize方法中完成代理替换:
- (void)pluginInitialize { // 获取当前的CDVViewController和它的WebView CDVViewController* viewController = (CDVViewController*)self.viewController; UIWebView* webView = viewController.webView; // 创建自定义代理实例,保存原代理 self.customDelegate = [[CustomWebViewDelegate alloc] init]; self.customDelegate.originalDelegate = webView.delegate; // 将WebView的代理替换为自定义代理 webView.delegate = self.customDelegate; }
方法二:监听Cordova原生通知(简单安全,适合常见场景)
如果你的需求只是监听页面加载开始、完成、失败这类事件,不需要拦截请求,那么可以直接监听Cordova内置的通知,完全不需要碰代理。
- (void)pluginInitialize { // 监听WebView开始加载的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleWebViewStartLoad:) name:CDVWebViewDidStartLoadNotification object:nil]; // 监听WebView加载完成的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleWebViewFinishLoad:) name:CDVWebViewDidFinishLoadNotification object:nil]; // 监听WebView加载失败的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleWebViewLoadError:) name:CDVWebViewDidFailLoadWithErrorNotification object:nil]; } // 处理开始加载事件 - (void)handleWebViewStartLoad:(NSNotification*)notification { UIWebView* webView = notification.object; NSURLRequest* request = notification.userInfo[@"request"]; NSLog(@"页面开始加载:%@", request.URL); // 你的自定义逻辑 } // 处理加载完成事件 - (void)handleWebViewFinishLoad:(NSNotification*)notification { NSLog(@"页面加载完成"); // 你的自定义逻辑 } // 处理加载失败事件 - (void)handleWebViewLoadError:(NSNotification*)notification { NSError* error = notification.userInfo[@"error"]; NSLog(@"页面加载失败:%@", error.localizedDescription); // 你的自定义逻辑 } // 插件销毁时移除通知监听,避免内存泄漏 - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; }
选择建议
- 如果需要处理所有UIWebViewDelegate方法(比如拦截请求、处理JS交互等),优先用方法一的代理转发;
- 如果只是监听页面加载状态,方法二更简单安全,不会干扰Cordova的内部逻辑。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

