(iOS) WKWebView Tutorial - WKWebView With UIToolbar and UIActivityIndicatorView

Kenta Kodashima
6 min readJan 7, 2019

--

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.

  1. Tap the button on the first screen and go to the other screen
  2. The screen loads the website
  3. The user can go back to the first screen by tapping the back button
  4. The screen shows the activity indicator while loading the website
The completed app

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.

Add constraints to place the button at the center of the screen

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.

Add new UIViewController and the DetailViewController file

Also, ‘Ctrl + Click’ to create a segue from the button to the new view controller. Choose ‘Show’ from the options.

Create a segue from the button the view controller

3. Add WKWebView and pin it

Add WKWebView (WKWebKit View in the object library) on the DetailViewController and pin it to all edges.

Add WKWebView on the view controller 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 WebKit
class 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 WebKit
class 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.

  1. Set webView’s navigationDelegate to self in viewDidLoad()
  2. Add global variables for the activity indicator
  3. Configure and set the activity indicator
  4. Show and remove the activity indicator depends on the status
import UIKit
import WebKit
class 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 = false
activityIndicatorContainer.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.

The completed app

That’s everything! You can also check the sample project for this article here. Happy coding!

Reference:

--

--

Kenta Kodashima
Kenta Kodashima

Written by Kenta Kodashima

I'm a Software Engineer based in Vancouver. Personal Interests: Books, Philosophy, Piano, Movies, Music, Studio Ghibli.

Responses (4)