iOS JavaScript桥


101

我正在开发一个将同时使用UIWebView中的HTML5和本机iOS框架的应用程序。我知道我可以实现JavaScript和Objective-C之间的通信。是否有任何库可以简化此通信的实现?我知道有几个库可以用HTML5和javascript创建本机iOS应用程序(例如AppMobi,PhoneGap),但是我不确定是否有一个库可以帮助创建使用JavaScript繁重的本机iOS应用程序。我需要:

  1. 从Objective-C执行JS方法
  2. 从JS执行Objective-C方法
  3. 监听来自Objective-C的本地JS事件(例如DOM ready事件)

1
您可以使用WKWebView:从javascript window.webkit.messageHandlers。{NAME} .postMessage(message)调用,然后使用[WKUserContentController addScriptMessageHandler:name:]处理它,以从JS调用Objective-C
kostyl,2015年

Answers:


150

有一些库,但是我没有在大型项目中使用过这些库,因此您可能想尝试一下:

-

但是,我认为这很简单,您可以自己尝试一下。当我需要这样做时,我个人就是这样做的。您可能还会创建一个适合您需求的简单库。

1.从Objective-C执行JS方法

这实际上只是一行代码。

NSString *returnvalue = [webView stringByEvaluatingJavaScriptFromString:@"your javascript code string here"];

有关官方UIWebView文档的更多详细信息。

2.从JS执行Objective-C方法

不幸的是,这稍微复杂了一点,因为Mac OSX上不存在相同的windowScriptObject属性(和类),因此两者之间无法完全通信。

但是,您可以轻松地从javascript自定义网址进行调用,例如:

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 ready事件)

从上面的说明中可以看出,如果要执行此操作,则必须创建一些JavaScript代码,将其附加到要监视的事件上,然后调用正确的window.location调用然后将其拦截。

同样,它不是很干净,但是可以工作。


12
提示:不要在方案中加下划线或类似内容。

4
并在应该执行以下操作时返回值:)
Pizzaiola Gorgonzola

2
另一个提示:将网址加载到iFrame中以防止闪烁
iCaramba 2014年

@Folleto嗨,您说过“但我没有在大型项目中使用过这些工具”,为什么不呢?您在这些库中找到几个问题了吗???提前致谢。
JERC

1
没有问题。我只是没有使用它们,因此无法评论在较大的项目中使用它们。我想澄清一下。:)
Folletto

57

不建议在接受的答案中建议从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中执行,因此在快速调用它们时不应忽略它们。

归功于这个家伙


1
您还可以在JavaScript中使用iOS可以提取的队列,以免因.src更改消息而丢失消息。上面链接的实现github.com/marcuswestin/WebViewJavascriptBridge使用队列方法。
kevintcoughlin 2014年

12

更新:这在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”
chandru

5

在iOS8中,您可以查看WKWebView而不是UIWebView。此类具有以下类:WKScriptMessageHandler:提供一种用于从运行在网页中的JavaScript接收消息的方法。


当然可以,但是该方法不能执行连续操作。.这是苹果提供的框架中的错误
Josh Kethepalli 2015年

WKWebView有很多问题,包括不能正确处理Cookie。对来到这里的人以为这是一个警告,认为它可以解决您的所有问题-在采用该方法之前,请先进行一下搜索。
CupawnTae

3

3
并非如此:JavaScriptCore不会与UIWebViews交互。这是一个很好的工具,但是可以解决另一类问题。;)
Folletto 2014年

1
是的,它的确是这样:JSContext * context = [_webView valueForKeyPath:@“ documentView.webView.mainFrame.javaScriptContext”]; 但是,JavaScriptCore目前存在太多错误,无法真正使用。
malhal 2014年

0

最好的选择是Appcelerators Titanium产品。他们已经使用webkit 使用的V8引擎 JavascriptCore引擎构建了Obj-C javascript桥。它也是开源的,因此您可以下载它并随意修改Obj-C。


@hova,不是Obj-C V8桥。V8仅适用于Android。
罗斯

0

看一下KirinJS项目:Kirin JS,它可以将Javascript用于适合其运行平台的应用程序逻辑和本机UI。


还有人为麒麟作贡献吗?我最近几个月没有任何活动...
山姆

0

我创建了一个类似WebViewJavascriptBridge的库,但它更像JQuery,易于设置且易于使用。不依赖jQuery(虽然值得赞扬,但在编写此书之前,我曾知道WebViewJavascriptBridge存在,所以在深入研究之前,我可能会稍作退缩)。让我知道你的想法!骑师


0

如果您在iOS 8上使用WKWebView,请查看XWebView,它可以自动将本机界面公开给javascript。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.