Apple Maps Not Showing Map Overlay - mapkit

I have a question regarding creating a Apple Map Overlay. I am trying to set on odd shape overlay from a JSON file. I have researched this on Stack Overflow, and have tried many of the solutions, but none seem to work. My code is below:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UIGestureRecognizerDelegate {
#IBOutlet weak var mapView: MKMapView!
var coordinate: CLLocationCoordinate2D?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
mapView.showsUserLocation = true
mapView.delegate = self
mapView.mapType = .standard
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.TapGesture))
mapView.addGestureRecognizer(gestureRecognizer)
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is MKPolygon {
let polygonView = MKPolygonRenderer(overlay: overlay)
polygonView.strokeColor = UIColor.black
polygonView.lineWidth = 0.5
polygonView.fillColor = UIColor.blue
return polygonView
}
return MKOverlayRenderer()
}
#objc func TapGesture(gesRect: UITapGestureRecognizer) {
let location = gesRect.location(in: mapView)
coordinate = mapView.convert(location,toCoordinateFrom: mapView)
let locCoord = mapView.convert(location, toCoordinateFrom: mapView)
print("Tapped at lat: \(locCoord.latitude) long: \(locCoord.longitude)")
print("Tapped at: \(location)")
self.retreiveShape() { (full_shape) in
if let shape = full_shape {
let polygon = MKPolygon.init(coordinates: shape, count: shape.count)
self.mapView.addOverlay(polygon)
} else {
print("ARRAY EMPTY")
}
}
}
func retreiveShape(completion: #escaping ([CLLocationCoordinate2D]?) -> ()) {
let path = Bundle.main.path(forResource: "shape", ofType: "json")
var coord_array = [CLLocationCoordinate2D]()
do {
let data = try Data.init(contentsOf: URL.init(fileURLWithPath: path!))
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let dictionary = json as? [String: Any] {
if let shape = dictionary["shape"] as? Array<Any> {
for regions in shape {
guard let region = regions as? Array<Array<Array<Double>>> else {
print("NOT HAPPENING")
return
}
for sections in region {
for coord in sections {
print("LATITUDE: \(coord[0])", "LONGITUDE: \(coord[1])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[0], longitude: coord[1])
coord_array.append(coordinatesToAppend)
}
}
}
completion(coord_array)
}
}
} catch let error {
print(error)
}
}
The shape.json file is below:
{
"shape":[
[
[
[-81.621199, 30.282314],
[-81.613987, 30.281941],
[-81.611277, 30.284743],
[-81.602735, 30.284026],
[-81.601978, 30.292561],
[-81.596275, 30.290861],
[-81.592406, 30.290182],
[-81.571146, 30.28763],
[-81.55922, 30.286602],
[-81.559148, 30.291132],
[-81.558633, 30.294747],
[-81.55881, 30.312887],
[-81.558601, 30.312888],
[-81.558622, 30.316235],
[-81.558313, 30.316828],
[-81.552252, 30.320252],
[-81.548471, 30.321618],
[-81.527882, 30.323989],
[-81.529486, 30.328076],
[-81.537635, 30.336704],
[-81.537706, 30.337221],
[-81.538717, 30.338277],
[-81.539343, 30.338462],
[-81.542809, 30.341686],
[-81.547286, 30.345211],
[-81.552498, 30.348839],
[-81.552559, 30.352445],
[-81.577566, 30.352039],
[-81.578098, 30.353324],
[-81.578161, 30.35642],
[-81.577294, 30.3596],
[-81.576996, 30.366609],
[-81.58011, 30.366553],
[-81.580875, 30.37062],
[-81.580844, 30.373862],
[-81.581462, 30.374486],
[-81.578114, 30.374236],
[-81.572908, 30.374611],
[-81.562232, 30.372303],
[-81.551965, 30.366559],
[-81.548676, 30.365568],
[-81.540187, 30.378172],
[-81.538175, 30.380467],
[-81.538213, 30.387239],
[-81.536613, 30.388739],
[-81.512612, 30.392739],
[-81.505211, 30.390739],
[-81.490911, 30.392139],
[-81.49085, 30.389014],
[-81.489978, 30.389207],
[-81.488818, 30.38775],
[-81.489203, 30.389266],
[-81.487056, 30.390019],
[-81.481446, 30.391262],
[-81.479505, 30.39117],
[-81.477708, 30.390635],
[-81.476792, 30.390609],
[-81.476244, 30.391002],
[-81.473212, 30.389422],
[-81.472125, 30.388436],
[-81.472225, 30.388071],
[-81.474072, 30.386758],
[-81.475085, 30.384287],
[-81.474394, 30.381898],
[-81.473246, 30.38059],
[-81.473337, 30.380112],
[-81.47295, 30.379864],
[-81.472643, 30.380053],
[-81.471914, 30.379532],
[-81.471629, 30.378346],
[-81.470845, 30.377256],
[-81.468671, 30.376016],
[-81.466871, 30.374481],
[-81.465402, 30.374424],
[-81.464374, 30.373764],
[-81.465116, 30.373015],
[-81.467728, 30.372493],
[-81.469102, 30.371435],
[-81.470279, 30.369931],
[-81.472008, 30.370608],
[-81.473695, 30.370041],
[-81.471862, 30.370238],
[-81.470952, 30.369737],
[-81.471715, 30.369462],
[-81.470506, 30.369378],
[-81.469456, 30.368207],
[-81.468051, 30.367707],
[-81.46754, 30.366828],
[-81.466905, 30.366464],
[-81.467432, 30.366219],
[-81.466928, 30.365735],
[-81.465222, 30.365136],
[-81.464909, 30.364103],
[-81.46316, 30.362764],
[-81.463369, 30.36188],
[-81.462197, 30.361235],
[-81.461151, 30.36123],
[-81.46117, 30.360531],
[-81.461878, 30.360305],
[-81.461619, 30.359642],
[-81.461873, 30.358669],
[-81.461645, 30.358376],
[-81.460504, 30.358329],
[-81.46288, 30.357969],
[-81.462786, 30.357137],
[-81.461247, 30.355282],
[-81.460556, 30.352518],
[-81.46184, 30.340222],
[-81.462497, 30.339325],
[-81.465064, 30.337897],
[-81.471588, 30.328301],
[-81.472988, 30.318258],
[-81.469123, 30.319481],
[-81.450496, 30.320896],
[-81.443818, 30.302908],
[-81.442451, 30.301512],
[-81.438991, 30.299798],
[-81.437921, 30.298031],
[-81.437696, 30.284657],
[-81.438134, 30.283427],
[-81.439935, 30.281191],
[-81.440578, 30.279729],
[-81.440309, 30.276152],
[-81.441217, 30.271746],
[-81.440891, 30.270368],
[-81.440247, 30.269313],
[-81.438555, 30.267721],
[-81.43765, 30.266188],
[-81.43705, 30.257116],
[-81.441869, 30.256519],
[-81.45385, 30.252008],
[-81.466184, 30.251073],
[-81.472173, 30.251296],
[-81.491372, 30.251034],
[-81.507105, 30.253603],
[-81.510744, 30.253761],
[-81.530261, 30.250144],
[-81.56957, 30.249854],
[-81.584658, 30.251369],
[-81.586895, 30.251326],
[-81.589607, 30.250593],
[-81.593308, 30.248471],
[-81.605497, 30.260294],
[-81.621493, 30.282334],
[-81.621199, 30.282314]
]
]
]
}
It should create an odd shape overlay in the Southside of Jacksonville,FL, but it isn't. When the completion block is called the Coordinates are added to the array, but the map overlay isn't showing. Any thoughts?

Well this is somewhat embarrassing. I did as was suggested in the comments, and tried having the shape with nine vertices. It still didn't work. I then changed the coord from:
print("LATITUDE: \(coord[0])", "LONGITUDE: \(coord[1])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[0], longitude: coord[1])
to:
print("LATITUDE: \(coord[1])", "LONGITUDE: \(coord[0])")
let coordinatesToAppend = CLLocationCoordinate2D(latitude: coord[1], longitude: coord[0])
It works perfectly. Turns out I had the Latitude and Longitude wrong.

Related

Without Bridging to ObjectiveC, Can We Get the Coordinates of a Tap Solely in SwiftUI?

I have the following code:
struct MyLocationMap: View {
#EnvironmentObject var userData: UserData
#State var annotationArray: [MyAnnotation] = [MyAnnotation(coordinates: CLLocationCoordinate2D(latitude: CurrentLocation().coordinates?.latitude ?? CLLocationCoordinate2D().latitude, longitude: CurrentLocation().coordinates?.longitude ?? CLLocationCoordinate2D().longitude), type: .waypoint)]
var body: some View {
MakeMapView(annotationArray: annotationArray)
.gesture(
LongPressGesture(minimumDuration: 1)
.onEnded { _ in
print("MapView pressed!\n--------------------------------------\n")
//Handle press here
})
}
}
import SwiftUI
import CoreLocation
import MapKit
struct MakeMapView : UIViewRepresentable {
typealias UIViewType = MKMapView
let annotationArray: [MyAnnotation]?
func makeUIView(context: UIViewRepresentableContext<MakeMapView>) -> MKMapView{
MKMapView()
}
func updateUIView(_ mapView: MKMapView, context: Context) {
mapView.showsUserLocation = true
if let coordinates = CurrentLocation().coordinates {
// updateAnnotations(from: mapView)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
mapView.showsUserLocation = true
mapView.showsCompass = true
mapView.showsScale = true
mapView.mapType = .satellite
let span = MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002)
let region = MKCoordinateRegion(center: coordinates, span: span)
mapView.setRegion(region, animated: true)
})
}
}
What I am having trouble with is implementing func convert(_ point: CGPoint, toCoordinateFrom view: UIView?) -> CLLocationCoordinate2D to obtain the lat/lon of the gesture solely using SwiftUI/Combine. Is it possible? If not, can I implement it with what I have?
I have reviewed the post at
How to handle touch gestures in SwiftUI in Swift UIKit Map component?
and
Add single pin to Mapkit with SwiftUI
Once I have the coordinates, dropping the pin is straightforward, but I can't wrap my head around getting the coordinates.
Thanks.
A DragGesture with no minimumDistance activates immediately and has location and startLocation in its onChanged listener, you can use that to grab location like so:
struct GesturesView: View {
#State private var location: CGPoint = .zero
var body: some View {
let drag = DragGesture(minimumDistance: 0).onChanged {
self.location = $0.startLocation
}
let longPress = LongPressGesture().onEnded { _ in
self.doSomething(with: self.location)
}
let gesture = longPress.simultaneously(with: drag)
return Rectangle()
.frame(width: 300, height: 300)
.padding()
.gesture(gesture)
}
func doSomething(with location: CGPoint) {
print("Pressed at", location)
}
}

Swiftui how to use MKOverlayRenderer?

I want draw a route on the map.
but struct without using delegate.
struct MapView : UIViewRepresentable {
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
}
how can I do?
You need to specify a delegate if you want mapView(_:rendererFor:) to be called:
struct MapView: UIViewRepresentable {
#Binding var route: MKPolyline?
let mapViewDelegate = MapViewDelegate()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ view: MKMapView, context: Context) {
view.delegate = mapViewDelegate // (1) This should be set in makeUIView, but it is getting reset to `nil`
view.translatesAutoresizingMaskIntoConstraints = false // (2) In the absence of this, we get constraints error on rotation; and again, it seems one should do this in makeUIView, but has to be here
addRoute(to: view)
}
}
private extension MapView {
func addRoute(to view: MKMapView) {
if !view.overlays.isEmpty {
view.removeOverlays(view.overlays)
}
guard let route = route else { return }
let mapRect = route.boundingMapRect
view.setVisibleMapRect(mapRect, edgePadding: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10), animated: true)
view.addOverlay(route)
}
}
class MapViewDelegate: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.fillColor = UIColor.red.withAlphaComponent(0.5)
renderer.strokeColor = UIColor.red.withAlphaComponent(0.8)
return renderer
}
}
Used like so:
struct ContentView : View {
#State var route: MKPolyline?
var body: some View {
MapView(route: $route)
.onAppear {
self.findCoffee()
}
}
}
private extension ContentView {
func findCoffee() {
let start = CLLocationCoordinate2D(latitude: 37.332693, longitude: -122.03071)
let region = MKCoordinateRegion(center: start, latitudinalMeters: 2000, longitudinalMeters: 2000)
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "coffee"
request.region = region
MKLocalSearch(request: request).start { response, error in
guard let destination = response?.mapItems.first else { return }
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: start))
request.destination = destination
MKDirections(request: request).calculate { directionsResponse, _ in
self.route = directionsResponse?.routes.first?.polyline
}
}
}
}
Yielding:

No sound from remote notification

Could some one help me to understand why i'm not receiving sound from remote notifications in IOS 10 Swift 3.
Here is my code:
AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]?) -> Bool {
let settings = UNUserNotificationCenter.current()
settings.requestAuthorization(options: [.badge, .alert, .sound]) {(granted, error) in
}
return true
}
// Override point for customization after application launch.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
let viewController = self.window?.rootViewController as! LoadingCloudDataViewController
let notification: CKNotification = CKNotification(fromRemoteNotificationDictionary: userInfo as! [String: NSObject])
if(notification.notificationType == CKNotificationType.query) {
let queryNotification = notification as! CKQueryNotification
let recordID = queryNotification.recordID
viewController.fetchRecord(recordID: recordID!)
}
}
And here is the notification subscription code:
func subscriptionNotificationsKcal() {
publicDatabase = container.publicCloudDatabase
let predicate = NSPredicate(format: "TRUEPREDICATE")
let subscription = CKQuerySubscription(recordType: "Kcal", predicate: predicate, options: .firesOnRecordCreation)
let notificationInfo = CKNotificationInfo()
notificationInfo.alertBody = "New Calories Item was Added"
notificationInfo.shouldBadge = true
subscription.notificationInfo = notificationInfo
publicDatabase?.save(subscription, completionHandler: ({returnRecord, error in
if let err = error {
print("subscription failed %#", err.localizedDescription)
}else{
print("sub success")
}
}))
}
By default the sound is muted. To activate it, in the case of this example, use the following code:
notificattionInfo.soundName = "default"

How to continue Alamofire Download task after suspend it, terminate the app, and reopen the app

I need to write a downloader for my app, and It can pause, continue and cancel the downloads. Also it must support to pause download, kill the app, and reopen the app and continue from where it paused.
How can i keep the downloaded data and how can I continue it?
import UIKit
import Foundation
import Alamofire
class DownloaderViewController: UIViewController {
#IBOutlet weak var label: UILabel!
let progressIndicatorView = UIProgressView()
var request: Alamofire.Request?
override func viewDidLoad() {
super.viewDidLoad()
}
}
#IBAction func cancelBtn(sender: AnyObject) {
self.request?.cancel()
self.label.text = "% 0.0"
}
#IBAction func pauseBtn(sender: AnyObject) {
self.request?.suspend()
}
#IBAction func continueBtn(sender: AnyObject) {
self.request?.resume()
}
#IBAction func startBtn(sender: AnyObject) {
var localPath: NSURL?
self.request = Alamofire.download(.GET, "https://dl.dropboxusercontent.com/u/11563257/3.%20Interactive_iPad_test_for_PDF_EXPERT.pdf", destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
}).progress() {
(_, totalBytesRead, totalBytesExpectedToRead) in
dispatch_async(dispatch_get_main_queue()) {
self.progressIndicatorView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
self.updateProgress(self.progressIndicatorView)
if totalBytesRead == totalBytesExpectedToRead {
self.progressIndicatorView.removeFromSuperview()
}
}
}
func updateProgress(prg:UIProgressView) {
let stepSize:Float = 0.1
prg.setProgress(prg.progress + stepSize, animated: true)
self.label.text = "% " + String(format: "%.2f", prg.progress*100)
}
}
This works while the app is running. But I need to save the data when the app is terminated and continue it when the app started. I have no i idea how to keep the downloaded data and how to continue it. Any help will be appriciated.
I find this in Alamofire documentation :
Alamofire.download(.GET, "https://httpbin.org/stream/100", destination: destination)
.response { _, _, data, _ in
if let
data = data,
resumeDataString = NSString(data: data, encoding: NSUTF8StringEncoding)
{
print("Resume Data: \(resumeDataString)")
} else {
print("Resume Data was empty")
}
}
But I get "Resume Data was empty" everytime. I give the same destination. But It can't catct the Resume Data. And I couldn't find an example with Alamofire.

MPNowPlayingInfoCenter : What is the best way to set MPMediaItemArtwork from an Url?

All methods I found to set MPMediaItemArtwork of MPNowPlayingInfoCenter are with local images.
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: [UIImage imageNamed:#"myimage"];
But I need to set this from an imageURL
Currently i use this...
UIImage *artworkImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:self.currentTrack.imageUrl]]];
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: artworkImage];
[self.payingInfoCenter setValue:albumArt forKey:MPMediaItemPropertyArtwork];
Any idea?
That's my best solution:
- (void)updateControlCenterImage:(NSURL *)imageUrl
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSMutableDictionary *songInfo = [NSMutableDictionary dictionary];
UIImage *artworkImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:imageUrl]];
if(artworkImage)
{
MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: artworkImage];
[songInfo setValue:albumArt forKey:MPMediaItemPropertyArtwork];
}
MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter];
infoCenter.nowPlayingInfo = songInfo;
});
}
/!\ if you've already setted the MPNowPlayingInfoCenter , get it or all other values will be overridden
let mpic = MPNowPlayingInfoCenter.default()
DispatchQueue.global().async {
if let urlString = yourUrlString, let url = URL(string:urlString) {
if let data = try? Data.init(contentsOf: url), let image = UIImage(data: data) {
let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { (_ size : CGSize) -> UIImage in
return image
})
DispatchQueue.main.async {
mpic.nowPlayingInfo = [
MPMediaItemPropertyTitle:"Title",
MPMediaItemPropertyArtist:"Artist",
MPMediaItemPropertyArtwork:artwork
]
}
}
}
}
That worked for me in iOS 11, swift 4
Here's a function that sets up a media session with image on the lock screen and control center:
(This code was a modified version of #NickDK's answer)
func setupNowPlaying(title: String, albumArtwork: String, artist:String, isExplicit: Bool, rate: Float, duration: Any) {
let url = URL.init(string: albumArtwork)!
let mpic = MPNowPlayingInfoCenter.default()
DispatchQueue.global().async {
if let data = try? Data.init(contentsOf: url), let image = UIImage(data: data) {
let artwork = MPMediaItemArtwork(boundsSize: image.size, requestHandler: { (_ size : CGSize) -> UIImage in
return image
})
DispatchQueue.main.async {
mpic.nowPlayingInfo = [
MPMediaItemPropertyTitle: title,
MPMediaItemPropertyArtist: artist,
MPMediaItemPropertyArtwork:artwork,
MPMediaItemPropertyIsExplicit: isExplicit,
MPNowPlayingInfoPropertyPlaybackRate: soundManager.audioPlayer?.rate ?? 0,
MPMediaItemPropertyPlaybackDuration: CMTimeGetSeconds(soundManager.audioPlayer?.currentItem?.asset.duration ?? CMTime(seconds: 0, preferredTimescale: 0))
]
}
}
}
}
Usage:
setupNowPlaying(
title: "Pull up at the mansion",
albumArtwork: "https://static.wixstatic.com/media/89b4e7_5f29de0db68c4d888065b0f03d393050~mv2.png/v1/fill/w_512,h_512/ImageTitle.png",
artist: "DJ bon26",
isExplicit: true
)
Full usage:
import MediaPlayer
class SoundManager : ObservableObject {
var audioPlayer: AVPlayer?
func playSound(sound: String){
if let url = URL(string: sound) {
self.audioPlayer = AVPlayer(url: url)
}
}
}
struct ContentView: View {
#State var song1 = false
#StateObject private var soundManager = SoundManager()
var body: some View {
Image(systemName: song1 ? "pause.circle.fill": "play.circle.fill")
.font(.system(size: 25))
.padding(.trailing)
.onTapGesture {
playSound(
url: "https://static.wixstatic.com/mp3/0fd70b_8d4e15117ff0458792a6a901c6dddc6b.mp3",
title: "Pull up at the mansion",
albumArtwork: "https://static.wixstatic.com/media/89b4e7_5f29de0db68c4d888065b0f03d393050~mv2.png/v1/fill/w_512,h_512/ImageTitle.png",
artist: "DJ bon26",
isExplicit: true
)
}
}
func playSound(url: String, title: String, albumArtwork: String, artist: String, isExplicit: Bool) {
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default, options:
.init(rawValue: 0))
try AVAudioSession.sharedInstance().setActive(true)
soundManager.playSound(sound: url)
song1.toggle()
if song1{
soundManager.audioPlayer?.play()
setupNowPlaying(
title: title,
albumArtwork: albumArtwork,
artist: artist,
isExplicit: isExplicit
)
UIApplication.shared.beginReceivingRemoteControlEvents()
MPNowPlayingInfoCenter.default().playbackState = .playing
} else {
soundManager.audioPlayer?.pause()
}
}catch{
print("Something came up")
}
}
}
I believe this is very useful,
Brendan Okey-iwobi