iOS JavaScript 桥接器

IT技术 javascript ios html uiwebview
2021-01-28 16:38:46

我正在开发一个应用程序,我将在其中同时使用 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 就绪事件)
6个回答

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

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

1.从Objective-C执行JS方法

这实际上只是一行代码。

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

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

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调用然后被拦截。

同样,不干净,但它的工作原理。

并在您应该返回时返回一个值:)
2021-03-27 16:38:46
另一个提示:在 iFrame 中加载 URL 以防止闪烁
2021-03-28 16:38:46
没有问题。我只是没有使用它们,所以我无法评论在更大的项目中使用它们。我想澄清一下。:)
2021-03-28 16:38:46
@Folleto 嗨,你说“但我没有在大项目中使用过这些”,为什么不呢??你在这些库上发现了几个问题吗???提前致谢。
2021-03-30 16:38:46
提示:不要在你的方案中加入下划线或类似的东西。
2021-04-01 16:38:46

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

这家伙的功劳

您还可以在 JavaScript 中使用 iOS 可以获取的队列,以避免丢失 .src 更改太快的消息。上面链接的实现github.com/marcuswestin/WebViewJavascriptBridge使用队列方法。
2021-03-24 16:38:46

更新:这在 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”
2021-03-20 16:38:46

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

当然,但是该方法没有执行continuoulsy ..这是苹果提供的框架中的一个错误
2021-03-29 16:38:46
WKWebView有很多问题,包括没有正确处理 cookie。只是对那些认为它会解决您所有问题的人的警告 - 在采用它之前先谷歌一下。
2021-04-05 16:38:46

这在 iOS7 上是可能的,请查看http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

是的:JSContext* context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; 但是 JavaScriptCore 目前太有问题了,没有任何实际用途。
2021-03-26 16:38:46
并非如此:JavaScriptCore 不与 UIWebViews 交互。这是一个很棒的工具,但用于解决不同类别的问题。;)
2021-04-10 16:38:46