0
点赞
收藏
分享

微信扫一扫

UIWebView 打开新tab

酷子腿长一米八 2021-10-15 阅读 23
日记本

Web知识

在此之前,最好还是先了解一下,Web开发中,是如何打开新页的。它有两种方式:

  • <a target='_blank' href='url'/>
  • window.open(url)

为什么它们行为相似,它们之间必有关联(我查过了:joy:)
结论是target属性指定了在何处打开链接,它可以指定为以下的值

_blank - 在新窗口或tab打开链接
_self - 在当前frame打开链接
_parent
_top
framename

a标签需要明确指定target=_blank,因为它默认是 _self
而window.open()默认 _blank,所以默认是打开新页的。它也可以指定target的,它的完整定义是window.open(URL,target,specs,replace)

那接下来,我们的问题可换为,如何在UIWebView里识别链接的target属性

正文

UIWebView 有一个代理对象,可以在网页加载过程中收到回调,获取一些信息。从UIWebViewDelegate中看看,它有哪些代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- (void)webViewDidStartLoad:(UIWebView *)webView;
- (void)webViewDidFinishLoad:(UIWebView *)webView;
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;

很快我们就找到了入口

- (BOOL)webView:shouldStartLoadWithRequest:navigationType:

这个方法会在链接加载之前被调用,target判断只能从它的两个参数入手了request&navigationType

navigationType是一个枚举值,能识别出是否是超链接、表格提交、前进后退、刷新、表格重复提交,但就是没有target或frame之类的识别?
全村的希望只能放在request的身上了,但它只是个标准的http request,根本不会携带frame这种UI层的信息...

好在Google是万能的:joy:,我们还是找到了解决方案:
希望还是在request,我们可以让打开新页的request.url携带上一些特殊的信息...

  • 对于a标签,当target=_blank时,修改href,给它拼上一个自定义协议名,例如
    <a href='http://example.com'>
    ↓↓↓↓↓
    <a href=' ftnewtab: http://example.com'>
  • 对于window.open(),重写系统方法,判断target=_blank时,给url拼上同样的自定义协议,然后用location.href方式加载它

这之后,当收到一个链接形如ftnewtab:url的request时,就可以明确,嗯,这是个要打开新页面的链接

修改URL

接下来且看如何修改 a.href 以及重写 window.open()

这样的事当然是要交给强大的JS了

  1. 定义JS function

    // 修改a.href
    function MyIPhoneApp_ModifyLinkTargets(newtabScheme) {
    var allLinks = document.getElementsByTagName('a');
    if (allLinks) {
    var i;
    for (i=0; i<allLinks.length; i++) {
    var link = allLinks[i];
    var target = link.getAttribute('target');
    if (target && target == '_blank') {
    link.setAttribute('target','_self');
    link.href = newtabScheme+':'+escape(link.href);
    }
    }
    }
    }

    // 重写window.open
    function MyIPhoneApp_ModifyWindowOpen(newtabScheme) {
    window.open =
    function(url,target,param) {
    if (url && url.length > 0) {
    if (!target) target = "_blank";
    if (target == '_blank') {
    location.href = newtabScheme+':'+escape(url);
    } else {
    location.href = url;
    }
    }
    }
    }
  2. 在WebView加载结束时,注入以及调用这两个函数

    // OC Code
    - (void)webViewDidFinishLoad:(UIWebView *)webView {
    // 注入js方法
    [webView stringByEvaluatingJavaScriptFromString:FTNewTabJSCode];

    // 调用js方法
    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyLinkTargets('ftnewtab')"];
    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyWindowOpen('ftnewtab')"];
    }

处理URL

经过JS注入后,发起一个新页请求时,就会得到一个特殊的URL了,当然也需要特殊处理一下

// OC Code
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSURL *URL = request.URL;
if ([URL.scheme isEqualToString:'ftnewtab'] == NO) {
// 不是新开页,正常执行
return YES;
}

NSString *baseURLString = [self stringByEvaluatingJavaScriptFromString:@"document.location.href"];
NSURL *baseURL = [NSURL URLWithString:baseURLString];
NSString *urlString = [[url resourceSpecifier] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
url = [NSURL URLWithString:urlString relativeToURL:baseURL];
[self openNewTabWithURL:url];
return NO;
}

优化

  • 实际运行中,webViewDidFinishLoad会等到html、css、js、image等所有资源文件下载完成后再调用。而实际上,页面早已部分渲染,此时的a标签已可点击。所以会出现js还未注入却被用户意外点击了开新页的请求。针对这种情况,我们将JS注入提前到了webViewDidStartLoad

    - (void)webViewDidStartLoad:(UIWebView *)webView {
    [webView injectAndExecuteNewTabJSCode];
    }
举报

相关推荐

0 条评论