custom Google Maps blue dot in Swift - swift

What I want to achieve is to customize google maps blue dot with an svg icon,
with the icon moving when location is changed.
what I am doing:
struct GoogleMapsView: UIViewRepresentable {
#ObservedObject var locationManager = LocationManager.shared
#State var myLocationDot = GMSMarker()
#Binding var nearbyDrivers: [NearbyDriver]
init(nearbyDrivers: Binding<[NearbyDriver]>){
self._nearbyDrivers = nearbyDrivers
myLocationDot.position = locationManager.userLocation!.coordinate
}
func makeUIView(context: Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: locationManager.userLocation!.coordinate.latitude, longitude: locationManager.userLocation!.coordinate.longitude, zoom: 15)
let mapView = GMSMapView(frame: CGRect.zero, camera: camera)
myLocationDot.position = CLLocationCoordinate2D(latitude: locationManager.userLocation!.coordinate.latitude, longitude: locationManager.userLocation!.coordinate.longitude)
let myLocationDotIcon = UIImage(named: "myLocationDotIcon.svg")
let myLocationDotIconImage = UIImageView(image: myLocationDotIcon!)
myLocationDotIconImage.frame = CGRect(x: 0, y: 0, width: 40, height: 50)
myLocationDot.iconView = myLocationDotIconImage
myLocationDot.zIndex = 1
myLocationDot.map = mapView
// MARK: - Load nearby drivers
if !nearbyDrivers.isEmpty {
for driver in nearbyDrivers {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: driver.location.lat, longitude: driver.location.long)
let locationIcon = UIImage(named: "carAnnotation.png")
let locationIconImage = UIImageView(image: locationIcon!)
locationIconImage.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
marker.iconView = locationIconImage
marker.zIndex = 0
marker.map = mapView
}
}
//MARK: - MapStyle
do {
if let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") {
mapView.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
} else {
NSLog("Unable to find style.json")
}
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
return mapView
}
func updateUIView(_ uiView: GMSMapView, context: Context) {
uiView.isMyLocationEnabled = false
}
func makeCoordinator() -> Coordinator {
Coordinator(myLocationDot: $myLocationDot, owner: self)
}
class Coordinator: NSObject, GMSMapViewDelegate, CLLocationManagerDelegate {
#ObservedObject var locationManager = LocationManager.shared
#Binding var myLocationDot: GMSMarker
let owner: GoogleMapsView
init(myLocationDot: Binding<GMSMarker>, owner: GoogleMapsView) {
self.owner = owner
_myLocationDot = myLocationDot
}
func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
mapView.animate(toLocation: locationManager.userLocation!.coordinate)
return true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
myLocationDot.position = locations.last!.coordinate
}
}
}
The idea is that I need to change the blue dot appearance but leave the functionalities as they need to be.
For some reason I can't find any course on advanced google maps customization. if you could refer a guy that does the thing, that would be awesome.

Related

Mapbox Maps SDK for iOS - Maps won't display anymore (or gets cut off)

Maps (from MapBox API) were working just fine in our app, but about a month they stopped displaying on a swiftui view. BUT the tricky thing is that the maps in some phones still work (or works partially but being cut off), and they still show the annotations/pins (see screenshot).
We have the MapBox Maps SDK for iOS installed as cocoapods into Xcode.
It's a simple app, it just detects a vehicle trip and add event points to the map (eg. point A as trip start, and point B as trip end).
Versions:
Xcode: 13.2.1
pod 'Mapbox-iOS-SDK', '~> 6.4.1'
pod 'MapboxNavigation', '~> 1.4.2'
Basically, the following is our main code to show the map.
Note: It also adds speeding, hard brake and rapid accelaration annotations to the map.
Does someone have any idea why the map is not showing properly?
Thanks much in advance.
import SwiftUI
import Mapbox
import MapboxDirections
import Polyline
struct TestMapDriveView: View {
var selectedTrip: VehicleTripData // The selected trip that needs to be shown
#Binding var speedingCount: Int? // the number of speeding events in the selected trip
#Binding var showCallouts: Bool // Whether to show callout when a point annotation is selected
#Binding var showInfoSheet: Bool // whether show the event details view or not (show the details when the user tap on a specific marker on the map)
#Binding var isFullScreen: Bool // whether show full screen map or not
#Binding var selectedEncodedPolyline: String // The encoded polyline for the trip
#State var alreadyClean: Bool = false
#State var changed: Bool = false
#State var showSpecificPoint = false // Whether focues on a specific trip point or not
let action: () -> () // Used to toggle the full screen state variable
var body: some View {
VStack {
NavigationLink(destination: EmptyView(), label: {EmptyView()})
NavigationLink(destination: EventDetails(tripPoint: selectedTripPoint, selectedTrip: selectedTrip, typeInfo: .constant(selectedTypeInfo))
, isActive: $showInfoSheet, label: {EmptyView()}).navigationTitle("").navigationBarTitle("").navigationBarHidden(true)
MapboxMap(selectedTrip: selectedTrip, speedingCount: $speedingCount, showCallouts: $showCallouts, showInfoSheet: $showInfoSheet, isFullScreen: $isFullScreen, selectedEncodedPolyline: $selectedEncodedPolyline, alreadyClean: $alreadyClean, changed: $changed, showSpecificPoint: $showSpecificPoint)
.onAppear {
showDetails = false
// The reason for adding a timer here is that since showDetails is a global variable,
// and it is not a state variable, we canot trigger its value
// We need a timer so that we can check the value of the showDetails every 0.1 second
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { timer in
showInfoSheet = showDetails
if showInfoSheet {
isFullScreen = false
findTripPoint()
timer.invalidate()
}
}
}
}
}
}
// MARK: - New Map Provided by MapBox
// MARK: - Since MapBox is designed to be implemented with the UIView, for SwiftUI, we need to transfer the UIView to View
struct MapboxMap: UIViewRepresentable {
var selectedTrip: VehicleTripData
#Binding var speedingCount: Int?
#Binding var showCallouts: Bool
#Binding var showInfoSheet: Bool
#Binding var isFullScreen: Bool
#Binding var selectedEncodedPolyline: String
#Binding var alreadyClean: Bool
#Binding var changed: Bool
#Binding var showSpecificPoint: Bool
private let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: MGLStyle.streetsStyleURL)
class Coordinator: NSObject, MGLMapViewDelegate {
var showCallouts: Bool
init(showcallouts: Bool) {
self.showCallouts = showcallouts
}
func mapView(_ mapView: MGLMapView, shapeAnnotationIsEnabled annotation: MGLShape) -> Bool {
return annotation.title != nil && annotation.title != "Speeding"
}
// When you tap on a point on the map, whether show the callout or not
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
if let title = annotation.title {
return (!(title == "Speeding") && showCallouts)
}
else {
return false
}
}
// MARK: - This function is used to replace the default marker icon with our icons for specific events
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
guard annotation is MGLPointAnnotation else {
return nil
}
guard annotation.title != nil else {return nil}
guard annotation.title! != nil else {return nil}
if annotation.title != ""{
let identifier = annotation.title!!
print("error \(annotation.title)")
var image = UIImage()
if identifier.hasPrefix("Speeding") {
image = UIImage(named: "Speeding Marker")!
} else if identifier.hasPrefix("Hard"){
image = UIImage(named: "Hard Brake Pin")!
} else if identifier.hasPrefix("Rapid") {
image = UIImage(named: "Rapid Accel Pin")!
} else {
image = UIImage(named: identifier)!
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MGLAnnotationView(annotation: annotation, reuseIdentifier: identifier as! String)
let imageView = UIImageView(image: image)
annotationView!.frame = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
annotationView?.addSubview(imageView)
annotationView?.centerOffset = CGVector(dx: 0, dy: -image.size.height / 2.0)
}
return annotationView
}
return nil
}
// MARK: - This function is executed when the user taps on "View Details"
// In fact, we should set showInfoSheet to be true in order to show the EventDetailView
// However, since we are using a coordinator here, we cannnot change showInfoSheet directly
// Therefore, we change the global variable "showDetails" to be true
#objc func labelAction(_ sender: UITapGestureRecognizer) {
showDetails = true
}
// MARK: - Add the "View Details" Label in the Callout View
// MARK: - We should use UIButtion, however, if we use UIButtion, it will never be shown. Need further tests
func mapView(_ mapView: MGLMapView, leftCalloutAccessoryViewFor annotation: MGLAnnotation) -> UIView? {
guard let title = annotation.title else {return nil}
if title != nil {
if title!.hasPrefix("Speeding:") {
selectedTypeInfo = "speeding"
} else if title!.hasPrefix("Hard") {
selectedTypeInfo = "hardBrake"
} else if title!.hasPrefix("Rapid") {
selectedTypeInfo = "rapidAccel"
} else {
return nil
}
let labelTap = UITapGestureRecognizer(target: self, action: #selector(labelAction(_:)))
labelTap.numberOfTapsRequired = 1
// Callout height is fixed; width expands to fit its content.
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 60, height: 50))
label.textAlignment = .center
label.textColor = UIColor(named: "Blue")
label.text = "View Details"
label.numberOfLines = 2
label.font = UIFont(name: "NotoSans-Regular", size: 12)
label.isUserInteractionEnabled = true
label.addGestureRecognizer(labelTap)
selectedCoor = annotation.coordinate
return label
}
return nil
}
// MARK: - This function is used to change the color of the polyline based on the event name
func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -> UIColor {
guard let title = annotation.title else {return UIColor(named: "Blue")!}
if title.hasPrefix("Speeding") {
return UIColor(named: "MapRed")!
} else {
return UIColor(named: "Blue")!
}
}
}
func makeCoordinator() -> MapboxMap.Coordinator {
return Coordinator(showcallouts: showCallouts)
}
// MARK: - Initialize the MapView
func makeUIView(context: Context) -> MGLMapView {
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(CLLocationCoordinate2D(latitude: selectedTrip.startedLatitude!, longitude: selectedTrip.startedLongitude!), animated: true)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ uiView: MGLMapView, context: Context) {
if (uiView.annotations ?? []).count == 0 {
mapViewDidFinishLoadingMap(uiView, didFinishLoading: MGLStyle())
}
}
func mapViewDidFinishLoadingMap(_ mapView: MGLMapView, didFinishLoading style: MGLStyle) {
mapView.isRotateEnabled = false
// MARK: - Get the result from the encoded polyline string
let result = Polyline.init(encodedPolyline: selectedEncodedPolyline, encodedLevels: .none)
// MARK: - Get the coordinates in the polyline for the trip
let coordinates = result.coordinates
guard let tripPoints = coordinates else {return}
// MARK: - Get the start and end point
let startPoint: CLLocationCoordinate2D = coordinates?.first ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
let endPoint: CLLocationCoordinate2D = coordinates?.last ?? CLLocationCoordinate2D(latitude: 0, longitude: 0)
var startTripPoint = MGLPointAnnotation()
var endTripPoint = MGLPointAnnotation()
startTripPoint.title = "Starting Point"
startTripPoint.coordinate = startPoint
endTripPoint.title = "End Point"
endTripPoint.coordinate = endPoint
mapView.addAnnotation(startTripPoint)
mapView.addAnnotation(endTripPoint)
let tryLine = MGLPolyline(coordinates: coordinates!, count: UInt(coordinates!.count))
mapView.addAnnotation(tryLine)
var speedingArray = [[TripPoint]]()
DispatchQueue.global(qos: .background).async {
// MARK: - Deal with the speeding events in the trip
// MARK: - The speeding icon will be placed at the point which has the maximum speed for each speeding polyline
var ctr = 0
while ctr < selectedTrip.speedingPoints!.count{
var speedPath = [TripPoint]()
speedPath.append(selectedTrip.speedingPoints![ctr])
while ctr + 1 < selectedTrip.speedingPoints!.count && selectedTrip.speedingPoints![ctr + 1].timeStamp!.timeIntervalSince1970 - selectedTrip.speedingPoints![ctr].timeStamp!.timeIntervalSince1970 < 10{
speedPath.append(selectedTrip.speedingPoints![ctr+1])
ctr += 1
}
speedingArray.append(speedPath)
ctr += 1
}
for speedingLine in speedingArray {
var maxDelta = 0.0
var maxDeltaPoint:TripPoint? = nil
var path: [CLLocationCoordinate2D] = []
for speedingPoint in speedingLine{
path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
if speedingPoint.speed_limit_delta! > maxDelta{
maxDelta = speedingPoint.speed_limit_delta!
maxDeltaPoint = speedingPoint
}
}
if let markerPoint = maxDeltaPoint{
tripPointsArray.append(markerPoint)
var speedMarker = MGLPointAnnotation()
speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
DispatchQueue.main.async {
mapView.addAnnotation(speedMarker)
}
}
let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
speedingPolyline.title = "Speeding"
DispatchQueue.main.async {
mapView.addAnnotation(speedingPolyline)
}
}
speedingCount = speedingArray.count
// MARK: - Deal with hard brakes in the trip
for hardBrakePoint in selectedTrip.hardBrakePoints!{
tripPointsArray.append(hardBrakePoint)
let hardBrakeMarker = MGLPointAnnotation()
hardBrakeMarker.coordinate = CLLocationCoordinate2D(latitude: hardBrakePoint.latitude!, longitude: hardBrakePoint.longitude!)
hardBrakeMarker.title = "Hard Brake"
hardBrakeMarker.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
DispatchQueue.main.async {
mapView.addAnnotation(hardBrakeMarker)
}
}
// MARK: - Deal with rapid accel in the trip
for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
tripPointsArray.append(rapidAccelPoint)
let rapidAccelMarker = MGLPointAnnotation()
rapidAccelMarker.coordinate = CLLocationCoordinate2D(latitude: rapidAccelPoint.latitude!, longitude: rapidAccelPoint.longitude!)
rapidAccelMarker.title = "Rapid Accel"
rapidAccelMarker.subtitle = "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
DispatchQueue.main.async {
mapView.addAnnotation(rapidAccelMarker)
}
}
}
// MARK: - If we are not in EventDetailView, then the mapView.showAnnotations will help us to zoom the map to the proper level so that all the markers and annotations in the map will be shown
if !showSpecificPoint {
Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { timer in
guard let annotations = mapView.annotations else {return}
mapView.showAnnotations(annotations, edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
}
} else {
// MARK: - If we need to zoom into a specific point, we need to find which point it is among all the trip points and zoom into that specific point
var alreadyAdded = false
var point = MGLPointAnnotation()
point.coordinate = CLLocationCoordinate2D(latitude: selectedTripPoint.latitude ?? 0, longitude: selectedTripPoint.longitude ?? 0)
for hardBrakePoint in selectedTrip.hardBrakePoints! {
if hardBrakePoint.latitude == selectedTripPoint.latitude && hardBrakePoint.longitude == selectedTripPoint.longitude {
point.title = "Hard Brake"
point.subtitle = "Acceleration: \(String(format: "%.1f", hardBrakePoint.acceleration! * 3.6)) " + "km/h/s"
mapView.addAnnotation(point)
alreadyAdded = true
break
}
}
if !alreadyAdded {
for rapidAccelPoint in selectedTrip.rapidAccelPoints!{
if rapidAccelPoint.latitude == selectedTripPoint.latitude && rapidAccelPoint.longitude == selectedTripPoint.longitude {
point.title = "Rapid Accel"
point.title! += "Acceleration: \(String(format: "%.1f", rapidAccelPoint.acceleration! * 3.6)) " + "km/h/s"
mapView.addAnnotation(point)
alreadyAdded = true
break
}
}
}
if !alreadyAdded {
var presentPolyLine: MGLPolyline = MGLPolyline()
for speedingLine in speedingArray{
var maxDelta = 0.0
var maxDeltaPoint:TripPoint? = nil
var path: [CLLocationCoordinate2D] = []
for speedingPoint in speedingLine{
path.append(CLLocationCoordinate2D(latitude: speedingPoint.latitude!, longitude: speedingPoint.longitude!))
if speedingPoint.speed_limit_delta! > maxDelta{
maxDelta = speedingPoint.speed_limit_delta!
maxDeltaPoint = speedingPoint
}
}
if let markerPoint = maxDeltaPoint{
if selectedTripPoint.latitude == markerPoint.latitude && selectedTripPoint.longitude == markerPoint.longitude {
let specificMarker = MGLPointAnnotation()
specificMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude ?? 0, longitude: markerPoint.longitude ?? 0)
var speedMarker = MGLPointAnnotation()
speedMarker.coordinate = CLLocationCoordinate2D(latitude: markerPoint.latitude!, longitude: markerPoint.longitude!)
speedMarker.title = "Speeding: \(Int(markerPoint.speed!)) " + markerPoint.speedUnit!
let speedLimitBR = Double(markerPoint.speed!) - markerPoint.speed_limit_delta! //before rounding
var speedLimit = 10 * Int((speedLimitBR / 10.0).rounded())//round to nearest 10
speedMarker.subtitle = "Speed Limit: ~ \(speedLimit) " + markerPoint.speedUnit!
point = speedMarker
DispatchQueue.main.async {
let speedingPolyline = MGLPolyline(coordinates: path, count: UInt(path.count))
speedingPolyline.title = "Speeding"
presentPolyLine = speedingPolyline
}
break
} else {
path.removeAll()
}
}
}
mapView.showAnnotations([point], edgePadding: .init(top: 60, left: 40, bottom: 10, right: 40), animated: true, completionHandler: nil)
} else {
mapView.showAnnotations([point], edgePadding: .init(top: -10, left: -10, bottom: -10, right: -10), animated: true, completionHandler: nil)
}
}
}
}
I tried updating the pods from 6.3 to 6.4.1 but it didn't work.
I know MapBox has a new framework as of v6.4.1 called v10, but I haven't installed yet cause that would change the whole code.
I am expecting to see the map as it was displaying before this new bug. eg. see screenshot

UIView to UIImage code is not working. It causing an runtime error

I have to convert UIView to UIImage because I want to make a custom marker.
Currently, I'm making an app using Naver Map SDK.
Here is My Code.
let marker = NMFMarker()
marker.position = NMGLatLng(lat: lat, lng: lng)
let windowView = CustomInfoWindowView()
windowView.nameLabel.text = name
windowView.jobLabel.text = job
windowView.scoreLabel.text = "\(score)"
marker.iconImage = NMFOverlayImage(image: windowView.asImage())
marker.mapView = mapView
NMFOverlayImage only supported UIImage type.
Here is My full code.
class MapViewController: UIViewController {
let viewModel = MapViewModel()
let locations = BehaviorRelay<Void>(value: ())
let disposedBag = DisposeBag()
private let imageCell = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let mapView = NMFMapView(frame: view.frame)
view.addSubview(mapView)
bind(mapView: mapView)
}
private func bind(mapView: NMFMapView) {
let input = MapViewModel.Input(getLocations: locations.asSignal(onErrorJustReturn: ()))
let output = viewModel.transform(input)
output.getLocations.asObservable().subscribe(onNext: { [unowned self] res in
if !res.isEmpty {
for i in 0..<res.count {
addMarker(
lat: res[i].address[1],
lng: res[i].address[0],
mapView: mapView,
name: res[i].name,
job: res[i].field,
score: res[i].level
)
}
}
}).disposed(by: disposedBag)
}
private func addMarker(lat: Double, lng: Double, mapView: NMFMapView, name: String, job: String, score: Double) {
let marker = NMFMarker()
marker.position = NMGLatLng(lat: lat, lng: lng)
let windowView = CustomInfoWindowView()
windowView.nameLabel.text = name
windowView.jobLabel.text = job
windowView.scoreLabel.text = "\(score)"
marker.iconImage = NMFOverlayImage(image: windowView.asImage())
marker.mapView = mapView
}
}
and this is my custom view code.
import UIKit
import SnapKit
import Then
class CustomInfoWindowView: UIView {
let customView = UIView().then {
$0.backgroundColor = .clear
}
let windowView = UIView().then {
$0.backgroundColor = R.color.mainColor()
$0.layer.cornerRadius = 10
}
let marker = UIImageView().then {
$0.image = R.image.marker()
}
let nameLabel = UILabel().then {
$0.font = .boldSystemFont(ofSize: 16)
$0.textColor = .white
}
let jobLabel = UILabel().then {
$0.font = .systemFont(ofSize: 12, weight: .medium)
$0.textColor = R.color.underLine()!
}
let starLogo = UIImageView().then {
$0.image = UIImage(systemName: "star.fill")
$0.tintColor = .systemYellow
}
let scoreLabel = UILabel().then {
$0.font = .boldSystemFont(ofSize: 14)
$0.textColor = .white
}
override init(frame: CGRect) {
super.init(frame: frame)
setUpSubViews()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
private func setUpSubViews() {
self.addSubview(customView)
[windowView, marker].forEach({customView.addSubview($0)})
[nameLabel, jobLabel, starLogo, scoreLabel].forEach({self.addSubview($0)})
customView.snp.makeConstraints {
$0.width.equalTo(108)
$0.height.equalTo(100)
}
windowView.snp.makeConstraints {
$0.width.equalToSuperview()
$0.height.equalTo(64)
$0.top.equalTo(0)
}
marker.snp.makeConstraints {
$0.width.equalTo(20)
$0.height.equalTo(27)
$0.top.equalTo(windowView.snp.bottom).offset(10)
$0.centerX.equalTo(windowView.snp.centerX)
}
nameLabel.snp.makeConstraints {
$0.top.leading.equalTo(10)
$0.width.equalTo(10)
}
jobLabel.snp.makeConstraints {
$0.centerY.equalTo(nameLabel.snp.centerY)
$0.leading.equalTo(nameLabel.snp.trailing).offset(5)
}
starLogo.snp.makeConstraints {
$0.top.equalTo(nameLabel.snp.bottom).offset(5)
$0.leading.equalTo(nameLabel.snp.leading)
$0.width.height.equalTo(15)
}
scoreLabel.snp.makeConstraints {
$0.centerY.equalTo(starLogo.snp.centerY)
$0.leading.equalTo(starLogo.snp.trailing).offset(3)
}
}
}
and this is my UIView Extension code.
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { context in
layer.render(in: context.cgContext)
}
}
}
I Use Snapkit, Then, RxSwift, RxCocoa,NMapsMap.
please Help me.
When I run the code .asImage(), It had runtime error.
enter image description here

How to properly place SCNNode on top of the QR Code?

I want to detect QR codes in the vertical plane and place a node on top of the detected QR. For QR detection, I used Vision framework and Arkit to place the nodes as below code. Whenever placing the node, it is not attached to the QR code and placed somewhere else.
Could someone help to figure out what I have done wrong?
class ViewController: UIViewController, ARSCNViewDelegate,ARSessionDelegate{
#IBOutlet var sceneView: ARSCNView!
var qrRequests = [VNRequest]()
var detectedDataAnchor: ARAnchor?
var processing = false
let configuration = ARWorldTrackingConfiguration()
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.delegate = self
self.sceneView.session.delegate = self
self.sceneView.session.run(configuration)
startQrCodeDetection()
}
func startQrCodeDetection() {
let request = VNDetectBarcodesRequest(completionHandler: self.requestHandler)
self.qrRequests = [request]
}
public func session(_ session: ARSession, didUpdate frame: ARFrame) {
DispatchQueue.global(qos: .userInitiated).async {
do {
if self.processing {
return
}
self.processing = true
let imageRequestHandler = VNImageRequestHandler(cvPixelBuffer: frame.capturedImage,
options: [:])
try imageRequestHandler.perform(self.qrRequests)
} catch {
}
}
}
func requestHandler(request: VNRequest, error: Error?) {
if let results = request.results, let result = results.first as? VNBarcodeObservation {
guard let payload = result.payloadStringValue else {return}
var rect = result.boundingBox
let center = CGPoint(x: rect.midX, y: rect.midY)
DispatchQueue.main.async {
self.hitTestQrCode(center: center)
self.processing = false
}
} else {
self.processing = false
}
}
func hitTestQrCode(center: CGPoint) {
print("Hit Test")
if let hitTestResults = self.sceneView?.hitTest(center, types: [.featurePoint, .existingPlaneUsingExtent] ),
let hitTestResult = hitTestResults.first {
if let detectedDataAnchor = self.detectedDataAnchor,
let node = self.sceneView.node(for: detectedDataAnchor) {
node.transform = SCNMatrix4(hitTestResult.worldTransform)
} else {
self.detectedDataAnchor = ARAnchor(transform: hitTestResult.worldTransform)
self.sceneView.session.add(anchor: self.detectedDataAnchor!)
}
}
}
func renderer(_ renderer: SCNSceneRenderer, nodeFor anchor: ARAnchor) -> SCNNode? {
if self.detectedDataAnchor?.identifier == anchor.identifier {
let sphere = SCNSphere(radius: 0.02)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let sphereNode = SCNNode(geometry: sphere)
sphereNode.transform = SCNMatrix4(anchor.transform)
return sphereNode
}
return nil
}
}

Google Map with Cluster and Custom view marker lagging too much while zoomIn and zoomOut

I want to load GoogleMap with custom view in replace of GMSMarker and want to show clustering and I have done that. But I'm facing lagging & memory usage while zoomIn or ZoomOut the Map.
I have load custom view to GMSMarker infoView
Here is my code.
class InitialMapViewController: UIViewController, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
private var clusterManager: GMUClusterManager!
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.initializeClusterItems()
self. setMapView()
}
//MARK:- Map view actions
func setMapView() {
mapView.delegate = self
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
mapView.mapType = .normal
self.mapView.camera = self.defaultCamera(latitude: SharedData.sharedInstance.userLocation.coordinate.latitude, longitude: SharedData.sharedInstance.userLocation.coordinate.longitude)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.getAllFriendsList()
}
//MARK: INITIALIZE CLUSTER ITEMS
func initializeClusterItems() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUGridBasedClusterAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
guard let _ = marker.userData as? POIItem else { return }
if UIImage(named: "profile_circle_gradiant") != nil {
marker.iconView = UIImageView(image: nil, highlightedImage: nil)
}
}
private func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
}
func defaultCamera(latitude: Double, longitude: Double) -> GMSCameraPosition {
let camera = GMSCameraPosition.camera(withLatitude: latitude,
longitude: longitude,
zoom: 16)
return camera
}
func setMarkers() {
for i in 0..<SharedData.sharedInstance.allFriends.count {
let marker = GMSMarker()
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
//marker.icon = imgTypeDoctor
marker.accessibilityHint = String(i)
let infoWindow = Bundle.main.loadNibNamed("HomeMapInfoView", owner: self, options: nil)?.first as! HomeMapInfoView
//infoWindow.imgUser.sd_setImage(with: URL(string: friend.user_details.user_photo), placeholderImage: #imageLiteral(resourceName: "User_profile"), options: .highPriority, completed: nil)
infoWindow.imgCar.image = UIImage.init(named: "small_" + friend.user_details.car_personality_name)
infoWindow.imgCar1.image = UIImage.init(named: "small_" + friend.user_details.car_personality_name)
// infoWindow.lblName.text = friend.user_details.name
// infoWindow.lblUsername.text = "#" + friend.user_details.screen_name
//KULDEEP 01/03/2019
infoWindow.btnImgVW.tag = Int(marker.accessibilityHint!) ?? 0
infoWindow.isUserInteractionEnabled = false
infoWindow.btnImgVW.addTarget(self, action: #selector(btnUserTapped(_:)), for: .touchUpInside)
marker.iconView = infoWindow
marker.tracksViewChanges = true
//marker.userData = friend
marker.map = mapView
//mapView.selectedMarker = marker
//print(i)
self.generatePOIItems(String(format: "%d", i), position: marker.position, icon: nil)
}
self.clusterManager.cluster()
//self.clusterManager.setDelegate(self, mapDelegate: self)
}
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
UIView.animate(withDuration: 5.0, animations: { () -> Void in
//self.londonView?.tintColor = .blue
}, completion: {(finished) in
// Stop tracking view changes to allow CPU to idle.
//mapView.selectedMarker?.tracksViewChanges = false
})
}
func renderer(_ renderer: GMUClusterRenderer, markerFor object: Any) -> GMSMarker? {
let marker = GMSMarker()
if object is POIItem {
// set image view for gmsmarker
}
return marker
}
func generatePOIItems(_ accessibilityLabel: String, position: CLLocationCoordinate2D, icon: UIImage?) {
let item = POIItem(position: position, name: accessibilityLabel)
self.clusterManager.add(item)
}
}
Please guide me why map is lagging when I zoomIn or zoomOut the Map or what am I missing on this code?
Here I fix the map lagging issue and for that I have done the following things.
Instead of loading all marker iconView at the starting, I load it inside willRenderMarker so it initially load the 1 or 2 marker iconView and when I zoomin the map clustering expand and marker iconView displays.
Here is the working code.
class InitialMapViewController: UIViewController, GMUClusterManagerDelegate, GMUClusterRendererDelegate {
private var clusterManager: GMUClusterManager!
#IBOutlet weak var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.initializeClusterItems()
self. setMapView()
}
//MARK:- Map view actions
func setMapView() {
mapView.delegate = self
mapView.isMyLocationEnabled = true
mapView.settings.myLocationButton = true
mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
mapView.mapType = .normal
self.mapView.camera = self.defaultCamera(latitude: SharedData.sharedInstance.userLocation.coordinate.latitude, longitude: SharedData.sharedInstance.userLocation.coordinate.longitude)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.getAllFriendsList()
}
//MARK: INITIALIZE CLUSTER ITEMS
func initializeClusterItems() {
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUGridBasedClusterAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
renderer.delegate = self
self.clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
}
func renderer(_ renderer: GMUClusterRenderer, willRenderMarker marker: GMSMarker) {
marker.groundAnchor = CGPoint(x: 0.5, y: 1)
if let markerData = (marker.userData as? POIItem) {
let infoWindow = Bundle.main.loadNibNamed("InitialMapInfoView", owner: self, options: nil)?.first as! InitialMapInfoView
infoWindow.imgUser.sd_setImage(with: URL(string: markerData.friend.user_details.user_photo_small), placeholderImage: #imageLiteral(resourceName: "User_profile"), options: .highPriority, completed: nil)
infoWindow.imgCar.image = UIImage.init(named: "small_" + markerData.friend.user_details.car_personality_name)
infoWindow.lblName.text = markerData.friend.user_details.name
infoWindow.btnImgVW.tag = markerData.userIndex
infoWindow.btnImgVW.addTarget(self, action: #selector(btnUserTapped(_:)), for: .touchUpInside)
marker.accessibilityHint = String(markerData.userIndex)
marker.iconView = infoWindow
marker.tracksViewChanges = false
}
}
func clusterManager(_ clusterManager: GMUClusterManager, didTap cluster: GMUCluster) -> Bool {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
return false
}
func defaultCamera(latitude: Double, longitude: Double) -> GMSCameraPosition {
let camera = GMSCameraPosition.camera(withLatitude: latitude,
longitude: longitude,
zoom: 16)
return camera
}
func setMarkers() {
for i in 0..<SharedData.sharedInstance.allFriends.count {
let marker = GMSMarker()
let friend = SharedData.sharedInstance.allFriends[i]
marker.position = CLLocationCoordinate2D.init(latitude: friend.user_details.latitude , longitude: friend.user_details.longitude)
marker.accessibilityHint = String(i)
marker.icon = nil
marker.tracksViewChanges = true
marker.map = mapView
self.generatePOIItems(String(format: "%d", i), position: marker.position, icon: nil, friend: friend, userIndex: i)
}
clusterManager.cluster()
clusterManager.setDelegate(self, mapDelegate: self)
}
func generatePOIItems(_ accessibilityLabel: String, position: CLLocationCoordinate2D, icon: UIImage?) {
let item = POIItem(position: position, name: accessibilityLabel)
self.clusterManager.add(item)
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if marker.accessibilityHint != nil {
let friend = SharedData.sharedInstance.allFriends[Int(marker.accessibilityHint!) ?? 0]
objSelectedUser = friend
self.setUserData(friend: objSelectedUser)
self.showUserLatestBeeppView()
}
return true
}
}

Route Between two A and B location

There is no error but I can not get the location from my current location to what I search. Can someone please help. It says this with I click on the Car logo to get directions: MKDirectionsErrorCode=7, NSLocalizedDescription=Directions Not Available}
I am only missing the line to get to the direction. When I click on the car logo, it just does not do anything but zoom out to my current location. I am missing anything or did something wrong?
Here is my code:
protocol HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark)
}
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView! // Handle the Map Kit View
var selectedPin:MKPlacemark? = nil //Any incoming placemarks
// Destination for directions*********
var destination:MKMapItem = MKMapItem()
var MyPosition = CLLocationCoordinate2D()
var resultSearchController:UISearchController? = nil // Keep the UISearchController in memory after it's created
var locationManager = CLLocationManager() // Core Location
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest // Accuracy level
locationManager.requestWhenInUseAuthorization() // Triggers the location permission dialog
locationManager.startUpdatingLocation()
//locationManager.requestLocation() // Trigers a one-time location request
// Set up the search results table
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
// Set up the search bar
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Search for places"
navigationItem.titleView = resultSearchController?.searchBar
// Configure the UISearchController appearance
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
locationSearchTable.mapView = mapView // Passes along a handle of the mapView
locationSearchTable.handleMapSearchDelegate = self // Handles the map search
}
//Getting direction of location
#objc func getDirections(sender: AnyObject){
if let selectedPin = selectedPin {
let mapItem = MKMapItem(placemark: selectedPin)
//let launchOptions = [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving]
//mapItem.openInMaps(launchOptions: launchOptions)
let request = MKDirectionsRequest()
request.source = MKMapItem.forCurrentLocation()
request.destination = destination
request.requestsAlternateRoutes = false
let directions = MKDirections(request: request)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
/*
directions.calculate(completionHandler: {(response: MKDirectionsResponse!, error: Error!) in
if error != nil {
print("Error \(error)")
} else {
self.displayRout(response)
var overlays = self.mapView.overlays
for route in response.routes {
self.mapView.add(route.polyline, level: MKOverlayLevel.aboveRoads)
for next in route.steps {
print(next.instructions)
}
}
}
})
}
*/
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}
}
//destination = MKMapItem(placemark: selectedPin!)
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : CLLocationManagerDelegate {
// When user responds to the permission dialog
private func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
// When location information comes back (Zoom to the user's current location)
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
//****
MyPosition = location.coordinate
locationManager.stopUpdatingLocation()
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
}
}
// Print out the error
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error finding location: \(error.localizedDescription)")
}
}
// Searches the location and city/state
extension ViewController: HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark){
// cache the pin
selectedPin = placemark
// clear existing pins
mapView.removeAnnotations(mapView.annotations)
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "\(city) \(state)"
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.01, 0.01)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
// Shows Pins and Car Logo
extension ViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.pinTintColor = UIColor.red
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPoint.zero, size: smallSquare))
button.setBackgroundImage(UIImage(named: "Car"), for: .normal)
button.addTarget(self, action: #selector(getDirections), for: .touchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView
}
}
Have a look at below code for reference hope it helps :
1 using two locations here as source and as destination and drawing a path according to locations
import UIKit
import CoreLocation
import MapKit
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
let sourceLocation = CLLocationCoordinate2D(latitude: 40.759011, longitude: -73.984472)
let destinationLocation = CLLocationCoordinate2D(latitude: 40.748441, longitude: -73.985564)
// 3.
let sourcePlacemark = MKPlacemark(coordinate: sourceLocation, addressDictionary: nil)
let destinationPlacemark = MKPlacemark(coordinate: destinationLocation, addressDictionary: nil)
// 4.
let sourceMapItem = MKMapItem(placemark: sourcePlacemark)
let destinationMapItem = MKMapItem(placemark: destinationPlacemark)
// 5.
let sourceAnnotation = MKPointAnnotation()
sourceAnnotation.title = "Times Square"
if let location = sourcePlacemark.location {
sourceAnnotation.coordinate = location.coordinate
}
let destinationAnnotation = MKPointAnnotation()
destinationAnnotation.title = "Empire State Building"
if let location = destinationPlacemark.location {
destinationAnnotation.coordinate = location.coordinate
}
// 6.
self.mapView.showAnnotations([sourceAnnotation,destinationAnnotation], animated: true )
// 7.
let directionRequest = MKDirectionsRequest()
directionRequest.source = sourceMapItem
directionRequest.destination = destinationMapItem
directionRequest.transportType = .walking
// Calculate the direction
let directions = MKDirections(request: directionRequest)
// 8.
directions.calculate {
(response, error) -> Void in
guard let response = response else {
if let error = error {
print("Error: \(error)")
}
return
}
let route = response.routes[0]
self.mapView.add((route.polyline), level: MKOverlayLevel.aboveRoads)
let rect = route.polyline.boundingMapRect
self.mapView.setRegion(MKCoordinateRegionForMapRect(rect), animated: true)
}
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(overlay: overlay)
renderer.strokeColor = UIColor.blue
renderer.lineWidth = 4.0
return renderer
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Output :