如果你曾经在你的App中使用UIWebView加载网页内容的话,你应该体会到了它的诸多不尽人意之处。UIWebView是基于移动版的Safari的,所以它的性能表现十分有限。特别是在对几乎每个Web应用都会使用的JavaScript,表现的尤为糟糕。

但是,所有的这一切都在iOS 8引入了一个新的框架——WebKit,之后变得好起来了。在WebKit框架中,有WKWebView可以替换UIKit的UIWebView和AppKit的WebView,而且提供了在两个平台可以一致使用的接口。

WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是Safari的JavaScript引擎。

WKWebView保证在滑动时保持60的帧率,同时具有KVO,内建手势,以及在App和网页之间的原生交流方式。

横跨2篇文章,我们即将建立2个App来探索WebKit的功能(特别是WKWebView)。在第一个App当中,我们将建立一个和Safari 功能相似的浏览器。在第二篇文章中,我们会深入到Webkit中去,探索更强大的功能:注入JavaScript到网页以改变内容和获取数据。

开始

打开Xcode,创建一个新的工程。选择Single View Application,取名叫Browser,选择Swift为开发语言,Devices选择Universal。

在ViewController.swift中导入WebKit框架。

import WebKit

将下面的变量加入到类中

var webView: WKWebView

将下面的方法加入到类中。它将会初始化webview并设置其frame为0.稍后我们会使用自动布局(auto layout)来给webview添加约束,这样这个webview就能在任何苹果设备和任何方向上正常工作了。

required init(coder aDecoder: NSCoder) {
self.webView = WKWebView(frame: CGRectZero)
super.init(coder: aDecoder)
}

在viewDidLoad()底部,添加如下语句。这样这个webView就被添加到主视图了。

view.addSubView(webView)

接下来,在viewDidLoad()方法底部添加如下约束

webView.setTranslatesAutoresizingMaskIntoConstraints(false)
let height = NSLayoutConstraint(item: webView, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: , constant: )
let width = NSLayoutConstraint(item: webView, attribute: .Width, relatedBy: .Equal, toItem: view, attribute: .Width, multiplier: , constant: )
view.addConstraints([height, width])

在此处,我们首先禁止了自动约束。然后我们对webview的宽和高添加了约束。这样webview就会和它的superview拥有一样的宽和高了。

在程序启动时,我们将会打开一个默认页。然后我们添加一个textfield控件,这样用户就能输入自己想浏览的地址了。在viewDidLoad()底部添加如下代码

let url = NSURL(string:"http://www.appcoda.com")
let request = NSURLRequest(URL:url!)
webView.loadRequest(request)

运行程序。它将会加载Appcoda的主页。注意当你滑动页面的时候在导航栏下面也能看到模糊的网页,我们将禁止这样。首先打开 Main.storyboard选择网页显示的ViewController。在属性检查(也就是右边的第四栏中)将Extend Edges中得Under Top Bars去掉勾选。再次运行程序,我们就会发现导航栏已经没有了毛玻璃效果,而且我们也不能看见它下面的网页内容了。

接下来让我们添加给用户输入URL的控件。

在storyboard文件中,给向导航栏中拖放一个view。在属性检查(右边第四栏)中,设置其背景色为透明(clear color)。因为我们无法给导航栏中的view添加约束,我们将会以代码的形式调整其大小。

@IBoutlet weak var barView: UIView!

在viewDidLoad()的super.viewDidLoad()之后添加如下代码

barView.frame = CGRect(x:, y: , width: view.frame.width, height: )

这段代码设置了barView的大小。

将下面的方法添加到类

override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
barView.frame = CGRect(x:, y: , width: size.width, height: )
}

这段代码将在设备方向改变时重新设置barView的大小。

接下来运行程序,你可以看见在导航栏中展开的view,如果更改设备方向,这个view也会跟着改变大小。

接下来,拖放一个textfield控件到这个view里面。然后点击画布右下方的Pin按钮(第二个)。将其top,bottom,right,left的距离设置为0,如下图所示。

然后依次点击,Editor-》Resolve Auto Layout Issues-》Selected View-》Update Frame,解决警告。

然后创建一个outlet。取名urlField。你应会看到如下代码。

@IBOutlet weak var urlField: UITextField!

我们希望viewcontroller能成为UITextFieldDelegate的代理,在storyboard的Document Outline中,按下control然后将textfield拖放到viewcontroller,然后在弹出视图选择delegate。

选择textfield控件,在属性检查(右边第四栏)设置如下属性。

Clear Button:Appears while editing

Correction: NO

Keyboard Type:URL

Return Key:Go

在类声明部分添加实现UITextFieldDelegate代码。

class ViewController: UIViewController, UITextFieldDelegate

接下来添加如下UITextFieldDelegate代码

func textFieldShouldReturn(textField: UITextField) -> Bool {
urlField.resignFirstResponder()
webView.loadRequest(NSURLRequest(URL: NSURL(string: urlField.text)!))
return false
}

以上代码会隐藏键盘,然后加载用户输入的url。尝试输入一个url,我们发现。我们必须输入一个完整的url,比如:http://google.com。对我们的用户这有一点麻烦,我们可以检查用户的输入,然后给用户输入的url在必要时添加‘http://’前缀。这里我们不会详述了。

浏览历史

现在我们的浏览器已经能工作了,但是它还缺少了一些浏览器应有的功能。载入进度提示,前进和后退,刷新按钮,等等。

通过KVO(Key Value Observing),我们可以监听WKWebView的载入进度、网页标题和url属性。你可以使用这些来更新你的UI。

首先,让我们添加后退、前进及刷新按钮。

在storyboard中,选择View Controller,点击属性检查(Attributes Inspector),在Simulated Metrics一栏,将BottomBar选择为None。

拖放一个Toolbar到view得底部。添加其left,right和bottom距离为0,并确保Constrans to margins一栏没有被勾选。

在viewDidLoad()方法中调整webView的高度,以显示toolbar。

let height = NSLayoutConstraint(item: webView, attribute: .Height, relatedBy: .Equal, toItem: view, attribute: .Height, multiplier: , constant: -)

移除toolbar中得button,然后按顺序添加以下控件:Bar Button Item、Fixed Space Bar Button Item、Bar Button Item,Flexible Space Bar Button Item和Bar Button Item。toolbar应该是这个样子。

编辑bar button成下面的样子。这些按钮将成为我们的前进后退及刷新按钮。在一个真实的App当中,为这些按钮放上图标将是更好的选择,但是为了简便,我们使用文本。toolbar接下来应该是这个样子。

为每一个bar button创建一个outlet。取名为backButton,forwardButton和reloadButton。你应该会得到如下代码

@IBOutlet weak var backButton: UIBarButtonItem!
@IBOutlet weak var forwardButton: UIBarButtonItem!
@IBOutlet weak var reloadButton: UIBarButtonItem!

然后为每个按钮依次分别创建back,forward和reload方法。将每个action的Type更改为UIBarButtonItem。你应该会得到如下代码。

@IBAction func back(sender: UIBarButtonItem) {}

@IBAction func forward(sender: UIBarButtonItem) {}

@IBAction func reload(sender: UIBarButtonItem) {}

在viewDidLoad()的底部添加如下代码。我们不希望后退和前进按钮在App被启动时就可点击。

backButton.enabled = false
forwardButton.enabled = false

在viewDidLoad()的约束条件添加代码之后,创建并载入一个任务代码之前,添加如下代码。这句代码使得这个类成为了loading属性的监听者。

webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)

添加下面的方法到类。这个方法将会在可监听的属性变化时执行。后退和前进按钮将根据当前webview的状态来决定是否可被点击。

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer) {
if (keyPath == "loading") {
backButton.enabled = webView.canGoBack
forwardButton.enabled = webView.canGoForward
}
}

修改back(),forward()和reload()方法。

@IBAction func back(sender: UIBarButtonItem) {
webView.goBack()
} @IBAction func forward(sender: UIBarButtonItem) {
webView.goForward()
} @IBAction func reload(sender: UIBarButtonItem) {
let request = NSURLRequest(URL:webView.URL!)
webView.loadRequest(request)
}

运行并测试这些按钮。后退和前进按钮开始时应该不可点击。当你浏览一个页面时,后退按钮应该可被点击。当你后退时,前进按钮应该可被点击。点击R按钮,会重新加载页面。

处理错误

我们不能保证用户总是输入正确地url,我们需要写代码来获取这一错误并提示用户。

首先修改类声明,如下所示:

class ViewController: UIViewController, UITextFieldDelegate, WKNavigationDelegate

WKWebView有一个属性navigationDelegate,接受一个实现WKNavigationDelegate协议的对象。这个协议提供了多种方法来处理导航事件,包括载入错误。

在init()底部添加如下代码。通过下面的代码,我们将这个类设置成了webview的navigation代理。

self.webView.navigationDelegate = self

接下来添加如下方法。这个代理方法将会在有错误发生时被调用。

func webView(webView: WKWebView!, didFailProvisionalNavigation navigation: WKNavigation!, withError error: NSError!) {
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: nil))
presentViewController(alert, animated: true, completion: nil)
}

运行程序,输入一个错误的url测试一下。

显示进度

最后,我们将会添加一个进度显示。

在storyboard文件中,在导航栏下方添加一个progress view。设置其top,right和left如下所示。

为progress view创建一个outlet。你应该会得到下面的代码。

@IBOutlet weak var progressView: UIProgressView!

在ViewController中,替换西面的代码

view.addSubview(webView)

view.insertSubview(webView, belowSubview: progressView)

在viewDidLoad()中创建和载入一个url任务之前添加下面的代码,调用webview的addObserver方法。

webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)

在observeValueForKeyPath()方法中的其他if语句之后添加如下代码。

if (keyPath == "estimatedProgress") {
progressView.hidden = webView.estimatedProgress ==
progressView.setProgress(Float(webView.estimatedProgress), animated: true)
}

这段代码将会更新progressview的进度,如果加载完毕会隐藏progressview。

在类中添加如下代码。这是一个WKNavigationDelegate的代理方法,将会在页面载入完毕后执行。当一个任务完成后,我们使用它来重置progress view的进度。

func webView(webView: WKWebView!, didFinishNavigation navigation: WKNavigation!) {
progressView.setProgress(0.0, animated: false)
}

运行程序,当网页加载时,你将会看到一个蓝色的进度条。

总结

我们已经了解了WebKit的基础部分。我们看到如何添加一些和Safari相似的功能,如载入url,浏览历史,检测错误,展示进度。

Swift - WebKit示例解读的更多相关文章

  1. WebKit示例解读

    如果你曾经在你的App中使用UIWebView加载网页内容的话,你应该体会到了它的诸多不尽人意之处.UIWebView是基于移动版的Safari的,所以它的性能表现十分有限.特别是在对几乎每个Web应 ...

  2. 【hyperscan】示例解读 pcapscan

    示例位置: <hyperscan source>/examples/pcapscan.cc参考:http://01org.github.io/hyperscan/dev-reference ...

  3. 【hyperscan】示例解读 simplegrep

    示例位置: <hyperscan source>/examples/simplegrep.c参考:http://01org.github.io/hyperscan/dev-referenc ...

  4. 示例解读Java的跨平台原理

    首先简单的解释一下Java跨平台的特征,相当于说写一个Java程序论述上可以运行在不同的操作系统平台上面(此处的平台我们就简单的看成是操作系统平台).下面我们用一些事例来说明它的好处. 我们先了解一些 ...

  5. js截取中英文字符串、标点符号无乱码示例解读

    <script> function subString(str, len, hasDot) { var newLength = 0; var newStr = ""; ...

  6. iOS集成丁香园DXY OAuth 登陆 swift代码示例

    问:iOS集成OAuth登陆分几步? 答:和把大象放冰箱里一样. 第一步:打开webview,跳转到登陆页面: let url = "https://auth.dxy.cn/conn/oau ...

  7. delphi ICS控件示例解读

    {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Author: Fran鏾is PIETTE ...

  8. appium日志示例解读

    http://www.colabug.com/thread-1048952-1-1.html

  9. Swift语言快速入门

    Swift语言快速入门(首部同步新版官方API文档和语法的Swift图书,确保代码可编译,作者专家在线答疑,图书勘误实时跟进) 极客学院 编著   ISBN 978-7-121-24328-8 201 ...

随机推荐

  1. perl 面向对象编程

    今天看到一个perl面向对象编程的例子,充分体现了如何对数据进行封装: 自己模仿写一个读取配置文件的例子, 配置文件的内容如下 samtools_binary = /usr/bin/samtools ...

  2. 在 SharePoint 2010 打开网页出错时,显示实际的错误信息

    每次打开错误页面时,SharePoint出错显示的都是友好信息“发生意外错误”,而且友好到您基本上无法明白到底出什么错,怎么办? 修改Web.config文件配置就可以了,就可以显示更详细的信息: 1 ...

  3. Windows性能监视器

    前端时间在做动态目标监控,应用程序需要考虑大数据量和多用户并发的操作,系统的性能是解决方案实施性的重要度量.这里简单的记录下Windows中性能监视查看和简单分析. 一般操作系统性能主要涉及到的问题主 ...

  4. 基于Nginx反向代理及负载均衡

    基于Nginx反向代理及负载均衡 参考:http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass 只要没有被启用,默认就是 ...

  5. CSS样式中” 大于号”

    CSS样式中” 大于号” 在一段CSS代码中见到一个大于号(>),代码如下: BODY#css-zen-garden > DIV#extraDiv2 { BACKGROUND-IMAGE: ...

  6. 工作流JBPM_day01:5-管理流程定义3点改进

    工作流JBPM_day01:5-管理流程定义3点改进 1.打包多个文件上传 再部署一个 查询所有看看,旧版本也查出来了 2.查询时只查询所有最新的版本 3.删除指定名称指定key的所有的它的版本

  7. hadoop程序MapReduce之SingletonTableJoin

    需求:单表关联问题.从文件中孩子和父母的关系挖掘出孙子和爷奶关系 样板:child-parent.txt xiaoming daxiong daxiong alice daxiong jack 输出: ...

  8. 【RF库Collections测试】Set To Dictionary

    Name:Set To DictionarySource:Collections <test library>Arguments:[ dictionary | *key_value_pai ...

  9. sql 链接符 ||

  10. N76E003学习之路(二)

    最近一直在想N76E003和STM8M003的对比情况,在网上找了不少资料,看了不少文档,具体总结如下: STM8S003F3P6:一共20个脚,最多支持16个GPIO,支持16个外部中断:2个16位 ...