UIWebView和H5的JS交互那些事儿

iOS与H5交互是JS之间的相互调用,这里我们使用的UIWebView和JavaScriptCore来交互,H5调用客户端的JS有两种形式,一种是H5去取客户端的相关数据,然后H5自己进行业务处理;另一种是H5通过回调的形式,调用客户端的方法,让客户端进行业务处理。

1.H5调用客户端方法,获取客户端数据
这里有个业务场景,假如说进入一个页面之后需要用户的user信息,这个时候H5就调用js方法直接去取数据,为了保持H5端代码的可读性,我们的H5业务是在window下面花了一个TEST来进行业务处理(类似调用的时候是window.TEST进行处理),所以我们绑定的时候和平时是不一样的需要用到JSExport这个类
H5直接在window下进行方法处理的时候

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
    __weak typeof(self) weakSelf = self;
    JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    context[@"getShareUrl"] = ^(NSString *articleId, NSString *title, NSString *imageUrl, NSString *shareUrl, NSString *url){
      //获取H5给的参数信息,进行方法处理。
        });
    };
    return YES;
}

上面的代码就是在webview的delegate方法里绑定了一个'getShareUrl'的JS方法,然后回调给客户端5个参数,我们通过绑定JSContext上下文和webview绑定之后就可以交互了。

H5在window下面挂了TEST这样二级window的时候
为了能够相应这样的JS调用,继承JSExport,创建一个代理相应类

@protocol ReceiveJSObjectProtocol <JSExport>
//获取客户端user信息
-(NSString *)userInfo;
//传给客户端一个内容
-(void)title:(NSString *)content;
//调用客户端登录方法
-(void)login;

@end

创建一个对象,实现我们刚才穿件的代理方法

//.h文件
@interface SpeakingReadOverJsExport : NSObject<ReceiveJSObjectProtocol>

@property (nonatomic,weak) UIWebView *webView;

@property (nonatomic,weak) id<ResponseJsCallbackDelegate> delegate;

@end

//.m文件
@implementation SpeakingReadOverJsExport

-(void)newShare:(NSString *)title :(NSString *)description :(NSString *)url :(NSString *)imageUrl :(NSString *)channel :(NSString*)funcname{
    if ([self.delegate respondsToSelector:@selector(newShare::::::)]) {//类似于这样的js调用真是坑爹,我只想说,H5的,你们能规范点儿吗,坑啊!
        [self.delegate newShare:title :description :url :imageUrl :channel :funcname];
    }
}

-(void)tips:(NSString *)content{
    if ([self.delegate respondsToSelector:@selector(tips:)]) {
        [self.delegate tips:content];
    }
}

-(void)title:(NSString *)content{
    if ([self.delegate respondsToSelector:@selector(title:)]) {
        [self.delegate title:content];
    }
}

-(void)login{
    if ([self.delegate respondsToSelector:@selector(login)]) {
        [self.delegate login]; //调用登录方法
    }
}

-(NSString *)userInfo
{
    if ([self.delegate respondsToSelector:@selector(userInfo)]) {
        return [self.delegate userInfo]; //这个就可以把客户端的user信息回传给H5了
    }
    else
    {
        return @"";
    }
}

这一切执行的前提是客户端能够相应到,H5在调用客户端JS的时候一定要等webview加载完成,JSContext上下文和webview绑定之后,我们的H5在和客户端交互的时候,没有等客户端加载完成就去调用客户端JS方法,结果没有响应到,在当初项目比较紧的情况下想了一个办法,在我们绑定完成之后,客户端调用H5的JS方法告诉他们,是不是很楼比啊。

- (void)webViewDidFinishLoad:(UIWebView *)webView{
    if (!webView.isLoading) {//这个是为了解决我们H5给的页面里多次load的问题
        JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        __weak typeof(self) weakSelf = self;
        SpeakingReadOverJsExport *jsExport = [SpeakingReadOverJsExport new];
        jsExport.delegate = _jsManger;
        _jsManger.webView = webView;
        context[@"TEST"] = jsExport;
        [_jsManger responseHsCallBack:@"init" dict:@{}];//通知H5绑定成功,可以交互了
    }
    DebugLog(@"webview finish");
}

最近仔细想了想其实没有哪家公司的H5是这么玩的,H5的window和controller一样类似,也是有自己的生命周期的,所以就在window. onload 方法里调用客户端的方法就不存在JSContext上下文没有绑定好的问题了,有一点差异的是,绑定的时间节点变成了在webViewDidStartLoad代理方法里执行

- (void)webViewDidStartLoad:(UIWebView *)webView{
    DebugLog(@"webview start");
    JSContext *context= [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    __weak typeof(self) weakSelf = self;
    SpeakingReadOverJsExport *jsExport = [SpeakingReadOverJsExport new];
    jsExport.delegate = _jsManger;
    _jsManger.webView = webView;
    context[@"TEST"] = jsExport;
}

这种有二级window的调用有一个很坑爹的问题就是,取客户端信息的时候,

客户端调用H5的JS方法

        JSContext *context= [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        context.exceptionHandler = ^(JSContext *context, JSValue *exception) {
            [JSContext currentContext].exception = exception;
            DebugLog(@"exception:%@",exception);
        };
        NSDictionary *dict = @{
                               @"status":@(status),
                               };
//调用H5的名为h5Back 的JS方法 ,里面是两个参数,参数一定要加单引号
        NSString *text = [NSString stringWithFormat:@"h5Back('%@','%@')",@"",dict];
        [context evaluateScript:text];

好了,啰里啰嗦就是这么多,欢迎大家讨论补充!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,997评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,603评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,359评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,309评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,346评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,258评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,122评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,970评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,403评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,596评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,769评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,464评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,075评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,705评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,848评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,831评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,678评论 2 354

推荐阅读更多精彩内容