(iOS) WKWebView Tutorial - WKWebView With UIToolbar and UIActivityIndicatorView
When you are working on your app, you might have a situation that you want to load URL and display a website on the screen just like browsers. In this article, I show you how to implement WKWebVithe ew in Swift 4.2.
0. The goal of this app
The expected functionalities of this app are as below.
- Tap the button on the first screen and go to the other screen
- The screen loads the website
- The user can go back to the first screen by tapping the back button
- The screen shows the activity indicator while loading the website
1. Add UIButton on UIViewController in the Main storyboard
Create a new project wherever you like, and add UIButton on the view controller in Main.storyboard. It’s up to you how you add constraints to it. I just place it at the center of the view controller.
2. Add another UIViewController and new swift file, and create a segue between the two controllers
Add another view controller in Main.storyboard and create a new Cocoa Touch Class file called ‘DetailViewController’. Don’t forget to connect the view controller and the new subclass of UIViewController just created.
Also, ‘Ctrl + Click’ to create a segue from the button to the new view controller. Choose ‘Show’ from the options.
3. Add WKWebView and pin it
Add WKWebView (WKWebKit View in the object library) on the DetailViewController and pin it to all edges.
4. Add IBOutlet of the ‘WKWebView’
and write a function to load a webpage
Now it’s about time to start coding. Import WebKit
and add IBOutlet of the WKWebView
. Don’t forget to connect the IBOutlet with the WKWebKit
in the storyboard.
In order to load a website from URL, you can use the load(_ request: URLRequest) -> WKNavigation?
function. In many cases, URL is stored as a String
. Therefore, I put the whole converting process in a single helper method as below.
I also create a constant which stores WKWebView
official document URL and call the helper function inside of viewDidLoad().
import UIKit
import WebKitclass DetailViewController: UIViewController {
@IBOutlet weak var webView: WKWebView! let sampleURL = "https://developer.apple.com/documentation/webkit/wkwebview/" override func viewDidLoad() {
super.viewDidLoad()
sendRequest(urlString: sampleURL)
}
// Convert String into URL and load the URL
private func sendRequest(urlString: String) {
let myURL = URL(string: urlString)
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
}
At this point, the app should be able to load the website. Build and run the app to see if it’s working. If you tap the button on the first view controller, it navigates to DetailViewController and the website should be displayed after a few seconds wait.
5. Add navigation in DetailViewController
Now the app is able to load the website. However, there is no way to go back to the first view controller. Let’s implement the ability to navigate back to the previous controller.
In order to specifically separate the app and website content, I prefer having a toolbar a the bottom of the screen instead of using default navigation bar from iOS framework.
Therefore, my strategy is creating a toolbar with a back button programmatically. Add new methods as below.
import UIKit
import WebKitclass DetailViewController: UIViewController {
@IBOutlet weak var webView: WKWebView! let sampleURL = "https://developer.apple.com/documentation/webkit/wkwebview/" override func viewDidLoad() {
super.viewDidLoad() setTooBar() sendRequest(urlString: sampleURL)
}
// Convert String into URL and load the URL
private func sendRequest(urlString: String) {
let myURL = URL(string: urlString)
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)
}
fileprivate func setToolBar() {
let screenWidth = self.view.bounds.width
let backButton = UIBarButtonItem(title: "Back", style: .plain, target: self, action: #selector(goBack)) let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: screenWidth, height: 44))
toolBar.isTranslucent = false
toolBar.translatesAutoresizingMaskIntoConstraints = false toolBar.items = [backButton]
webView.addSubview(toolBar)// Constraints
toolBar.bottomAnchor.constraint(equalTo: webView.bottomAnchor, constant: 0).isActive = true
toolBar.leadingAnchor.constraint(equalTo: webView.leadingAnchor, constant: 0).isActive = true
toolBar.trailingAnchor.constraint(equalTo: webView.trailingAnchor, constant: 0).isActive = true
} @objc private func goBack() {
if webView.canGoBack {
webView.goBack()
} else {
self.dismiss(animated: true, completion: nil)
}
}
}
The purpose of setToolBar()
is to create a toolbar with a back button. The toolbar has the height of 44 and the same width with the screen. After the toolbar is added, it is pinned to the bottom.
I also add the function called goBack()
as the action for the backButton
. canGoBack
is the property to judge the webView can go back or not. If webView
can go back, the goBack()
method is called. Otherwise, the view controller is dismissed.
6. Add UIActivityIndicator in DetailViewController
We are almost at the end of this tutorial. You might have noticed that it takes a while to load the website. It is much more user-friendly if the app can indicate it is loading. Let’s finish the implementation by adding the activity indicator.
In order to implement the activity indicator, follow the few steps as below.
- Set webView’s
navigationDelegate
toself
inviewDidLoad()
- Add global variables for the activity indicator
- Configure and set the activity indicator
- Show and remove the activity indicator depends on the status
import UIKit
import WebKitclass DetailViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!let sampleURL = "https://developer.apple.com/documentation/webkit/wkwebview/"
private var activityIndicatorContainer: UIView!
private var activityIndicator: UIActivityIndicatorView! override func viewDidLoad() {
super.viewDidLoad() webView.navigationDelegate = self setToolBar() sendRequest(urlString: sampleURL)
}
// Convert String into URL and load the URL
private func sendRequest(urlString: String) {
...
} fileprivate func setActivityIndicator() {
// Configure the background containerView for the indicator
activityIndicatorContainer = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
activityIndicatorContainer.center.x = webView.center.
// Need to subtract 44 because WebKitView is pinned to SafeArea
// and we add the toolbar of height 44 programatically
activityIndicatorContainer.center.y = webView.center.y - 44
activityIndicatorContainer.backgroundColor = UIColor.black
activityIndicatorContainer.alpha = 0.8
activityIndicatorContainer.layer.cornerRadius = 10
// Configure the activity indicator
activityIndicator = UIActivityIndicatorView()
activityIndicator.hidesWhenStopped = true
activityIndicator.style = UIActivityIndicatorView.Style.whiteLarge
activityIndicator.translatesAutoresizingMaskIntoConstraints = falseactivityIndicatorContainer.addSubview(activityIndicator)
webView.addSubview(activityIndicatorContainer)
// Constraints
activityIndicator.centerXAnchor.constraint(equalTo: activityIndicatorContainer.centerXAnchor).isActive = true
activityIndicator.centerYAnchor.constraint(equalTo: activityIndicatorContainer.centerYAnchor).isActive = true
}
fileprivate func setToolBar() {
...
}@objc private func goBack() {
...
}// Helper function to control activityIndicator's animation
fileprivate func showActivityIndicator(show: Bool) {
if show {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
activityIndicatorContainer.removeFromSuperview()
}
}
}extension DetailViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.showActivityIndicator(show: false)
} func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
// Set the indicator everytime webView started loading
self.setActivityIndicator()
self.showActivityIndicator(show: true)
} func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
self.showActivityIndicator(show: false)
}
}
First, I add activityIndicatorContainer
and activityIndicator
as global variables, and set self
as webView.navigationDelegate
in viewDidLoad()
.
Next, I create the setActivityIndicator()
function to configure activityIndicatorContainer
and activityIndicator
.
Then, I add an extension to make DetailViewcontroller
conform to WKNavigationDelegate
. There are three functions provided by WKNavigationDelegate
.
- webView(_ :, didFinish:)
Called when the webView finishes loading. - webView(_ :, didStartProvisionalNavigation:)
Called when the webView starts loading. - webView(_ :, didFail:, withError:)
Called when there is an error while loading.
Inside of these methods, I just put another helper function named showActivityIndicator
to control the activity indicator’s animation. The logic here is that showing the indicator only when webView
is loading, otherwise the indicator is removed from the view.
With this implementation, everything is ready. Now the app should have all the functionalities it needs in this tutorial.
That’s everything! You can also check the sample project for this article here. Happy coding!
Reference:
- WKWebView official document:
https://developer.apple.com/documentation/webkit/wkwebview - WKNavigationDelegate official document:
https://developer.apple.com/documentation/webkit/wknavigationdelegate - Add an activity indicator on a uiwebview iOS:
https://medium.com/@javedmultani16/add-an-activity-indicator-on-a-uiwebview-ios-d1c1dcad4e3d