WebKit on the iPhone

如果你开发一个应用程序,它是显示一个web页面或HTML文件,您可以使用WebKit框架,这是MacOS和iPhone
OS的一部分。

但是即使再mac上,webkit的框架也提供了奖金160个公共的头文件甚至有更多的类和方法,你可以使用他控制很多东西,包括加载,渲染显示和修改页面。但iPhone上仅仅给我们了一个类(UIWebView)来控制这一系列错作。尽管UIWebView用的是相同的WebKit组建,但mac是作为公有的API,而iPhone是作为私有的API,所以不能使用。很少的UIWebView的方法足够漂亮的文本,但是对于一个浏览器(iCab Moblie)或者其他基于网页的应用这是不够的,缺失很多的必要的方法。

一些例子:

UIWebView没有提供一个方法来获取当前显示的标题web页面,

它只是忽略所有试图打开链接,旨在打开新窗口或选项卡

它不允许访问HTML树

WebKit本身为所有这些任务提供了许多类,但他们都是私有的和iPhone所不具备的。

一些在应用商店的更改的浏览器都刚刚宣布这些限制作为一种特性(例如他们广告无法打开新窗口或标榜为“没有烦人的弹出式窗口”)。这听起来很不错,但现实使用中,这并不使这样的浏览器很有用。

所以我们能做些什么来克服这些局限性的UIWebView类?在Mac在iPhone没有违反与苹果iPhone
SDK协议下,我们可以(重新)实现所有很酷的特性是可用的WebKit框架,让iPhone和mac一样。不幸的是,我们不能。但我们可以实现许多丢失的特性。

如果你看一下可用的方法,只有一个,这将允许访问web页面的内容,这是或多或少的惟一办法回丢失的特性。和这种方法是

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

这个方法是用来执行JavaScript代码上下文中的当前web页面,并返回一个字符串作为结果。这意味着我们必须使用JavaScript代码来实现我们需要的特性。

让我们先从一些非常容易。我们实现方法来获取标题和URL的当前显示的web页面。我们实现这个作为一个Objective-C“类别”,所以我们不需要子类UIWebView:

File: MyWebViewAdditions.h

@interface UIWebView (MyWebViewAdditions)
- (NSString*)title;
- (NSURL*)url;
@end

File: MyWebViewAdditions.m

#import "MyWebViewAdditions.h"

@implementation UIWebView (MyWebViewAdditions)

- (NSString*)title
{
    return [self stringByEvaluatingJavaScriptFromString:@"document.title"];
}

- (NSURL*)url
{
    NSString *urlString = [self stringByEvaluatingJavaScriptFromString:@"location.href"];
    if (urlString) {
        return [NSURL URLWithString:urlString];
    } else {
        return nil;
    }
}

@end

我们现在在做什么呢?

从JavaScript的观点是,一个网页所代表的“文档”对象有几个属性。一个属性是“title”包含页面的标题。所以用“document.title”我们可以在JavaScript访问文档的标题。而这正是我们需要传递参数的方法”stringByEvaluatingJavaScriptFromString:“获得文档标题。

对于URL我们做类似的事情。

所以每当我们需要得到title或URL的web页面,显示在一个UIWebView对象,我们只需要调用“title”或“URL”方法:

NSString *title = [anyUIWebViewObject title];

接下来我们想做的限制可能是想地址是不能打开链接将打开一个新窗口。在Mac上的WebKit只会调用一个委托方法的主机应用程序请求一个新的WebView对象创建一个URL请求。应用程序将创建一个新的WebView对象并加载新页。但是在iPhone的UIWebView并不支持这样一个委托方法,因此所有试图打开一个链接只是忽略。

这些链接做通常看起来像这样:

<a href="destination" target="_blank">Link Text</a>

The “target”
attribute defines where the link will open. The value can be a name of a frame (if the web page has frames), the name of a window or some reserved target names like “_blank”
(opens a new window), “_self”
(the window itself), “_parent”
(the parent frame, if there are nested frames) and “_top”
(the top-level or root frame, or identical to “_self”
if the page doesn’t use frames).

As a first step, we want to tap on a such a link in our iPhone App, and the link should open like any other normal link in the same UIWebView object. What we need to do is simple: we need to find all links with a “target”
attribute set to “_blank”
and change its value to “_self“.
Then the UIWebView object will no longer ignore these links. To be able to modify all of the link targets we have to wait until the page has finished loading and the whole web page content is available. Fortunately UIWebView provides the delegate method

- (void)webViewDidFinishLoad:(UIWebView *)webView;

which will be called when the web page has finished loading. So we have everything we need: We get notified when the page has loaded, and we know a way to access and modify the web page content (using “stringByEvaluatingJavaScriptFromString:“).

First we write our JavaScript code. Because this will be a little bit more code than what was needed to get the document title, it’s a good idea to create an extra file for our JavaScript code and then we add this file to the resources of our project in XCode:

File: ModifyLinkTargets.js:

function MyIPhoneApp_ModifyLinkTargets() {
    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');
            }
        }
    }
}

What is this JavaScript function doing, when called?
It gets an array of all links (“a”
tags) and then loops through all of these tags, checks if there’s a target attribute with the value “_blank“.
If this is the case it changes the value to “_self“.

Note: There are other tags which can have a “target”
attribute, like the “form”
tag and the “area”
tag. So you can use the “getElementsByTagName()”
call to get these tags as well and modify their target attributes in the same way as I’ve done this for the “a”
tag.

In our iPhone App we need to define a delegate for the UIWebView object and this delegate object will be called whenever the web page has finished loading. This is the method that is called in the delegate by the UIWebView object:

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"ModifyLinkTargets" ofType:@"js"];
    NSString *jsCode = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

    [webView stringByEvaluatingJavaScriptFromString:jsCode];

    [webView stringByEvaluatingJavaScriptFromString:@"MyIPhoneApp_ModifyLinkTargets()"];
}

What is this code doing?
At first the access path of the JavaScript file we created before is retreived from the application bundle and we load the content of the file (the JavaScript code) into a string. Then we execute/inject this code into the web page and finally we call out JavaScript
function
which is modifying the link targets.

Some notes:

  • Getting the JavaScript file from the application bundle and loading it into a string should be usually done somewhere in the init methods of your UIWebView delegate object. This way the string with our JavaScript code is only loaded once and can be simply reused
    whenever a new link is clicked and a new page is loaded.
  • Using a long name for our JavaScript function which also includes a prefix like “MyIPhoneApp_”
    makes it unlikely that the code we inject into a web page will interfere or confict with functions and variables which the web page itself has already defined for its own purposes. This is especially important when we modify web pages we haven’t created ourselves
    and where we can not predict which function or variable names the JavaScript code of the web page is already using.
  • Using separate calls of “stringByEvaluatingJavaScriptFromString”
    to first injecting our own JavaScript code and then calling our own JavaScript function to start modifying the link targets seems to be more complicated that necessary. And for this simple example you would be right. But it is likely that you’ll define much
    more additional JavaScript functions for many different tasks as well. Some of the tasks are started when the page has finished loading (like modifying the link targets), but some tasks will be started later and maybe even multiple times. And so it makes much
    sense that injecting the code and calling the functions are done in separate calls.
  • The delegate method “webViewDidFinishLoad:(UIWebView
    *)webView” is called for each frame, not only when the page itself has finished loading. This means that this delegate method can be called multiple times while a single web page is loaded. I think that this can be called a bug in the iPhone OS, but
    nevertheless it is important to know. When you modify the web page, be aware that this might be done multiple times and so make sure that none of your modifications will have bad side effectes when being modified a second time.

What next?

  • The above example code does not cover web pages where new windows are opened using JavaScript.
  • The links will open in the same window, which is fine because they are no longer ignored. But they still don’t open in a new window or Tab.

More about this topic and the cases which are not yet covered will come in the second part of the “WebKit on the iPhone” article.

Feel free to ask questions and write comments. I’d like to get some feedback.

时间: 2024-10-26 05:32:27

WebKit on the iPhone的相关文章

Web是开源最大的成功

  开源运动广受欢迎,并且在软件开发史上写下了浓重一笔.但是它影响最深远的地方在哪呢?有史以来,最成功的开源"项目"又是什么呢? 事实上,总体来看,Web不就是开源运动最大的成功么? 可能最有名的例子就是隐藏于众多网站背后的LAMP,也就是Linux.Apache.MySQL和PHP.但当你仔细考虑后,你会发现更多. 下面列出了Web得以运转的一些开源项目. Web browsers - 网络浏览器 在网络浏览器市场中,虽然微软的封闭源码软件IE浏览器仍然占有很大的份额,但其它功能相似

如何制作一个HTML5的iPhone应用程序

在过去的一年里,你是不是很沮丧,对于所有的使用Objective-C开发iPhone程序的开发者而言,日子都不那么好过,你是不是为了学习开发iPhone应用程序曾经硬着头皮去读着那生涩难懂的学习教程,事实是Objective-C是一门类似C语言的语言,这也就绝定了它不是那么容易学习. 我不是劝你去放弃学习Objective-C,因为世上无难事,只怕有心人.但是条条大路通罗马,也许你转换下思路你可以用另一种方法达到相同的目的. 你可以制作一个原生的iPhone应用程序去模仿其他的程序,多半上它也许

针对webkit的HTML, CSS和Javascript

前面有一篇文章介绍了HTML5的一些新特性以及技巧, 现再来总结一些更多的针对webkit的HTML, CSS和Javascript方面的特性. HTML, 从HTML文档的开始到结束排列: XML/HTML Code复制内容到剪贴板 <meta name="viewport" content="width=device-width, initial-scale=1.0″/>    <!--让内容的宽度自适应为设备的宽度, 在做Mobile Web时必须加的

简析Chrome和Webkit的渊源

互联网的浪潮从未停息,而用以网上冲浪的冲浪板也一直在变得愈加精良.自人们进入互联网时代以来,即已经发生了三次浏览器大战.第一次浏览器大战的主角是IE和Netscape,最终IE凭借着Windows的庞大身躯推倒了Netscape;第二次浏览器大战Netscape浴火重生化身为火狐狸Firefox,一口咬了IE身上一大块肥肉;正在Firefox和IE正缠绵不息之时,突然凭空杀出个Chrome--这名出身豪门Google的小伙子一下子成长得额外精壮,上串势头凶猛,追得两位前辈娇喘吁吁. Chrome

[译] iPhone X 网页设计

本文讲的是[译] iPhone X 网页设计, 原文地址:Designing Websites for iPhone X 原文作者:Timothy Horton 译文出自:掘金翻译计划 本文永久链接:github.com/xitu/gold-m- 译者:Hyde Song 校对者:Larry Vernon 在最新发布 iPhone X 的全面屏上,Safari 可以精美地显示现有的网站.内容自动嵌入到显示屏的安全区域内,以免被圆角.原深感摄像头系统的空间遮挡住. 凹槽部分填充了页面的 backg

iphone开发笔记

  退回输入键盘   - (BOOL)textFieldShouldReturn:(id)textField{     [textField resignFirstResponder]; }   CGRect CGRect frame = CGRectMake (origin.x,origin.y, size.width, size.height):矩形 NSStringFromCGRect(someCG) 把CGRect结构转变为格式化字符串: CGRectFromString(aString

【原】[webkit移动开发笔记]之如何去除android上a标签产生的边框

去年年底,做完最后一个项目就可以开开心心回家,可是在测试阶段,发现了不少bug,为了不影响回家时间,加班加点也要解决这些问题,这里算是工作回忆,也算是工作的一点小总结. 在ios4+和android2+系统,当手指触摸屏幕a标签链接或按钮时,会产生不同的效果,对于ios点击元素的时候,就会出现一个半透明的灰色背景:对于android则出现红色的边框.对这2个系统自带的效果,这种体验的意义无非为了告知用户按钮已经点击到,带来的价值是好的.可惜带来了体验的同时,也带来了bug...... 主要是在a

《深入理解Android》一2.2 浏览器和WebKit简史

2.2 浏览器和WebKit简史 和HTML标准相伴至今,浏览器从无到有,从简单到复杂,经历了许多变化,内容越发丰富,功能日益强大. 第一个浏览器也是由World Wide Web发明人Tim Berners-Lee于1990年发明,恰好最开始它的名字就叫World Wide Web,后来为避免浏览器软件与万维网技术重名而改称Nexus.第一个易于使用并大规模流行的浏览器是Marc Andreesen于1993年发明的Mosaic,他又成立了网景公司,在1994年推出了赫赫有名的Netscape

今年的移动Pwn2own破解大赛:iPhone+安卓=50万美元

移动Pwn2Own黑客大赛2016重装上阵,这次为安全研究人员奉上的,是25万美元的iPhone解锁奖励和25万美元的安卓手机破解奖金. 今年年初,惠普企业(HPE)将零日计划(ZDI)转手卖给了趋势科技,随着赞助商的转换,Pwn2Own大赛也经历了一些改组.Pwn2Own大赛浏览器版已于今年3月由惠普和趋势科技联手举办.将于10月举行的2016 移动Pwn2Own黑客大赛,则是首届没有HPE赞助的Pwn2Own. 趋势科技漏洞研究高级经理布莱恩·戈伦茨说:"对我们而言,这依然是Pwn2Own.