WKWebView和UIWebView加载本地html和JS交互各种坑解决办法

来源:互联网 发布:查看mysql启动状态 编辑:程序博客网 时间:2024/06/05 02:25

因为苹果的文件机制,所有的资源文件都相当于放在bundle的路径里,里面不分任何文件夹路径,所以我们在加载(js, css, png)等等的资源文件的时候,不应该加上任何文件名,所以最好是把所有有关html的文件都放在同一平级的文件夹


UIWebView

1.OC调JS

    /**     *  ocCalls:js的函数名     */    JSValue *value = self.jsContext[@"ocCalls"];    /**     *  @[@"参数"]:传给js端的参数     */    [value callWithArguments:@[@"参数"]];


2.JS调OC

JS调OC有好几种方法,这里我就只列举一种我个人常用的方法,这个可以写在加载之前就行

    // 获取js上下文    self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];    // 获取js对象,这样会强持有。。没释放对象,目前没解决(个人觉得用单例,这样起码不会一直创建对象,有哪个大神这个可以指教一下嘛0.0)    self.jsContext[@"obj"] = self.uihd;


self.uihd .h文件
// 个人觉得这两个协议还是分开好,虽然写在一次方便,看起来少,但我觉得逻辑上是比较混乱的// 这是传出去的协议@protocol UIHDelegate <NSObject>- (void)hdOne;- (void)hdTwo:(NSString *)name;- (void)hdThree:(NSString *)name age:(NSString *)age;@end// 这是遵守JS里方法的协议,必须得遵守JSExport协议@protocol JSDelegate <JSExport>// 这里的方法名称和html的函数名称必须相同,同时这个方法的返回值,html里面也能取的到,也相当于另一种传值- (NSString *)one;- (void)two:(NSString *)name;// 多参数写法一:注意,js那边的函数必须驼峰命名- (void)three:(NSString *)name age:(NSString *)age;// 多参数写法二:直接完全和js一样,但后面参数面前不能加名字//- (void)threeAge:(NSString *)name :(NSString *)age;@end// 遵守刚刚写的js协议,然后在.m实现,再通过另一个协议传出去@interface UIHD : NSObject <JSDelegate>@property (nonatomic, weak) id <UIHDelegate> delegate;

self.uihd .m文件

#pragma mark -- JSDelegate- (NSString *)one {    // 代理    if ([self.delegate respondsToSelector:@selector(hdOne)]) {        [self.delegate hdOne];    }    NSLog(@"one");    return @"one";}- (void)two:(NSString *)name {    if ([self.delegate respondsToSelector:@selector(hdTwo:)]) {        [self.delegate hdTwo:name];    }    NSLog(@"two %@", name);}- (void)three:(NSString *)name age:(NSString *)age {    if ([self.delegate respondsToSelector:@selector(hdThree:age:)]) {        [self.delegate hdThree:name age:age];    }    NSLog(@"three %@, %@", name, age);}

html代码

    // html代码 创建对象,让UIWebView在外面监听    var obj;    // 调用OC方法,并且获取方法返回值    var returnValue = obj.threeAge("老王", "18");



WKWebView

说到WKWebView就一把心酸泪了,最近公司突然说搞h5界面,并且得用性能比较好的,而且还是保持用原生,不用第三方的情况下。就这样,默默的跳进了这个坑里,特别我这边的前端当时还写错html代码,然后我一直以为是我的错~.~,好吧,吐槽到此为止,直接上代码

OC调用JS

    /** !!必须在加载完之后才能调用     *  调用的函数,传参数则自己拼接到 () 里面     */    NSString *js = @"tapBtnThree()";    [self.webView evaluateJavaScript:js completionHandler:^(id _Nullable object, NSError * _Nullable error) {        if (error) {            NSLog(@"error = %@", error);        }else {            NSLog(@"object = %@", object);        }    }];


JS给OC发送消息

        // configuration:WKWebViewConfiguration类,自己查这个是什么鬼        /** !!!注意,这个方法不要填self,连weakSelf都不行,不然会一直强持有,所以这里得重新建一个控制器,当然你也可以想一下其他办法         *  @prama Handler:回调人         *  @prama name:js方法名称         */        WKMD *delegate = [[WKMD alloc] init];        delegate.delegate = self;        /**         *  @prama Handler:代理         *  @prama name:JS发送消息的名字  JS发送消息格式: window.webkit.messageHandlers.hehe.postMessage(message)         */        [configuration.userContentController addScriptMessageHandler:delegate name:@"one"];


WKMD .h文件

@protocol WKMDDelegate <NSObject> // 通过代理传js发送的消息出去- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;@end@interface WKMD : NSObject <WKScriptMessageHandler>@property (nonatomic, weak) id <WKMDDelegate> delegate;@end

WKMD .m文件

#pragma mark -- WKScriptMessageHandler- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {    NSLog(@"WKMD调用代理");    if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {        [self.delegate userContentController:userContentController didReceiveScriptMessage:message];    }}

html代码

        // 这是html里面的代码        // 消息        var message = {            'method' : 'hello',            'param1' : 'liuyanwei',        };        // 发送消息 window.webkit.messageHandlers.约定好的消息名.postMessage(消息)        window.webkit.messageHandlers.three.postMessage(message);



WKWebView9.0版本以下加载本地html问题

- (void)loadWeb {    // 原理就是9.0以下,把文件移到临时文件夹        // 9.0以上    if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {        // 取本地html文件路径        NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];        if (path) {            // 获取本地html的url和资源的url(就是bundle的url)            [self.webView loadFileURL:[NSURL fileURLWithPath:path] allowingReadAccessToURL:[NSBundle mainBundle].resourceURL];        }            }else {        // 9.0以下                // 获取本地文件夹的路径(必须得是蓝色文件夹 Create folder references)        NSString *path = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"devyellow_8.0"];                if(path) {            NSURL *fileUrl = [NSURL fileURLWithPath:path];            // 把文件夹转到tmp目录            fileUrl = [self fileURLForBuggyWKWebView:fileUrl];            NSURL *realUrl = [NSURL fileURLWithPath:[fileUrl.path stringByAppendingString:@"/index.html"]];            NSURLRequest *request = [NSURLRequest requestWithURL:realUrl];                        [self.webView loadRequest:request];        }    }}// 9.0以下将文件夹copy到tmp目录- (NSURL *)fileURLForBuggyWKWebView:(NSURL *)fileURL {    NSError *error = nil;    if (!fileURL.fileURL || ![fileURL checkResourceIsReachableAndReturnError:&error]) {        return nil;    }        NSFileManager *fileManager = [NSFileManager defaultManager];    NSURL *temDirURL = [NSURL fileURLWithPath:NSTemporaryDirectory()];    [fileManager createDirectoryAtURL:temDirURL withIntermediateDirectories:YES attributes:nil error:&error];        NSURL *dstURL = [temDirURL URLByAppendingPathComponent:fileURL.lastPathComponent];        [fileManager removeItemAtURL:dstURL error:&error];    [fileManager copyItemAtURL:fileURL toURL:dstURL error:&error];        return dstURL;}


WKWebView这里还有个坑,当时还有个需求,就是本地的html加载的时候要从我这边获取值才能加载,WKWebView它又不能在加载的时候从我这边获取值,不像UIWebView能直接用一个对象调用OC方法,然后传一个返回值给html。经过各种姿势的查资料,终于想出了一个解决办法,就是加载之前,把要传的值,放到webView的缓存里,然后前端自己从缓存里取值加载!~不废话,直接上代码

        /** 其实这个格式和字典一样         *  key: 和前端约定好的key         *  value: 需要传的值         */        NSString *sipNum = [NSString stringWithFormat:@"localStorage.setItem(\"key\", '%@');", @"value"];        /** 添加脚本         *  param injectionTime:WKUserScriptInjectionTimeAtDocumentStart在加载之前注入         *  param forMainFrameOnly:是否主窗口(其实我也不清楚这个是啥)         */        WKUserScript *script = [[WKUserScript alloc] initWithSource:sipNum injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];        [configuration.userContentController addUserScript:script];



原创粉丝点击