iOS JavaScript 桥接器
Posted
技术标签:
【中文标题】iOS JavaScript 桥接器【英文标题】:iOS JavaScript bridge 【发布时间】:2012-03-17 10:21:16 【问题描述】:我正在开发一个应用程序,我将同时使用 UIWebView 中的 html5 和原生 ios 框架。我知道我可以实现 javascript 和 Objective-C 之间的通信。是否有任何库可以简化这种通信的实现?我知道有几个库可以在 HTML5 和 javascript 中创建原生 iOS 应用程序(例如 AppMobi、PhoneGap),但我不确定是否有一个库可以帮助创建使用大量 JavaScript 的原生 iOS 应用程序。我需要:
-
从 Objective-C 执行 JS 方法
从 JS 执行 Objective-C 方法
从 Objective-C 监听原生 JS 事件(例如 DOM 就绪事件)
【问题讨论】:
您可以使用 WKWebView: 从 javascript window.webkit.messageHandlers.NAME.postMessage(message) 调用,然后使用 [WKUserContentController addScriptMessageHandler:name:] 处理它以从 JS 调用 Objective-C 【参考方案1】:有一些库,但我没有在大型项目中使用这些库,因此您可能想尝试一下:
WebViewJavascriptBridge:https://github.com/marcuswestin/WebViewJavascriptBridge GAJavaScript:https://github.com/newyankeecodeshop/GAJavaScript—
但是,我认为这很简单,您可以自己尝试一下。当我需要这样做时,我个人正是这样做的。您还可以创建一个适合您需要的简单库。
1。从 Objective-C 执行 JS 方法
这实际上只是一行代码。
NSString *returnvalue = [webView stringByEvaluatingJavaScriptFromString:@"your javascript code string here"];
更多详情见官方UIWebView Documentation。
2。从 JS 执行 Objective-C 方法
不幸的是,这稍微复杂一些,因为在 Mac OSX 上没有相同的 windowScriptObject 属性(和类)允许两者之间完全通信。
但是,您可以轻松地从 javascript 自定义 URL 调用,例如:
window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value
然后用这个从 Objective-C 中截取它:
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:@"yourscheme"])
// parse the rest of the URL object and execute functions
这并不像应有的那样干净(或使用 windowScriptObject),但它可以工作。
3。监听来自 Objective-C 的原生 JS 事件(例如 DOM 就绪事件)
从上面的解释可以看出,如果你想这样做,你必须创建一些 JavaScript 代码,将它附加到你想要监控的事件上,并调用正确的 window.location
调用然后被拦截。
再一次,虽然不是应有的干净,但它确实有效。
【讨论】:
提示:不要在你的方案中使用下划线或类似符号。 并在你应该返回的时候返回一个值:) 另一个提示:在 iFrame 中加载 URL 以防止闪烁 @Folleto 嗨,你说“但我没有在大项目中使用这些”,为什么不呢?你在这些库上发现了几个问题???提前致谢。 没有问题。我根本没有使用它们,所以我无法评论在更大的项目中使用它们。我想澄清一下。 :)【参考方案2】:不推荐在接受的答案中从 JS 调用目标 c 的建议方法。问题的一个示例:如果您立即连续拨打两个电话,其中一个会被忽略(您不能太快更改位置)。
我推荐以下替代方法:
function execute(url)
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", url);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
您反复调用execute
函数,由于每次调用都在自己的 iframe 中执行,因此在快速调用时不应忽略它们。
感谢this guy。
【讨论】:
您还可以在 JavaScript 中使用 iOS 可以获取的队列,以避免丢失来自 .src 更改太快的消息。上面链接的实现,github.com/marcuswestin/WebViewJavascriptBridge,使用队列方法。【参考方案3】:更新:这在 iOS 8 中有所改变。我的回答适用于以前的版本。
另一种可能会被应用商店拒绝的方法是使用 WebScriptObject。
这些 API 在 OSX 上是公开的,但不在 iOS 上。
您需要定义内部类的接口。
@interface WebScriptObject: NSObject
@end
@interface WebView
- (WebScriptObject *)windowScriptObject;
@end
@interface UIWebDocumentView: UIView
- (WebView *)webView;
@end
您需要定义将用作 WebScriptObject 的对象
@interface WebScriptBridge: NSObject
- (void)someEvent: (uint64_t)foo :(NSString *)bar;
- (void)testfoo;
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (WebScriptBridge*)getWebScriptBridge;
@end
static WebScriptBridge *gWebScriptBridge = nil;
@implementation WebScriptBridge
- (void)someEvent: (uint64_t)foo :(NSString *)bar
NSLog(bar);
-(void)testfoo
NSLog(@"testfoo!");
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
return NO;
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
return NO;
+ (NSString *)webScriptNameForSelector:(SEL)sel
// Naming rules can be found at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/Protocols/WebScripting_Protocol/Reference/Reference.html
if (sel == @selector(testfoo)) return @"testfoo";
if (sel == @selector(someEvent::)) return @"someEvent";
return nil;
+ (WebScriptBridge*)getWebScriptBridge
if (gWebScriptBridge == nil)
gWebScriptBridge = [WebScriptBridge new];
return gWebScriptBridge;
@end
现在为你的 UIWebView 设置一个实例
if ([uiWebView.subviews count] > 0)
UIView *scrollView = uiWebView.subviews[0];
for (UIView *childView in scrollView.subviews)
if ([childView isKindOfClass:[UIWebDocumentView class]])
UIWebDocumentView *documentView = (UIWebDocumentView *)childView;
WebScriptObject *wso = documentView.webView.windowScriptObject;
[wso setValue:[WebScriptBridge getWebScriptBridge] forKey:@"yourBridge"];
现在您可以在 javascript 中调用:
yourBridge.someEvent(100, "hello");
yourBridge.testfoo();
【讨论】:
我的应用在应用商店被拒绝。我收到的问题是“应用程序包含或继承自 AppName 中的非公共类:UIWebDocumentView”【参考方案4】:In iOS8 you can look at WKWebView instead of UIWebView。这有以下类: WKScriptMessageHandler:提供一种从网页中运行的 JavaScript 接收消息的方法。
【讨论】:
当然可以,但是该方法没有连续执行..这是苹果提供的框架中的一个错误WKWebView
有很多问题,包括没有正确处理 cookie。只是对任何来到这里认为它会解决你所有问题的人的警告 - 在采用它之前先谷歌一下。【参考方案5】:
这在 iOS7 上是可能的,结帐http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/
【讨论】:
并非如此:JavaScriptCore 不与 UIWebViews 交互。这是一个很棒的工具,但针对不同类别的问题。 ;) 是的:JSContext* context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];但目前 JavaScriptCore 漏洞百出,无法真正发挥作用。【参考方案6】:您最好的选择是 Appcelerators Titanium 产品。他们已经使用 webkit 使用的 V8 引擎 JavascriptCore 引擎构建了一个 Obj-C javascript 桥。它也是开源的,因此您可以下载它并随意修改 Obj-C。
【讨论】:
@hova,不是 Obj-C V8 桥。 V8 仅适用于 android。【参考方案7】:看看 KirinJS 项目:Kirin JS,它允许将 Javascript 用于应用程序逻辑和适合其运行平台的原生 UI。
【讨论】:
还有人为麒麟做贡献吗?过去几个月我没有看到任何活动...【参考方案8】:我创建了一个类似于 WebViewJavascriptBridge 的库,但它更像 JQuery,更易于设置且更易于使用。不依赖 jQuery(尽管值得称赞的是,如果我在写这篇文章之前就知道 WebViewJavascriptBridge 存在,我可能只是在深入研究之前稍微退缩了)。让我知道你的想法! jockeyjs
【讨论】:
【参考方案9】:如果您在 iOS 8 上使用 WKWebView,请查看XWebView,它可以自动将原生界面暴露给 javascript。
【讨论】:
以上是关于iOS JavaScript 桥接器的主要内容,如果未能解决你的问题,请参考以下文章
webviewjavascriptbridge之js与ios桥接篇 在h5里加啥代码