WebKit 框架一瞥(A Look at the WebKit Framework) – Part 1

简介: A Look at the WebKit Framework – Part 1If you’ve ever built an application that required a webview to load web content in your native app, then...


A Look at the WebKit Framework – Part 1



If you’ve ever built an application that required a webview to load web content in your native app, then you have probably experienced the frustrations that came with using UIWebView. UIWebView is quite limited and its performance lags behind that of Mobile Safari. JavaScript, which powers just about every web application, performs poorly in native mobile applications.

However, all of this has changed with the introduction of the WebKit framework in iOS 8. With the WebKit framework, comes WKWebView which replaces UIWebView in UIKit and WebView in AppKit, offering a consistent API across the two platforms.

The WebKit framework enables developers to create web enabled native apps that can utilize the power and Speed of Nitro, which is the JavaScript engine that powers Safari.

webkit-featured

WKWebView boasts of 60fps smooth scrolling, KVO, built-in gestures and native communication between app and webpage.

In the span of two articles, we’ll build two applications which will explore some of the features that WebKit (in particular, WKWebView) offers. For the first application (built in this tutorial), we’ll create a web browser that has some of the functionalities of Safari. The second article will go deeper into Webkit and show the more powerful features like injecting JavaScript into a web page to modify content and extract data.

Getting Started

To get started, create a new project. Make it a Single View Application named Browser and select Swift as the language and make it Universal.

image01

In ViewController.swift import the WebKit framework.

1
import WebKit

Next add the following variable to the class.

1
var webView: WKWebView

Add the following method to the class. It initializes the web view with frame of size zero. Later we’ll add auto layout constraints to the webview so that it adapts to whichever device and orientation it is run on.

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

At the bottom of viewDidLoad() add the following statement which will add the webView to the main View.

1
view.addSubview(webView)

Next add the following constraints at the bottom of viewDidLoad()

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

Here we first disable auto-generated constraints with the first statement and then we define the height and width constraints for the webView. The webView will have the same height and width as its superview’s height and width.

We’ll load a default URL when the app starts. Later we’ll add a text field that the user can use to enter a URL. Add the following at the bottom of viewDidLoad()

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

Run the application. It will load the homepage of Appcoda. When you scroll down, notice that you can see the page scrolling underneath the translucent navigation bar. We’ll disable this. Open Main.storyboard and select the View Controller. In the Attributes Inspector, uncheck the Under Top Bars option in the Extend Edges group. When you run the app again, the nav bar should now be solid and you won’t see the webpage underneath it.

image02

Next we’ll enable url input from the user.

In the storyboard file, drag a view onto the navigation bar in the view controller. In the Attribute Inspector, set its Background to Clear Color. Since you can’t apply auto layout constraints within a navigation bar, we’ll set its size in code.

Open the Assistant Editor and create an outlet for the view by control-dragging from the view to the ViewController class. Name the outlet barView. You should have the following in your code.

1
@IBOutlet weak var barView: UIView!

Add the following to viewDidLoad() after the call to super.viewDidLoad()

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

This sets the size of the barView when the app loads.

Add the following method to the class.

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

This will set the size of the barView when the device orientation changes.

On running the app you should see the view stretched out on the navigation bar. When you change orientations or devices, the view adapt its size accordingly.

Next drag a Text Field onto the view. Using the Pin button at the bottom of the canvas, pin its top, bottom, right and left with a spacing of 0.

image03

Fix the Auto Layout issues by selecting Editor > Resolve Auto Layout Issues > Selected View > Update
Frames

Create an outlet for the text field. Name it urlField. You should have the following.

1
@IBOutlet weak var urlField: UITextField!

We want the view controller to receive UITextFieldDelegate protocol methods, so in the Document Outline, control-drag from the text field to the view controller and select delegate from the popup.

image04

With the text field selected, set the following in the Attributes Inspector.

  • Clear Button: Appears while editing
  • Correction: No
  • Keyboard Type: URL
  • Return Key: Go

Change the class declaration as follows so that it conforms to the UITextFieldDelegate protocol.

1
class ViewController: UIViewController, UITextFieldDelegate

Next add the following UITextFieldDelegate protocol method to the class.

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

This dismisses the keyboard and loads the URL given by the user. Test it with a url. You have to enter the full url, i.e. http://google.com. Since this can be a bit cumbersome to your users, you could write code that checks the url string for ‘http://’ and if not present, appends it to the beginning of the url string, thus allowing users to enter such urls as google.com. We won’t get into this here.

Navigating Through History

Our browser is working but still lacks some features that we’ve come to expect of web browsers, namely loading indicator, back and forward buttons, reload button e.t.c.

With KVO (Key Value Observing) loading progress, page title and URL are now observable properties of WKWebView. You can use these values to update your UI accordingly.

First, we’ll add the back, forward and reload buttons.

In the storyboard, select View Controller and then in the Attributes Inspector, under Simulated Metrics, change Bottom Bar to None.

Drag a Toolbar onto the view and place it at the bottom. Pin its left, right and bottom with a spacing of 0, making sure Constrain to margins is unchecked.

image05

In viewDidLoad() edit the webView’s height constraint to take this into account.

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

Remove the existing button item from the toolbar and drag the following in order: a Bar Button Item, a Fixed Space Bar Button Item, a Bar Button Item, Flexible Space Bar Button Item and a Bar Button Item. The toolbar should look as shown.

image06

Edit the bar button items text to and R respectively. These will be our Back, Forward and Reload buttons. In a real app, it would be better to use icons on the buttons, but for ease, we’ll use text. The toolbar should look as shown.

image07

Create outlets for each of the bar button items. Name them backButton, forwardButton and reloadButton respectively. You should have the following in your code.

1
2
3
@IBOutlet weak var backButton: UIBarButtonItem!
@IBOutlet weak var forwardButton: UIBarButtonItem!
@IBOutlet weak var reloadButton: UIBarButtonItem!

Then create actions for the same buttons and name them back, forward and reload respectively. For each action, change the Type to UIBarButtonItem. You should have the following in your code.

1
2
3
4
5
6
7
8
@IBAction func back(sender: UIBarButtonItem) {
}
   
@IBAction func forward(sender: UIBarButtonItem) {
}
   
@IBAction func reload(sender: UIBarButtonItem) {
}

At the bottom of viewDidLoad() add the following. We don’t want the back and forward buttons to be enabled when the app first loads.

1
2
backButton.enabled = false
forwardButton.enabled = false

Add the following to viewDidLoad() after the constraints are added, and before the code that creates and loads a request. This adds the class as an observer of the loading property.

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

Add the following method to the class. It will be called whenever the observable property changes. The state of the back and forward buttons will be changed according to the current state of the web view.

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

Modify the back(), forward() and reload() functions as shown.

1
2
3
4
5
6
7
8
9
10
11
12
@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)
}

Run the application and test the buttons. The back and forward should be disabled at first. When you navigate to a page, the back button should be enabled. When you go back, the forward button should be enabled. Tapping R should reload the page.

Handling Errors

You can’t always rely on the user to always type in a correct url. We’ll write code to catch errors and notify the user.

First modify the class declaration as shown.

1
class ViewController: UIViewController, UITextFieldDelegate, WKNavigationDelegate

The WKWebView has a property named navigationDelegate which expects an object that conforms to the WKNavigationDelegate protocol. The protocol provides different methods dealing with navigation events, including loading errors.

Add the following to the bottom of init(). With this, the class will be the navigation delegate of the web view.

1
self.webView.navigationDelegate = self

Next add the following method to the class. This is the delegate method that gets called when there is an error.

1
2
3
4
5
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)
}

Run and test the app with an incorrect url.

image08

Displaying Progress

To finish off, we’ll add a progress indicator to the app.

In the storyboard file, drag a Progress View onto the view and place it below the navigation bar. Pin its top, right and left as shown.

image09

Create an outlet for the Progress View and name it progressView. You should have the following.

1
@IBOutlet weak var progressView: UIProgressView!

In ViewController, replace the statement

1
view.addSubview(webView)

with this

1
view.insertSubview(webView, belowSubview: progressView)

In viewDidLoad(), add the following below the statement that calls addObserver() on the webView, before the creation and loading of the url request.

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

Add the following to the bottom of observeValueForKeyPath() after the other if statement.

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

This updates the progress view as the value of estimatedProgress changes or hides it when loading completes.

Add the following method to the class. It is a WKNavigationDelegate protocol method that gets called when the page load completes. We use it here to reset the progress view value after each request.

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

Run the app and you should see a blue progress line as the app loads.

image10

Conclusion

We have looked at the basics of the new WebKit framework. We’ve seen how to add some of the features offered by Safari, namely loading urls, navigating through history, detecting and handling errors and displaying progress. In part 2 of this tutorial, we’ll go deeper into webview and look at how to inject JavaScript into a web page to build a more powerful application.

You can download the code to this tutorial here.




目录
相关文章
|
安全 JavaScript 前端开发
浏览器内核之WebKit 架构与模块
此文章是我最近在看的【WebKit 技术内幕】一书的一些理解和做的笔记。 而【WebKit 技术内幕】是基于 WebKit 的 Chromium 项目的讲解。
632 0
浏览器内核之WebKit 架构与模块
|
Web App开发 存储 JavaScript
webkit 基础
引用:http://blog.csdn.net/milado_nju/article/details/7292131 # WebKit渲染基础 ## 概述 WebKit是一个引擎,而不是一个浏览器,它专注于网页内容展示,其中渲染是其中核心的部分之一。
964 0
|
前端开发 JavaScript API
[WebKit] JavaScriptCore解析--基础篇 (一)JSC与WebCore
先看一下官方的基本介绍,短短几句就塞满了关键字。 SquirrelFish,正式名称是JavaScriptCore,包括register-based(基于寄存器的虚拟机), direct-threaded, high-level bytecode engine(字节码引擎).它使用基于内置copy propagation(复制性传播算法)的一次性编译器(one-pass compiler),能够延迟从语法树(Syntax Tree)上生成字节码(Bytecodes)。
1831 0
|
9月前
|
前端开发 JavaScript 数据可视化
Webkit结构简介
Webkit结构简介
65 0
|
JavaScript 前端开发 测试技术
深入分析WebKit之Layout Tests
转载请注明出处:http://blog.csdn.net/horkychen 来自内部一个分享PPT整理过程的知识点,没有特别组织。   Layout Test主流程: 运行的指令:   run-webkit-tests [...
1190 0
|
Web App开发 JavaScript Unix
WebKit模块化分析
模块化       软件的首要技术使命是管理复杂度(Complexity)。这是中的一个标题。软件本质性困难的根源都在于复杂性。Dijkstra指出没有谁的大脑能容得下一计算机程序。
1164 0
|
存储 编解码 安全
[WebKit]WebKit2多进程机制的解析
在中说到WebKit2中的多进程模型。多进程模型已经是浏览器的基本架构要素,下面展开分析一下WebKit2中的多进程模型。 协作决定接口,确立责任分工后,对于模块或系统间最重要的事莫过于接口定义,而且是有着简洁明确的定义。
1402 0
|
7月前
|
Web App开发 前端开发 JavaScript
深入了解WebKit:简介及工作流程详解
📚 WebKit 是苹果创立的开源浏览器引擎,驱动Safari,且跨多平台。它以快速渲染、标准兼容和开源社区支持著称。WebKit从KHTML发展而来,2003年随Safari推出,2005年开源。关键组件包括WebCore(渲染),JavaScriptCore(JS引擎),DOM,CSS解析器和网络模块。工作流程涉及加载、解析、布局、渲染和绘制。用于测试的工具包括Web Inspector、DumpRenderTree和WebDriver。
139 2
|
7月前
|
Web App开发 缓存 前端开发
WebKit简介及工作流程
WebKit简介及工作流程

热门文章

最新文章