M6L12 : Directions

In this lesson, we moved the business title into a reusable view. Our new BusinessTitle SwiftUI view is this:

struct BusinessTitle: View {

    var business: Business

    var body: some View {
        VStack(alignment: .leading) {
            Text(business.name!)
                .font(.largeTitle)
            if business.location!.displayAddress != nil {
                ForEach(business.location!.displayAddress!, id: \.self) { displayLine in
                    Text(displayLine)
                }
            }
            Image("regular_\(business.rating ?? 0)")
        }
    }
}

The bulk of the above was in our BusinessDetail view, but has now been replaced by this:

BusinessTitle(business: business)
                    .padding()

This can be used elsewhere we need the business title.

In the lesson, we created our DirectionsMap view that uses MapKit to show the user’s route to their chosen destination.

import SwiftUI
import MapKit

struct DirectionsMap : UIViewRepresentable {
    @EnvironmentObject var model: ContentModel
    var business: Business
    var start:CLLocationCoordinate2D {
        return model.locationManager.location?.coordinate ?? CLLocationCoordinate2D()
    }
    var end:CLLocationCoordinate2D {
        if let lat = business.coordinates?.latitude,
            let long = business.coordinates?.longitude {
            return CLLocationCoordinate2D(latitude: lat, longitude: long)
        }
        else {
            return CLLocationCoordinate2D()
        }
    }
    func makeUIView(context: Context) -> MKMapView {
        let map = MKMapView()
        map.delegate = context.coordinator
        map.showsUserLocation = true
        map.userTrackingMode = .followWithHeading
        let request = MKDirections.Request()
        request.source = MKMapItem(placemark: MKPlacemark(coordinate: start))
        request.destination = MKMapItem(placemark: MKPlacemark(coordinate: end))
        let directions = MKDirections(request: request)
        directions.calculate { response, error in
            if error == nil && response != nil {
                for route in response!.routes {
                    map.addOverlay(route.polyline)
                    map.setVisibleMapRect(route.polyline.boundingMapRect,
                                          edgePadding: UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100),
                                          animated: true)
                }
            }
        }
        let annotation = MKPointAnnotation()
        annotation.coordinate = end
        annotation.title = business.name ?? ""
        map.addAnnotation(annotation)
        return map
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {
    }
    static func dismantleUIView(_ uiView: MKMapView, coordinator: Coordinator) {
        uiView.removeAnnotations(uiView.annotations)
        uiView.removeOverlays(uiView.overlays)
    }
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }
    class Coordinator: NSObject, MKMapViewDelegate {
        func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
            let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
            renderer.strokeColor = .blue
            renderer.lineWidth = 5
            return renderer
        }
    }
}

I’ll be reading over this a few times to get my head around what’s going on here.