WebKit on the iPhone

简介: 如果你开发一个应用程序,它是显示一个web页面或HTML文件,您可以使用WebKit框架,这是MacOS和iPhone OS的一部分。 但是即使再mac上,webkit的框架也提供了奖金160个公共的头文件甚至有更多的类和方法,你可以使用他控制很多东西,包括加载,渲染显示和修改页面。但iPhone上仅仅给我们了一个类(UIWebView)来控制这一系列错作。尽管UIW

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

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


一些例子:

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

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

它不允许访问HTML

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


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


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


如果你看一下可用的方法,只有一个,这将允许访问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我们做类似的事情。

所以每当我们需要得到titleURLweb页面,显示在一个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.




目录
相关文章
|
3月前
|
自然语言处理 前端开发 JavaScript
WebKit的使用技巧
WebKit的使用技巧
|
Web App开发 JavaScript iOS开发
iOS Safari 浏览器 100vh 带有滚动条解决方案
iOS Safari 浏览器 100vh 带有滚动条解决方案
547 0
|
Web App开发 移动开发 前端开发
关于 Safari 100vh 的问题与解决方案
关于 Safari 100vh 的问题与解决方案
1494 0
关于 Safari 100vh 的问题与解决方案
|
前端开发 iOS开发 Android开发
移动端 Retina屏border实现0.5px
首先来看一下造成Retina边框变粗的原因 其实这个原因很简单,因为css中的1px并不等于移动设备的1px,这些由于不同的手机有不同的像素密度。在window对象中有一个devicePixelRatio属性,他可以反应css中的像素与设备的像素比。
1561 0
|
传感器 编解码 算法
拿到iPhone X 后,你首先要做的10件事!
据 iPhone X 全球正式发售也近两周了,国内外媒体对这款代表着苹果未来10年发展方向的新机,已有了不少评测和总结,科技君今天就给各位果粉小伙伴们整理下有关 iPhone X 上手的十个必备技巧,助你发挥出它全部的潜力和乐趣。
198 0
拿到iPhone X 后,你首先要做的10件事!
|
Web App开发 iOS开发 JavaScript
iOS Safari/WebKit对DeviceMotionEvent的实现
请先阅读《iOS Safari/WebKit对DeviceOrientationEvent的实现》,本文是姐妹篇。 简单地描述一下区别,后面会更详细对比: DeviceOrientationEvent是获取方向,得到device静止时的绝对值; DeviceMotionEvent是获取移动速度,得到device移动时相对之前某个时间的差值比。
1470 0
|
Web App开发 iOS开发 内存技术
苹果允许Flash程序在iPad和iPhone中使用
北京时间11月5日,据国外媒体报道,苹果已经允许某些Flash视频播放程序在iPad和iPhone中使用。Skyfire2.0售价2.99美元,受欢迎程度在苹果Safari浏览器之上。有开发者在Skyfire2.0演示视频中称,当一个嵌有Flash视频的页面被浏览时,该视频会在一个独立的部件中播放,观看效果毫无差异。
1002 0
|
Shell iOS开发
Iphone攻与防-一
iphone-越狱机Hook APP 价值
1629 0