Each used UIWebView
iOS developer has deep feelings about its limitations and limited functionality. Xingran, it will change the dilemma after IOS8 launched the WebKit framework. In this article I will go deep into the webkit to experience the benefits it brings us, while also looking at the new iOS9 in the Sfsafariviewcontroller some new surprises.
Common browsing behavior
The so-called general browsing behavior can be summed up in the following several:
Web page Load Progress
Forward
Back off
Refresh
If every app that uses WebView has to do a dedicated controller, it's also a hassle, and I used to do it directly with other Third-party-written packages.
But now, if you're going to be handy with Wkwebview, say it in code:
Class Viewcontroller:uiviewcontroller {var webview:wkwebview!
@IBOutlet weak var progressview:uiprogressview!
Required Init (coder Adecoder:nscoder) {super.init (Coder:adecoder)! instantiated Wkwebview Self.webview = Wkwebview (Frame:cgrectzero)} override func Viewdidload () {Super.viewdi
Dload ()//coding to join Wkwebview View.addsubview (webview) View.insertsubview (WebView, Abovesubview:progressview) Webview.translatesautoresizingmaskintoconstraints = False Let Widthconstraint = Nslayoutconstraint (item:webVie W, attribute:. Width, Relatedby:. Equal, Toitem:view, attribute:. Width, Multiplier:1, constant:0) view.addconstraint (widthconstraint) Let Heightconstraint = Nslayoutconstr Aint (item:webview,attribute:. Height, Relatedby:. Equal,toitem:view, attribute:. Height, Multiplier:1, constant: -46) view.addconstraint (heightconstraint)//Detect changes to WebView object Properties Webvie W.addobserver (self, Forkeypath: "Loading", Options:. New, Context:nil) webview.addobserver (self, Forkeypath: "title", Options:.
New, Context:nil)//Loading web page Let request = Nsurlrequest (Url:nsurl (string: "http://ray.dotnetage.com")!) Webview.loadrequest (Request)} override Func Observevalueforkeypath (keypath:string?, Ofobject Object:anyo
Bject?, change: [String:anyobject]?, context:unsafemutablepointer<void>) {if (KeyPath = = "Loading") { Detection button Availability forwardbutton.enabled = Webview.cangoback backbutton.enabled = Webview.cangoback stopbut Ton.image = webview.loading? UIImage (Name: "Cross"): UIImage (named: "Syncing")} else if KeyPath = "title" {title = Webview.title} else if KeyPath = = "Estimatedprogress" {Progressview.hidden = Webview.estimatedprogress = 1 PROGRESSVIEW.S Etprogress (Float (webview.estimatedprogress), Animated:true)}}}
The code I think there is nothing to say, in addition to Wkwebview can not be visualized through the IB to build, the above code is the AutoLayout part of the code optimization is the. Write a write, do a Example to understand.
Communicating with Javascript
By WebKit, you don't need to communicate with the DOM through a JavaScript bridge. In fact, this is not a new technology, early Windows98 in VB or in Delphi can also use the COM interface in a completely similar manner with the DOM communication.
Don't say much nonsense, talk about the basic principles of WebKit. The following is a schematic diagram of the communication relationship between the web process of WebKit Host and the APP master process:
This contains two processes
Executing JavaScript scripts
We can include JavaScript scripts within the Bundle of the app as application resources. Inject it through WebKit into the target Web page at run time.
First we have to prepare a target page, and here is a sample of my own blog (http://ray.dotnetage.com). Open it in the App with WebKit.
Now, I will be the first page of my blog to change the text of the headline, the specific code is very simple:
$(".page-header h1").text("iOS注入测试");
Then, add a script file called in the iOS project to inject.js
copy the code.
JavaScript scripts included in the App are best executed once in the console of the browser to ensure that the script itself can be executed correctly. If the script contains a potential error, it is not detectable in the app.
Then, create an instance within the Controller's constructor WKWebViewConfiguration
and pass in the constructor as a parameter WKWebView
, as follows:
Viewcontroller.swift
Import WebKit
class Viewcontroller:uiviewcontroller {
var webview:wkwebview!
Required Init (coder Adecoder:nscoder) {
super.init (coder:adecoder)!
Let configuation = Wkwebviewconfiguration ()
Configuation.userContentController.addUserScript (Getuserscript (" Inject "))
Self.webview = Wkwebview (frame:cgrectzero,configuration:configuation)
}
//read from Resource JavaScript script
func getuserscript (fromname:string)-> wkuserscript {let
FilePath = Nsbundle.mainbundle (). Pathforresource (FromName, OfType: "JS") let
js = try! String (contentsoffile:filepath!, encoding:nsutf8stringencoding) return
wkuserscript (Source:js, Injectiontime : . Atdocumentend, Formainframeonly:true)
}
Another point to note in this code snippet is the getUserScript()
object returned by the custom method WKUserScript
. We can injectionTime
decide to inject the script into the beginning of the HTML or at the end of the document.
Execute the code again with the following effect:
That is, we can control all the DOM objects in the page arbitrarily after we inject JavaScript through WebKit in the app!
JavaScript callback
In addition to injecting code into the browser from one end of the app, perform an action. In some cases we also need to do some processing from the Web page, such as reading some elements of the Web page and turning them into a collection of JSON objects, back to App processing. Or maybe our app wants to read all the images in the page one at a time after loading a Web page, and when the user clicks on the images we use the app's local way to preview the screen, and so on. In these contexts:
We all have to return the object from the page.
In other words, to communicate with the app process within the process of a Web page, we need to use the script in:
webkit.messageHandlers.{MessageName}.postMessage([params]);
This method cannot be executed directly in a standard HTML5 browser, such as Chrome and Safair. This object is only available on pages that are WebKit Host webkit
. It's not hard to understand, but WebKit injected this instance into Windows after loading the page webkit
, allowing JavaScript to send information to the app.
If we're going to send a message to the app, such as: After a button is clicked on the page, to execute the code that opens the album in the app, you have to write the code in JavaScript first:
$ ("#mybutton"). Click (function () {
webkit.messageHandlers.openPhotoLibrary.postMessage ();
});
Please note that openPhotoLibrary
This object is not in swift, when this method is sent back to Swift this is just the name of a message, and in Swift to receive this to the browser to send the information our controller needs to implement WKScriptMessageHandler
this interface, there is only one way, Let's take a little more space to open the code for this interface directly:
/*! A class conforming to the Wkscriptmessagehandler protocol provides a for
receiving messages from JavaScript run Ning in a webpage.
*/Public
protocol Wkscriptmessagehandler:nsobjectprotocol {
/*! @abstract invoked when a script message is Rece Ived from a webpage.
@param usercontentcontroller The User Content controller invoking the
delegate method.
@param the script message received.
* *
@available (IOS 8.0, *) public
func Usercontentcontroller (usercontentcontroller:wkusercontentcontroller , Didreceivescriptmessage message:wkscriptmessage)
}
So, we're going to implement this interface directly:
Class Viewcontroller:uiviewcontroller, Wkscriptmessagehandler {
required init (coder Adecoder:nscoder) {
// ... Previous Code IBID.
Configuation.userContentController.addScriptMessageHandler (self, Name: "Openphotolibrary")
Self.webview = Wkwebview (frame:cgrectzero,configuration:configuation)
}
...
Func Usercontentcontroller (Usercontentcontroller:wkusercontentcontroller, Didreceivescriptmessage message: Wkscriptmessage) {
if message.name = = "Openphotolibrary" {
//Here you can add the code to open the album}
}
From the code you can see the principle of one or two:
WKWebView
to addScriptMessageHandler
register a message name with a method before constructing the configuration object, the routine here is "openphotolibrary".
Implements WKScriptMessageHandler
the interface, judging the source of the message from the userContentController()
parameters of the method message.name
, and executing the corresponding code.
In addition, if we need to pass an object to the app from a JavaScript script, you can postMessage()
enter the object as a parameter directly within the method, but usually the type of the argument should be an array or a normal JSON object, so that the app can read it from the Dictionary object.
For example, I read the address and name of all the menus from the current Web page and generated a menus
JavaScript array object:
var menus = $ (". NavBar a"). Map (function (n,i) {return
{
title: $ (N). Text,
Link: $ (n). attr ("href")
};
} );
Webkit.messageHandlers.didFetchMenus.postMessage (menus);
Here we skip the interface implementation and look directly at the implementation of the userContentController
method:
var menus: [menus]?
Func Usercontentcontroller (Usercontentcontroller:wkusercontentcontroller, Didreceivescriptmessage message: Wkscriptmessage) {
if message.name = = "Didfetchmenus" {
if let resultarray = Message.body as? [Dictionary<string,string>] {
menus = resultarray.map{Menu (dict: $)}
//Check out and convert JSON to Swift's Menu object
print (menus)
}
}
}
The Safair browser in iOS9
This new module has been added to the iOS9 SafariServices
to provide a fully functional inline Safair
SFSafariViewController
can be used like a normal controller.
The following is a simple example
Import uikit
Import safariservices
class Viewcontroller:uiviewcontroller {
@IBAction func openbrowser ( Sender:anyobject) {let
safari = Sfsafariviewcontroller (Url:nsurl (string: "http://www.apple.com")!)
Self.showviewcontroller (Safari, sender:self)
}
}
SFSafariViewController
The biggest difference with WebKit is that SFSafariViewController
there is no controllable method, just a controller that can be completely embedded in the app, avoiding the same as before if you open an external link to jump out of the current app, and SFSafariViewController
all the features embedded in Safari and Safari are , the same features as 3D touch and page cutting are also supported. And when our app uses an external network account for integrated login, Safari gets more direct access to the current app's application context without having to jump out of the back and back to app. This is a big boost to the app's experience when it comes to integrating with Safari.
On Apple's developer website, the WebKit and safariservices choices are given:
Select WebKit if you want to interact with a Web page
If you need to have the same experience with Safari and do not need to interact with Web pages to recommend using Safariservices
This is a really good update.