Skip to content

Instantly share code, notes, and snippets.

@egzonpllana
Created April 7, 2021 11:42
Show Gist options
  • Save egzonpllana/f8b6eb2c41dd90ce9a038ee31d9298a6 to your computer and use it in GitHub Desktop.
Save egzonpllana/f8b6eb2c41dd90ce9a038ee31d9298a6 to your computer and use it in GitHub Desktop.
Integration of UIKit frameworks to SwiftUI ecosystem through Coordinator.
/// Copyright (c) 2021 Razeware LLC
/// RayWenderlich SwiftUI Book 3.0 (2021)
/// Egzon Pllana 07.03.2021
import SwiftUI
import MapKit
// MARK: - SwiftUI Coordinator (connect delegate & protocols)
class MapCoordinator: NSObject {
var mapView: FlightMapView
var fraction: CGFloat
init(
_ mapView: FlightMapView,
progress: CGFloat = 0.0
) {
self.mapView = mapView
self.fraction = progress
}
}
// MARK: - UIKit View -> SwiftUI View (UIViewRepresentable)
struct FlightMapView: UIViewRepresentable {
var startCoordinate: CLLocationCoordinate2D
var endCoordinate: CLLocationCoordinate2D
var progress: CGFloat
// MARK: - UIViewRepresentable Initializers
func makeUIView(context: Context) -> MKMapView {
let view = MKMapView(frame: .zero)
view.delegate = context.coordinator
return view
}
func updateUIView(_ view: MKMapView, context: Context) {
// Implement the overlays that need a delegate
let startOverlay = MKCircle(
center: startCoordinate,
radius: 10000.0
)
let endOverlay = MKCircle(
center: endCoordinate,
radius: 10000.0
)
let flightPath = MKGeodesicPolyline(
coordinates: [startCoordinate, endCoordinate],
count: 2
)
view.addOverlays([startOverlay, endOverlay, flightPath])
// 1
let startPoint = MKMapPoint(startCoordinate)
let endPoint = MKMapPoint(endCoordinate)
// 2
let minXPoint = min(startPoint.x, endPoint.x)
let minYPoint = min(startPoint.y, endPoint.y)
let maxXPoint = max(startPoint.x, endPoint.x)
let maxYPoint = max(startPoint.y, endPoint.y)
// 3
let mapRect = MKMapRect(
x: minXPoint,
y: minYPoint,
width: maxXPoint - minXPoint,
height: maxYPoint - minYPoint
)
// 4
let padding = UIEdgeInsets(
top: 10.0,
left: 10.0,
bottom: 10.0,
right: 10.0
)
// 5
view.setVisibleMapRect(
mapRect,
edgePadding: padding,
animated: true
)
// 6
view.mapType = .mutedStandard
view.isScrollEnabled = false
}
// MARK: - SwiftUI Coordinator
// You need to tell SwiftUI about the Coordinator class.
func makeCoordinator() -> MapCoordinator {
/*
This method creates the coordinator and returns it to the SwiftUI framework to pass in where necessary. SwiftUI will call makeCoordinator() before makeUIViewController(context:) so it’s available during the creation and configuration of your non-SwiftUI components.
*/
MapCoordinator(self, progress: progress)
}
}
// MARK: - MapViewDelegates
extension MapCoordinator: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay ) -> MKOverlayRenderer {
if overlay is MKCircle {
let renderer = MKCircleRenderer(overlay: overlay)
renderer.fillColor = UIColor.black
renderer.strokeColor = UIColor.black
return renderer
}
if overlay is MKGeodesicPolyline {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor(
red: 0.0,
green: 0.0,
blue: 1.0,
alpha: 0.3
)
renderer.lineWidth = 3.0
renderer.strokeStart = 0.0
renderer.strokeEnd = fraction
return renderer
}
return MKOverlayRenderer()
}
}
// MARK: - Preview
struct MapView_Previews: PreviewProvider {
static var previews: some View {
FlightMapView(
startCoordinate: CLLocationCoordinate2D(
latitude: 35.655, longitude: -83.4411
),
endCoordinate: CLLocationCoordinate2D(
latitude: 36.0840, longitude: -115.1537
),
progress: 0.67
)
.frame(width: 300, height: 300)
}
}
/* Code Expainations
1. When you project the curved surface of the Earth onto a flat surface, such as a device screen, some distortion occurs. You convert the start and end coordinates on the globe to MKMapPoint values in the flattened map. Using MKMapPoints dramatically simplifies the calculations to follow.
2. Next, you determine the minimum and maximum x and y values among these points.
3. You create a MKMapRect from those minimum and maximum values. The resulting rectangle covers the space between the two points along the rectangle's edge.
4. Next, you create a UIEdgeInsets struct with all sides set to an inset of ten points.
5. You use the setVisibleMapRect(_:edgePadding:animated:) method to set the map's viewable area. This method uses the rectangle calculated in step three as the area to show. The edgePadding adds the padding that you set up in step four, so the airports' locations are not directly at the edge of the view and, therefore, easier to see.
6. You set the type of map and do not allow the user to scroll the map.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment