IOS themeableBrowser 調(diào)用Cordova原生插件(一)

注:本篇文章themeableBrowser緊能調(diào)用本地調(diào)用本地Cordova,如需遠程調(diào)用本地Cordova,請看IOS themeableBrowser 調(diào)用Cordova原生插件(二)http://www.itdecent.cn/p/a6d95ccf341a

cordova themeableBrowser插件中調(diào)用原生插件,比如相機、指紋等。這里采用themeableBrowser調(diào)用OC,然后OC把結(jié)果通過js回調(diào)給themeableBrowser的方式,實現(xiàn)如下:

1、首先添加 javaScriptCore.framework,用于js調(diào)用OC。

2、在OC代碼中添加 js 要調(diào)用的方法。找到themeableBrowser插件下的CDVThemeableBrowser.m,修改代碼中webViewDidFinishLoad方法:

- (void)webViewDidFinishLoad:(UIWebView*)theWebView
{
    // update url, stop spinner, update back/forward

    self.addressLabel.text = [self.currentURL absoluteString];
    [self updateButton:theWebView];

    if (self.titleLabel && _browserOptions.title
            && !_browserOptions.title[kThemeableBrowserPropStaticText]
            && [self getBoolFromDict:_browserOptions.title withKey:kThemeableBrowserPropShowPageTitle]) {
        // Update title text to page title when title is shown and we are not
        // required to show a static text.
        self.titleLabel.text = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    }

    [self.spinner stopAnimating];

    // Work around a bug where the first time a PDF is opened, all UIWebViews
    // reload their User-Agent from NSUserDefaults.
    // This work-around makes the following assumptions:
    // 1. The app has only a single Cordova Webview. If not, then the app should
    //    take it upon themselves to load a PDF in the background as a part of
    //    their start-up flow.
    // 2. That the PDF does not require any additional network requests. We change
    //    the user-agent here back to that of the CDVViewController, so requests
    //    from it must pass through its white-list. This *does* break PDFs that
    //    contain links to other remote PDF/websites.
    // More info at https://issues.apache.org/jira/browse/CB-2225
    BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
    if (isPDF) {
        [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
    }

    JSContext* content = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];        //self do
    content[@"getMessage"] = ^(NSArray* jsonEntry) {
        // 獲取到根控制器MainViewContoller,因為這個控制器初始化了Cordova插件,需要用這個控制器來調(diào)用插件
        AppDelegate *appdelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
        UIViewController *rootViewController = appdelegate.window.rootViewController;
        CDVViewController *vc = (CDVViewController *) rootViewController;
        
        // 解析調(diào)用插件所需要的參數(shù)
        CDVInvokedUrlCommand* command = [CDVInvokedUrlCommand commandFromJson:jsonEntry];
        
        // 用根控制器上的commandQueue方法,調(diào)用插件
        if (![vc.commandQueue execute:command]) {
#ifdef DEBUG
            NSError* error = nil;
            NSString* commandJson = nil;
            NSData* jsonData = [NSJSONSerialization dataWithJSONObject:jsonEntry
                                                               options:0
                                                                 error:&error];
            
            if (error == nil) {
                commandJson = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
            }
            
            static NSUInteger maxLogLength = 1024;
            NSString* commandString = ([commandJson length] > maxLogLength) ?
            [NSString stringWithFormat : @"%@[...]", [commandJson substringToIndex:maxLogLength]] :
            commandJson;
            
            NSLog(@"FAILED pluginJSON = %@", commandString);
#endif
        }
    };
    [self.navigationDelegate webViewDidFinishLoad:theWebView];
}

3、第二步中通過最上層根控制器去調(diào)用方法,而不是themeableBrowser中的,所以重寫present方法,獲取當(dāng)前最上層控制器進行present。新建present類。

#import "UIViewController+Present.h"
#import <objc/runtime.h>
@implementation UIViewController (Present)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method presentM = class_getInstanceMethod(self.class, @selector(presentViewController:animated:completion:));
        Method presentSwizzlingM = class_getInstanceMethod(self.class, @selector(dy_presentViewController:animated:completion:));
        
        method_exchangeImplementations(presentM, presentSwizzlingM);
    });
}
- (void)dy_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    UIViewController *currentVc = [self topViewController];
    if ([currentVc  isKindOfClass:[UIAlertController class]]) {
        [self dy_presentViewController:viewControllerToPresent animated:flag completion:completion];
    } else {
        [[self topViewController] dy_presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
}
- (UIViewController *)topViewController {
        UIViewController *topVC;
        topVC = [self getTopViewController:[[UIApplication sharedApplication].keyWindow rootViewController]];
        while (topVC.presentedViewController) {
        
                topVC = [self getTopViewController:topVC.presentedViewController];
        
         }
        return topVC;
}
- (UIViewController *)getTopViewController:(UIViewController *)vc {
    
    if (![vc isKindOfClass:[UIViewController class]]) {
                return nil;
    }    
    if ([vc isKindOfClass:[UINavigationController class]]) {
                    return [self getTopViewController:[(UINavigationController *)vc topViewController]];
            
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [self getTopViewController:[(UITabBarController *)vc selectedViewController]];
    } else {
        return vc;
    }
}
@end

4、將themeableBrowser設(shè)置為全局,方便調(diào)用回調(diào),修改themeableBrowser.js。

exports.open = function(strUrl, strWindowName, strWindowFeatures, callbacks) {
    // Don't catch calls that write to existing frames (e.g. named iframes).
    if (window.frames && window.frames[strWindowName]) {
        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
        return origOpenFunc.apply(window, arguments);
    }
    strUrl = urlutil.makeAbsolute(strUrl);
    var iab = new ThemeableBrowser();
    callbacks = callbacks || {};
    for (var callbackName in callbacks) {
        iab.addEventListener(callbackName, callbacks[callbackName]);
    }
    var cb = function(eventname) {
       iab._eventHandler(eventname);
    };
    strWindowFeatures = strWindowFeatures && JSON.stringify(strWindowFeatures);
    // Slightly delay the actual native call to give the user a chance to
    // register event listeners first, otherwise some warnings or errors may be missed.

    setTimeout(function() {
        exec(cb, cb, 'ThemeableBrowser', 'open', [strUrl, strWindowName, strWindowFeatures || '']);
    }, 0);
    // 聲明全局變量__globalThemeableBrowser,表示當(dāng)前界面開啟了ThemeableBrowser      //self do
    window.__globalThemeableBrowser = iab;
    return iab;
};

5、由于原生是調(diào)用根控制器上的插件返回callback,是和ThemeableBrowser不同層級的webview,所以需要做一層轉(zhuǎn)發(fā),判斷當(dāng)前webview的callback數(shù)組,是否含有接收到的callbackid,如果不在在數(shù)組中,則說明不是該webview調(diào)用的插件,則調(diào)用ThemeableBrowser里的js回傳方法,回傳開ThemeableBrowser的webview接收callback。修改cordova.js:

callbackFromNative: function(callbackId, isSuccess, status, args, keepCallback) {
        try {
            var callback = cordova.callbacks[callbackId];
            if (callback) {
                if (isSuccess && status == cordova.callbackStatus.OK) {
                    callback.success && callback.success.apply(null, args);
                } else if (!isSuccess) {
                    callback.fail && callback.fail.apply(null, args);
                }
                /*
                else
                    Note, this case is intentionally not caught.
                    this can happen if isSuccess is true, but callbackStatus is NO_RESULT
                    which is used to remove a callback from the list without calling the callbacks
                    typically keepCallback is false in this case
                */
                // Clear callback if not expecting any more results
                if (!keepCallback) {
                    delete cordova.callbacks[callbackId];
                }
            }
            else {          //self do
                if(window.__globalThemeableBrowser) {
                    var message = 'cordova.callbackFromNative("'+callbackId+'",'+isSuccess+',' + status +',' +JSON.stringify(args) + ',' + keepCallback + ')';
//調(diào)用hemeableBrowser插件里的js回傳方法
                window.__globalThemeableBrowser.executeScript({code: message});
                }
            }
        }
        catch (err) {
            var msg = "Error in " + (isSuccess ? "Success" : "Error") + " callbackId: " + callbackId + " : " + err;
            console && console.log && console.log(msg);
            cordova.fireWindowEvent("cordovacallbackerror", { 'message': msg });
            throw err;
        }
    }

6、編寫測試用例。注意調(diào)用getMessage方法調(diào)用OC,四個參數(shù),第一個是回調(diào),也就是callbackId,第二個和第三個是插件名和OC方法名,結(jié)合插件js中cordova.exec方法可以快速找到,第四個是插件參數(shù)。

function camera() {
            var callbackId = getCallbackId(
                                          "Camera",
                                          function (ret) {
                                          alert("successs=============")
                                          },
                                          function (ret) {
                                          alert("fail=============")
                                          });
            getMessage([callbackId,
                      "Camera",
                      "takePicture",
                      [50,Camera.DestinationType.DATA_URL]
                        ]);
        }
function getCallbackId(className,successBack,failBack) {
            var callbackId = className + cordova.callbackId++;
            cordova.callbacks[callbackId] = {success: successBack, fail: failBack};
            return callbackId;
        }

最后初入cordova,多多關(guān)照,多多指點。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容