Day 61 - 100 Days of Swift
Project 16 (part 2)
Day 61 is the second part of the sixteenth project. You review some of the MapKit
stuff you learned yesterday, and he gives you three challenges. The first challenge is to change the tint of the pins on the map. The second is to present an alert controller that allows the user to change the style of the map. And the third is to present a new view controller with a web view that will load the related Wikipedia article when the user taps on the info button in the callout.
For the first one, I type-casted the view returned from dequeueReusableAnnotationView
as an MKPinAnnotationView
, and then set the pinTintColor
:
1
2
3
4
5
6
// In mapView(_:viewFor)
var annotationView = mapView
.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
// right before the return
annotationView?.pinTintColor = .cyan
It looks like this:

For the second challenge, I wrote an intermediary enum
to hold the options I wanted to present the user:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
enum MapType: String, CaseIterable {
case standard = "Standard"
case muted = "Muted"
case hybrid = "Hybrid"
case satellite = "Satellite"
func mapType() -> MKMapType {
switch self {
case .standard:
return .standard
case .muted:
return .mutedStandard
case .hybrid:
return .hybrid
case .satellite:
return .satellite
}
}
}
Then I used that to build a UIAlertController
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@objc private func presentMapTypeAlertController() {
let alertController =
UIAlertController(title: "Map Display Type",
message: "How would you like to display the map?",
preferredStyle: .actionSheet)
for mapType in MapType.allCases {
let action = UIAlertAction(title: mapType.rawValue,
style: .default,
handler: changeMapType)
alertController.addAction(action)
}
let cancelAction = UIAlertAction(title: "Cancel",
style: .cancel)
alertController.addAction(cancelAction)
present(alertController, animated: true)
}
Then I just wrote the handler function:
1
2
3
4
5
private func changeMapType(_ action: UIAlertAction) {
let mapType = MapType(rawValue: action.title!)!
mapView.mapType = mapType.mapType()
}
Using this method allows me to change the titles to whatever I want, while still being hooked up to the options in MKMapStyle
. It also lets me present only the options that I want to present. It looks like this:

For the third challenge I pulled in a modified version of the view controller we wrote in Project2:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
import UIKit
import WebKit
class WebViewController: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
var progressView: UIProgressView!
var selectedItem: String?
override func loadView() {
// Change the default view to be a WKWebView
webView = WKWebView()
webView.navigationDelegate = self
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.isToolbarHidden = true
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
if keyPath == "estimatedProgress" {
progressView.progress = Float(webView.estimatedProgress)
}
}
func setupViews() {
// Set up the progress view
progressView = UIProgressView(progressViewStyle: .default)
view.addSubview(progressView)
progressView.translatesAutoresizingMaskIntoConstraints = false
progressView.leadingAnchor
.constraint(equalTo: view.leadingAnchor)
.isActive = true
progressView.trailingAnchor
.constraint(equalTo: view.trailingAnchor)
.isActive = true
progressView.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
.isActive = true
let keyPath =
#keyPath(WKWebView.estimatedProgress)
webView.addObserver(self,
forKeyPath: keyPath,
options: .new,
context: nil)
// Set up the toolbar
let backward =
UIBarButtonItem(title: "←",
style: .plain,
target: webView,
action: #selector(webView.goBack))
let spacer1 =
UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil,
action: nil)
let refresh =
UIBarButtonItem(barButtonSystemItem: .refresh,
target: webView,
action: #selector(webView.reload))
let spacer2 =
UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
target: nil,
action: nil)
let forward =
UIBarButtonItem(title: "→",
style: .plain,
target: webView,
action: #selector(webView.goForward))
toolbarItems = [backward, spacer1, refresh, spacer2, forward]
navigationController?.isToolbarHidden = false
// Load a default website
if let item = selectedItem {
title = item
let url = URL(string: "https://wikipedia.org/wiki")?
.appendingPathComponent(item)
webView.load(URLRequest(url: url!))
webView.allowsBackForwardNavigationGestures = true
}
}
// MARK: - WK Navigation Delegate
func webView(_ webView: WKWebView,
didFinish navigation: WKNavigation!) {
title = webView.title
}
}
I got rid of the stuff that checks what url they are going to, because I don’t really care. I also hardcoded the url for Wikipedia, and used appendingPathComponent
instead of just appending the string, because that will url-encode the spaces and whatnot (e.g. in “Washington DC”).
Then I just needed to get rid of the alert controller when the info button is tapped, and replace it with this new view controller:
1
2
3
4
5
6
7
8
// In mapView(_:annotationView:calloutAccessoryControlTapped:)
let placeName = capital.title
let webViewController = WebViewController()
webViewController.selectedItem = placeName
navigationController?.pushViewController(webViewController,
animated: true)
And with that, it looks like this:

You can find my version of this project at the end of day 61 on Github here.
Reflections
I will probably start moving a little slower through the 100 Days of Swift, because I just started my first development job at Madwire. That is why I haven’t posted a 100 Days of Swift post since last Saturday. I’m excited for the new job, and I’m learning a lot already, but it is very mentally demanding and this week I just didn’t have the capacity to also do these exercises. I am going to try to push through though, and hopefully get through a couple per week from here on out. So we’ll see how that goes. I at least have today’s written, and only another 39 to go.