I am implementing Google Maps SDK in a SwiftUI project, In this project I bind markers according with a list of order's locations that I request from server.
I am already binding the markers on map, but now I want to centralize the maps with the right zoom on markers, I already did this on the android app version using bounds, and now I want to do the same on swift. But neither mapView.moveCamera() and mapView.animate() are working.
I am requesting the camera change in updateUIView(_ mapView: GMSMapView, context: Context) after that I bind the markers.
The code:
import SwiftUI
import GoogleMaps
import shared
struct GoogleMapsView: UIViewRepresentable {
private let MARKER_SIZE = 40
private let zoom: Float = 15.0
private let bounds: GMSCoordinateBounds = GMSCoordinateBounds()
#Binding var mappedOrders: Dictionary<Int32, [OrderDTO]>
#Binding var filteredOrders: [OrderItem]
#Binding var showAlert: Bool
#Binding var wichAlert: Int
func makeUIView(context: Self.Context) -> GMSMapView {
let camera = GMSCameraPosition.camera(withLatitude: 40.4637, longitude: 3.7492, zoom: 6.0)
let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = context.coordinator
return mapView
}
func updateUIView(_ mapView: GMSMapView, context: Context) {
mapView.clear()
for (key) in mappedOrders.keys {
let orderList: [OrderDTO] = mappedOrders[key]!
orderList.filter { order in
return order.deliveryStateString == "rider_assigned" ||
order.deliveryStateString == "delivery_in_progress" ||
order.deliveryStateString == "pending_rider"
}.forEach{ order in
switch order.deliveryState {
case .deliveryInProgress:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
break
case .pendingRider:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
if(order.restaurantLocation != nil) {
createRestaurantMarker(order: order).map = mapView
}
break
case .riderAssigned:
if (order.deliveryLocation != nil) {
createDeliveryPointMarker(order: order).map = mapView
}
if(order.restaurantLocation != nil) {
createRestaurantMarker(order: order).map = mapView
}
break
default:
break
}
}
}
let update = GMSCameraUpdate.fit(bounds)
mapView.animate(with: update)
}
private func createDeliveryPointMarker(order: OrderDTO) -> GMSMarker {
let location = CLLocationCoordinate2DMake(
order.restaurantLocation!.latitude,
order.restaurantLocation!.longitude
)
let marker = GMSMarker(
position: location
)
bounds.includingCoordinate(location)
marker.title = order.user
marker.userData = MarkerDataStored(
order: order,
isClientMarker: true
)
let iconName:String
switch order.deliveryState {
case .pendingRider:
iconName = "flag_red"
break
case .riderAssigned:
iconName = "flag_blue"
break
default:
iconName = "flag_green"
break
}
marker.isTappable = true
marker.icon = resizeImage(
image: UIImage(named: iconName)!,
scaledToSize: CGSize(width: MARKER_SIZE, height: MARKER_SIZE)
)
return marker
}
private func createRestaurantMarker(order: OrderDTO) -> GMSMarker {
let marker = GMSMarker(
position: CLLocationCoordinate2DMake(
order.deliveryLocation!.latitude,
order.deliveryLocation!.longitude
)
)
marker.title = order.user
marker.userData = MarkerDataStored(
order: order,
isClientMarker: false
)
let iconName:String
switch order.deliveryState {
case .pendingRider:
iconName = "restaurant_red"
break
default:
iconName = "restaurant_blue"
break
}
marker.isTappable = true
marker.icon = resizeImage(
image: UIImage(named: iconName)!,
scaledToSize: CGSize(width: MARKER_SIZE, height: MARKER_SIZE)
)
return marker
}
func makeCoordinator() -> MapsDelegate {
let delegate = MapsDelegate()
delegate.onClickMarker = { marker in
filterByRestaurant(marker: marker)
wichAlert = OPEN_RESTAURANT_ORDERS
showAlert = true
}
return delegate
}
private func filterByRestaurant(marker: GMSMarker) {
let data: MarkerDataStored = marker.userData as! MarkerDataStored
filteredOrders.removeAll()
if (data.isClientMarker) {
filteredOrders.append(OrderItem(order: data.order))
} else {
self.mappedOrders[data.order.commercialPremiseId]?.forEach() { order in
filteredOrders.append(OrderItem(order: order))
}
}
}
}
struct MarkerDataStored {
let order: OrderDTO
let isClientMarker: Bool
}
The correct way is :
Change this line
bounds.includingCoordinate(location)
with this
bounds = bounds.includingCoordinate(location)
Related
I am trying to build an app which loops through overlay tiles. The problem is the map tiles take forever to reload when the map displays. What is the best way around this issue? I'm not sure if it is a caching issue which I think MapKit does itself. My guess is it is a Swift redrawing issue. My code is below, and I appreciate your help.
// Copyright 2020 Oklahoma Weather Blog
//
import SwiftUI
import MapKit
import SwiftSoup
/*
struct RadarMapView: UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
var tileRenderer = MKOverlayRenderer()
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
guard let tileOverlay = overlay as? MKTileOverlay else {
return MKOverlayRenderer()
}
return MKTileOverlayRenderer(tileOverlay: tileOverlay)
}
func makeUIView(context: Context) -> MKMapView {
var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
template = "https://tilecache.rainviewer.com/v2/ radar/1600575600/512/{z}/{x}/{y}/6/0_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlay.canReplaceMapContent = false
let mapView = MKMapView(frame: .zero)
var renderedOverlay = MKTileOverlayRenderer(tileOverlay: overlay)
mapView.addOverlay(overlay, level: .aboveLabels)
mapView.setNeedsDisplay()
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 0.3, longitudeDelta: 0.3)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
}
}
*/
func getTimesURL() -> [String] {
let myURLString = "https://api.rainviewer.com/public/maps.json"
guard let myURL = URL(string: myURLString) else {
printToConsole("Error: \(myURLString) doesn't seem to be a valid URL")
return []
}
do {
let myHTMLString = try String(contentsOf: myURL)
do {
let doc: Document = try SwiftSoup.parse(myHTMLString)
let text = try doc.text()
let resultArray = text.trimmingCharacters(in: CharacterSet(charactersIn: "[]"))
.components(separatedBy:",")
return resultArray
} catch Exception.Error( _, let message) {
printToConsole(message)
} catch {
printToConsole("error")
}
} catch let error as NSError {
printToConsole("Error: \(error)")
}
return []
}
func getOverlays() -> [MKTileOverlay] {
var overlays: [MKTileOverlay] = []
for time in getTimesURL() {
let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/256/{z}/{x}/{y}/7/1_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlays.append(overlay)
}
return overlays
}
struct RadarView: View {
private static let mapStyles: [MKMapType] = [.hybrid, .hybridFlyover, .mutedStandard, .satellite, .satelliteFlyover, .standard]
#State var mapTime = "1600585200"
let cache = NSCache<NSString, MKTileOverlay>()
#AppStorage("selectedMapStyle") var selectedMapStyle = 0
#State private var showingSheet = false
private static var overlayArray: [MKTileOverlay] {
getOverlays()
}
private static var timeArray: [String] {
getTimesURL()
}
func dateToString(_ epoch: String) -> String{
let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateFormat = "MM/dd hh:mm a"
let date = Date(timeIntervalSince1970: TimeInterval(Int(epoch)!))
return dateFormatterPrint.string(from: date)
}
#State private var timeIndex: Double = 0
#State private var loopMap: Bool = false
#State var radarTimer: Timer?
var body: some View {
VStack{
ZStack(alignment: .top) {
RadarMapView(mapStyle: RadarView.mapStyles[selectedMapStyle], overlay: RadarView.overlayArray[Int(timeIndex)]).edgesIgnoringSafeArea(.bottom)
HStack{
VStack{
Slider(value: $timeIndex.animation(.linear), in: 0...9, step: 1)
Text("\(dateToString(RadarView.timeArray[Int(timeIndex)]))")
}
Button(action: {
loopMap.toggle()
if loopMap {
radarTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
// do something here
if RadarView.overlayArray.count > 0 {
withAnimation{
timeIndex = Double((Int(timeIndex) + 1) % RadarView.overlayArray.count )
}
}
}
} else {
radarTimer?.invalidate()
}
}, label: {
if !loopMap { Text("Loop") }
else { Text("Pause") }
})
}.padding(.horizontal).padding(.horizontal).background(Color.init(UIColor.systemBackground).opacity(0.75))
}
HStack(){
Spacer()
Button(action: {
/*
selectedMapStyle = (selectedMapStyle + 1) % mapStyles.count
*/
showingSheet.toggle()
}, label: {
Image(systemName: "map.fill").resizable()
.renderingMode(.template)
.font(.title)
.foregroundColor(Color.primary)
.frame(width: 20, height: 20)
}).padding()
}.padding(.horizontal).padding(.bottom).background(Color.init(UIColor.systemBackground).opacity(0.75))
}.actionSheet(isPresented: $showingSheet) {
ActionSheet(title: Text("What map style do you want?"), message: Text("Please select one option below"), buttons: [
.default(Text("Muted")) { self.selectedMapStyle = 2 },
.default(Text("Satellite")) { self.selectedMapStyle = 3 },
.default(Text("Satellite w/ Roads")) { self.selectedMapStyle = 0 },
.default(Text("Satellite 3-D")) { self.selectedMapStyle = 4 },
.default(Text("3-D Satellite w/ Roads")) { self.selectedMapStyle = 1 },
.default(Text("Standard")) { self.selectedMapStyle = 5 },
.cancel(Text("Dismiss"))
])
}.edgesIgnoringSafeArea(.bottom).navigationBarTitle("Radar")
}
}
struct RadarMapView: UIViewRepresentable {
var mapStyle: MKMapType
var overlay: MKTileOverlay
class Coordinator: NSObject, MKMapViewDelegate {
var parent: RadarMapView
init(_ parent: RadarMapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKTileOverlayRenderer(overlay: overlay)
return renderer
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
return MKMapView()
}
func updateUIView(_ mapView: MKMapView, context: Context) {
// var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
//1600626600
mapView.mapType = self.mapStyle
mapView.delegate = context.coordinator
let overlays = mapView.overlays
mapView.addOverlay(overlay)
let regionRadius: CLLocationDistance = 50000
let location = CLLocationCoordinate2D(latitude: 33.7490, longitude: -84.3880)
let coordinateRegion = MKCoordinateRegion(center: location,
latitudinalMeters: regionRadius * 2.0, longitudinalMeters: regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
for overlay in overlays {
// remove all MKPolyline-Overlays
if overlay is MKTileOverlay {
mapView.removeOverlay(overlay)
}
}
}
}
struct RadarView_Previews: PreviewProvider {
static var previews: some View {
RadarView()
}
}
I had a similar problem a few days ago (with a different tile server) and solved it by using 512px tiles.
My theory is, that almost nobody uses 256px tiles, so servers don't cache them.
Do something like
func getOverlays() -> [MKTileOverlay] {
var overlays: [MKTileOverlay] = []
for time in getTimesURL() {
let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/512/{z}/{x}/{y}/7/1_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlay.tileSize = CGSize(width: 512, height: 512)
overlays.append(overlay)
}
return overlays
}
I would like to take a screenshot of ongoing VideoCaptureView during a call in iOS Swift. I used QuickBlox.
I have used below code that return black image
public extension UIView {
public func snapshotImage() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque,0)
drawHierarchy(in: bounds, afterScreenUpdates: false)
let snapshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return snapshotImage
}
public func snapshotView() -> UIView? {
if let snapshotImage = snapshotImage() {
return UIImageView(image: snapshotImage)
} else {
return nil
}
}
}
let snapshot = view.snapshotView()
Try this
fileprivate func captureUIImageFromUIView(_ view:UIView?) -> UIImage {
guard (view != nil) else{
// if the view is nil (it's happened to me) return an alternative image
let errorImage = UIImage(named: "Logo.png")
return errorImage!
}
// if the view is all good then convert the image inside the view to a uiimage
if #available(iOS 10.0, *) {
let renderer = UIGraphicsImageRenderer(size: view!.bounds.size)
let capturedImage = renderer.image {
(ctx) in
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: true)
}
return capturedImage
} else {
UIGraphicsBeginImageContextWithOptions((view!.bounds.size), view!.isOpaque, 0.0)
view!.drawHierarchy(in: view!.bounds, afterScreenUpdates: false)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return capturedImage!
}
}
Call
let pdfImage = self.captureUIImageFromUIView(self.containerView)
//
// ViewController.swift
// GooglePlace-AutoComplete
//
// Created by infos on 7/10/18.
// Copyright © 2018 infos. All rights reserved.
//
import UIKit
import GoogleMaps
import GooglePlaces
import SwiftyJSON
import Alamofire
enum Location {
case startLocation
case destinationLocation
}
class ViewController: UIViewController , GMSMapViewDelegate , CLLocationManagerDelegate,UITextFieldDelegate {
#IBOutlet weak var googleMaps: GMSMapView!
#IBOutlet weak var startLocation: UITextField!
#IBOutlet weak var destinationLocation: UITextField!
var locationManager = CLLocationManager()
var locationSelected = Location.startLocation
var polyline = GMSPolyline()
var locationStart = CLLocation()
var locationEnd = CLLocation()
override func viewDidLoad() {
super.viewDidLoad()
GMSPlacesClient.provideAPIKey("AIzaSyC55Dq1qPH7EM_uiAVf-8QuxJtf2W1viQs")
GMSServices.provideAPIKey("AIzaSyC55Dq1qPH7EM_uiAVf-8QuxJtf2W1viQs")
// Create a GMSCameraPosition that tells the map to display the
let camera = GMSCameraPosition.camera(withLatitude: 13.082680,
longitude: 80.270718,
zoom: 10.0,
bearing: 30,
viewingAngle: 40)
//Setting the googleView
googleMaps.camera = camera
googleMaps.delegate = self
googleMaps.isMyLocationEnabled = true
googleMaps.settings.myLocationButton = true
googleMaps.settings.compassButton = true
googleMaps.settings.zoomGestures = true
googleMaps.animate(to: camera)
self.view.addSubview(googleMaps)
//Setting the start and end location
let origin = "\(13.082680),\(80.270718)"
let destination = "\(15.912900),\(79.739987)"
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
//Rrequesting Alamofire and SwiftyJSON
Alamofire.request(url).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // HTTP URL response
print(response.data as Any) // server data
print(response.result) // result of response serialization
do {
let json = try JSON(data: response.data!)
let routes = json["routes"].arrayValue
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath.init(fromEncodedPath: points!)
let polyline = GMSPolyline.init(path: path)
polyline.strokeColor = UIColor.blue
polyline.strokeWidth = 2
polyline.map = self.googleMaps
}
}
catch {
}
}
// Creates a marker in the center of the map.
let marker = GMSMarker()
marker.position = CLLocationCoordinate2D(latitude: 28.524555, longitude: 77.275111)
marker.title = "Mobiloitte"
marker.snippet = "India"
marker.map = googleMaps
//28.643091, 77.218280
let marker1 = GMSMarker()
marker1.position = CLLocationCoordinate2D(latitude: 28.643091, longitude: 77.218280)
marker1.title = "NewDelhi"
marker1.snippet = "India"
marker1.map = googleMaps
}
// MARK: function for create a marker pin on map
func createMarker(titleMarker: String, iconMarker: UIImage, latitude: CLLocationDegrees, longitude: CLLocationDegrees) {
let marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(latitude, longitude)
marker.isDraggable=true
marker.title = titleMarker
marker.icon = iconMarker
marker.map = googleMaps
}
//MARK: - Location Manager delegates
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error to get location : \(error)")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
// let camera = GMSCameraPosition.camera(withLatitude: (location?.coordinate.latitude)!, longitude: (location?.coordinate.longitude)!, zoom: 17.0)
let locationMobi = CLLocation(latitude: 28.524555, longitude: 77.275111)
drawPath(startLocation: location!, endLocation: locationMobi)
//self.googleMaps?.animate(to: camera)
self.locationManager.stopUpdatingLocation()
}
// MARK: - GMSMapViewDelegate
func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
googleMaps.isMyLocationEnabled = true
}
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
googleMaps.isMyLocationEnabled = true
if (gesture) {
mapView.selectedMarker = nil
}
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
googleMaps.isMyLocationEnabled = true
return false
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("COORDINATE \(coordinate)") // when you tapped coordinate
}
func didTapMyLocationButton(for mapView: GMSMapView) -> Bool {
googleMaps.isMyLocationEnabled = true
googleMaps.selectedMarker = nil
return false
}
//MARK: - Marker Delegate
func mapView(_ mapView: GMSMapView, didDrag marker: GMSMarker) {
}
func mapView(_ mapView: GMSMapView, didBeginDragging marker: GMSMarker) {
}
func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
self.googleMaps.reloadInputViews()
//self.polyline.map = nil;
print("marker dragged to location: \(marker.position.latitude),\(marker.position.longitude)")
let locationMobi = CLLocation(latitude: marker.position.latitude, longitude: marker.position.longitude)
self.drawPath(startLocation: locationMobi, endLocation: locationEnd)
}
//MARK: - this is function for create direction path, from start location to desination location
func drawPath(startLocation: CLLocation, endLocation: CLLocation)
{
let origin = "\(startLocation.coordinate.latitude),\(startLocation.coordinate.longitude)"
let destination = "\(endLocation.coordinate.latitude),\(endLocation.coordinate.longitude)"
self.polyline.map = nil
//self.googleMaps.clear()
let url = "https://maps.googleapis.com/maps/api/directions/json?origin=\(origin)&destination=\(destination)&mode=driving"
Alamofire.request(url).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // HTTP URL response
print(response.data as Any) // server data
print(response.result as Any) // result of response serialization
do {
let json = try JSON(data: response.data!)
let routes = json["routes"].arrayValue
// print route using Polyline
for route in routes
{
let routeOverviewPolyline = route["overview_polyline"].dictionary
let points = routeOverviewPolyline?["points"]?.stringValue
let path = GMSPath.init(fromEncodedPath: points!)
self.polyline = GMSPolyline.init(path: path)
self.polyline.strokeWidth = 2
self.polyline.strokeColor = UIColor.red
self.polyline.map = self.googleMaps
}
} catch {
}
}
}
// MARK: when start location tap, this will open the search location
#IBAction func openStartLocation(_ sender: UIButton) {
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self
// selected location
locationSelected = .startLocation
// Change text color
UISearchBar.appearance().setTextColor(color: UIColor.black)
self.locationManager.stopUpdatingLocation()
self.present(autoCompleteController, animated: true, completion: nil)
}
// MARK: when destination location tap, this will open the search location
#IBAction func openDestinationLocation(_ sender: UIButton) {
let autoCompleteController = GMSAutocompleteViewController()
autoCompleteController.delegate = self
// selected location
locationSelected = .destinationLocation
// Change text color
UISearchBar.appearance().setTextColor(color: UIColor.black)
self.locationManager.stopUpdatingLocation()
self.present(autoCompleteController, animated: true, completion: nil)
}
// MARK: SHOW DIRECTION WITH BUTTON
#IBAction func showDirection(_ sender: UIButton) {
// when button direction tapped, must call drawpath func
self.drawPath(startLocation: locationStart, endLocation: locationEnd)
}
}
// MARK: - GMS Auto Complete Delegate, for autocomplete search location
extension ViewController: GMSAutocompleteViewControllerDelegate {
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
print("Error \(error)")
}
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
// Change map location
let camera = GMSCameraPosition.camera(withLatitude: place.coordinate.latitude, longitude: place.coordinate.longitude, zoom: 16.0
)
// set coordinate to text
if locationSelected == .startLocation {
if (self.locationManager.location?.coordinate.longitude) != nil {
startLocation.text = "\(place.coordinate.latitude), \(place.coordinate.longitude)"
locationStart = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
createMarker(titleMarker: "Location Start", iconMarker: #imageLiteral(resourceName: "images"), latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
}else {
// handle the error by declaring default value
}
} else {
if (self.locationManager.location?.coordinate.longitude) != nil {
destinationLocation.text = "\(place.coordinate.latitude), \(place.coordinate.longitude)"
locationEnd = CLLocation(latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
createMarker(titleMarker: "Location End", iconMarker: #imageLiteral(resourceName: "images"), latitude: place.coordinate.latitude, longitude: place.coordinate.longitude)
}else {
// handle the error by declaring default value
}
}
self.googleMaps.camera = camera
self.dismiss(animated: true, completion: nil)
}
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
self.dismiss(animated: true, completion: nil)
}
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
public func textFieldShouldReturn(_ textField: UITextField) -> Bool
{
textField.resignFirstResponder()
return true
}
public extension UISearchBar {
public func setTextColor(color: UIColor) {
let svs = subviews.flatMap { $0.subviews }
guard let tf = (svs.filter { $0 is UITextField }).first as? UITextField else { return }
tf.textColor = color
}
}
Here is working code to take a screenshot of ongoing video during a call in QuickBlox
#IBOutlet weak var stackView: UIStackView!
let stillImageOutput = AVCaptureStillImageOutput()
override func viewDidLoad() {
super.viewDidLoad()
QBRTCClient.initializeRTC()
QBRTCClient.instance().add(self)
cofigureVideo()
}
func cofigureVideo() {
QBRTCConfig.mediaStreamConfiguration().videoCodec = .H264
QBRTCConfig.setAnswerTimeInterval(30)
QBRTCConfig.setStatsReportTimeInterval(5)
let videoFormat = QBRTCVideoFormat.init()
videoFormat.frameRate = 30
videoFormat.pixelFormat = .format420f
videoFormat.width = 640
videoFormat.height = 480
self.videoCapture = QBRTCCameraCapture.init(videoFormat: videoFormat, position: .front)
self.videoCapture.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.videoCapture.startSession {
self.stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
if self.videoCapture.captureSession.canAddOutput(self.stillImageOutput) {
self.videoCapture.captureSession.addOutput(self.stillImageOutput)
}
let localView = LocalVideoView.init(withPreviewLayer:self.videoCapture.previewLayer)
self.stackView.addArrangedSubview(localView)
}
}
Take photo Button Click
#IBAction func TakePhotoTapped(_ sender: Any) {
if let videoConnection = stillImageOutput.connection(with: AVMediaType.video) {
stillImageOutput.captureStillImageAsynchronously(from: videoConnection) {
(imageDataSampleBuffer, error) -> Void in
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)
if let image = UIImage(data: imageData!){
// Your image is Here
}
}
}
}
I am working on Swift Google Maps. I cannot get the didTapMarker delegate to execute when the user taps on the marker. I put the mapView.delegate = self in the viewDidLoad method, but it did not help.
import UIKit
import GoogleMaps
import Alamofire
import ObjectMapper
import SwiftyJSON
class ViewController: UIViewController, GMSMapViewDelegate, GMUClusterManagerDelegate, CLLocationManagerDelegate {
var locationManager = CLLocationManager()
lazy var mapView = GMSMapView()
private var clusterManager: GMUClusterManager!
//true - marker clustering / false - map markers without clustering
let isClustering : Bool = false
//true - images / false - default icons
let isCustom : Bool = false
var userLocation: CLLocation = CLLocation(latitude: 0, longitude: 0)
override func viewDidLoad() {
super.viewDidLoad()
// User Location
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.distanceFilter = 10
locationManager.startUpdatingLocation()
mapView.delegate = self
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
userLocation = locations.last!
print("userLocation latitude \(self.userLocation.coordinate.latitude)")
print("userLocation longitude \(self.userLocation.coordinate.longitude)")
let camera = GMSCameraPosition.camera(withLatitude: userLocation.coordinate.latitude,
longitude: userLocation.coordinate.longitude, zoom: 17.0)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.settings.myLocationButton = true
mapView.settings.scrollGestures = true
mapView.settings.zoomGestures = true
mapView.isMyLocationEnabled = true
self.view = mapView
locationManager.stopUpdatingLocation()
//locationManager.delegate = nil
fetch()
}
func fetch () {
let cityLocationService = CityLocationService()
let cityService = cityLocationService.getCityService(latitude: self.userLocation.coordinate.latitude , longitude: self.userLocation.coordinate.longitude)
cityService.fetch(latitude: self.userLocation.coordinate.latitude, longitude: self.userLocation.coordinate.longitude) { (incidents) in
let incidentsJson = incidents
DispatchQueue.main.async(execute: {
self.didGetJson(incidentsJson: incidentsJson)
})
}
}
func didGetJson(incidentsJson: JSON) {
if self.isClustering {
var iconGenerator : GMUDefaultClusterIconGenerator!
if self.isCustom {
var images : [UIImage] = []
for imageID in 1...5 {
images.append(UIImage(named: "m\(imageID).png")!)
}
iconGenerator = GMUDefaultClusterIconGenerator(buckets: [ 10, 50, 100, 200, 500 ], backgroundImages: images)
} else {
iconGenerator = GMUDefaultClusterIconGenerator()
}
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: self.mapView, clusterIconGenerator: iconGenerator)
self.clusterManager = GMUClusterManager(map: self.mapView, algorithm: algorithm, renderer: renderer)
self.addToMap(resultJson: incidentsJson, isCluster: true)
// Call cluster() after items have been added to perform the clustering and rendering on map.
self.clusterManager.cluster()
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
self.clusterManager.setDelegate(self, mapDelegate: self)
} else {
self.addToMap(resultJson: incidentsJson, isCluster: false)
}
self.view = self.mapView
}
func addToMap(resultJson: JSON, isCluster: Bool) {
for(_, subJson): (String, JSON) in resultJson {
let position = self.checkIfMutlipleCoordinates(latitude: subJson["latitude"].floatValue, longitude: subJson["longitude"].floatValue)
let id = subJson["id"]
let type = subJson["type"].stringValue
let address = subJson["address"].stringValue
// let case_number = subJson["case_number"].stringValue
let date = subJson["date"].stringValue
let markerImage = subJson["markerImage"].stringValue
if isCluster {
let item = POIItem(position: position, name: "#\(id)")
// print("pdid name=\(item.name),latitude=\(item.position.latitude),longitude=\(item.position.longitude).")
clusterManager.add(item)
} else {
let marker = GMSMarker(position: position)
marker.title = "#\(id)"
let image = UIImage(named: markerImage)
marker.icon = image?.resized(to: CGSize(width: 30, height: 30))
marker.opacity = 0.6
marker.snippet = "\r\n date: \(date) id # \(id) \r\n address: \(address) \r\n type: \(type)"
marker.map = mapView
}
}
}
func checkIfMutlipleCoordinates(latitude : Float , longitude : Float) -> CLLocationCoordinate2D{
var lat = latitude
var lng = longitude
let variation = (self.randomFloat(min: 0.0, max: 2.0) - 0.5) / 1500
lat = lat + variation
lng = lng + variation
let finalPos = CLLocationCoordinate2D(latitude: CLLocationDegrees(lat), longitude: CLLocationDegrees(lng))
return finalPos
}
func randomFloat(min: Float, max:Float) -> Float {
return (Float(arc4random()) / 0xFFFFFFFF) * (max - min) + min
}
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
//Show the marker title while tapping
func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
print("tapped on marker")
if marker.title == "myMarker"{
print("handle specific marker")
}
return true
}
//Optional Feature:
//Add new markers while tapping at coordinates without markers/clusters
func mapView(mapView: GMSMapView, didTapAtCoordinate coordinate: CLLocationCoordinate2D) {
let item = POIItem(position: coordinate, name: "NEW")
clusterManager.add(item)
clusterManager.cluster()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Add delegate GMSMapViewDelegate
mapView.delegate = self // in viewDidLoad
If you want to print lat and long of the touched marker, this is the code:
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
print("You tapped : \(marker.position.latitude),\(marker.position.longitude)")
return true // or false as needed.
}
And for long press, if you want to redirect to google maps.
func mapView(_ mapView: GMSMapView, didLongPressInfoWindowOf marker: GMSMarker) {
if (UIApplication.shared.canOpenURL(NSURL(string:"comgooglemaps://")! as URL)) {
UIApplication.shared.open(NSURL(string:"comgooglemaps://?saddr=&daddr=\(marker.position.latitude),\(marker.position.longitude)&directionsmode=driving")! as URL, options: [:], completionHandler: nil)
}
else {
let alert = UIAlertController(title: "Google Maps not found", message: "Please install Google Maps in your device.", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
This will work!!!
In Android SDK there is minClusterSize property that when we set it to 1 it show's every elements as cluster not marker.
But in iOS SDK doesn't exist minClusterSize and DefaultRenderer by default show's lower than 3 items as items not cluster.
There is any solution ?
Thanks.
import UIKit
class CustomClusterRenderer: GMUDefaultClusterRenderer {
let GMUAnimationDuration: Double = 0.5
var mapView: GMSMapView?
override init(mapView: GMSMapView, clusterIconGenerator iconGenerator: GMUClusterIconGenerator) {
super.init(mapView: mapView, clusterIconGenerator: iconGenerator)
self.mapView = mapView
}
func markerWith(position: CLLocationCoordinate2D, from: CLLocationCoordinate2D, userData: AnyObject, clusterIcon: UIImage, animated: Bool) -> GMSMarker {
let initialPosition = animated ? from : position
let marker = GMSMarker(position: initialPosition)
marker.userData = userData
marker.icon = clusterIcon
marker.groundAnchor = CGPoint(x: 0.5, y: 0.5)
marker.map = mapView
if animated {
CATransaction.begin()
CAAnimation.init().duration = GMUAnimationDuration
marker.layer.latitude = position.latitude
marker.layer.longitude = position.longitude
CATransaction.commit()
}
return marker
}
func getCustomUIImageItem(userData: AnyObject) -> UIImage {
if let item = userData as? Marker {
return item.merkerIcon
}
return UIImage()
}
override func shouldRender(as cluster: GMUCluster, atZoom zoom: Float) -> Bool {
print("Zoom Level is \(zoom) , and result is \(zoom<=14)")
return zoom <= 14;
}
}
class Marker: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var estate: Estate
init(estate: Estate) {
self.estate = estate
self.position = CLLocationCoordinate2D(latitude: estate.latitude,longitude: estate.longitude)
}
}
add one line on class GMUDefaultClusterRenderer.m
static const NSUInteger kGMUMinClusterSize = 10;
I am working with Google Map for IOS .Where i have implemented clustering but cluster and cluster item marker click event not work . But individual google map marker click event is working .
Here is my full code
import UIKit
import GoogleMaps
import Alamofire
import SwiftyJSON
let kCameraLatitude = 22.3475
let kCameraLongitude = 91.8123
/// Point of Interest Item which implements the GMUClusterItem protocol.
class POIItem: NSObject, GMUClusterItem {
var position: CLLocationCoordinate2D
var currencies = "currencies";
var phone = "phone";
var dateTime = "dateTime";
var location = "location";
var link = "link";
var exchangeLimits = "exhangeLimits";
var id = "id";
var updated_at = "updated";
var operationName = "opeationName";
var email = "email";
var address = "address";
var createdAt = "createdAt";
var workingDays = "workingDays";
var longitude = "longitude";
var latitude = "latitude";
var exchange = "exChange";
init(position: CLLocationCoordinate2D, operationName: String) {
self.position = position
self.operationName = operationName
}
}
class FirstViewController: UIViewController , GMUClusterManagerDelegate, GMSMapViewDelegate{
private var mapView: GMSMapView!
private var clusterManager: GMUClusterManager!
override func loadView() {
let camera = GMSCameraPosition.camera(withLatitude: kCameraLatitude,
longitude: kCameraLongitude, zoom: 6)
mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)
mapView.delegate = self
self.view = mapView
}
override func viewDidLoad() {
super.viewDidLoad()
// Set up the cluster manager with default icon generator and renderer.
let iconGenerator = GMUDefaultClusterIconGenerator()
let algorithm = GMUNonHierarchicalDistanceBasedAlgorithm()
let renderer = GMUDefaultClusterRenderer(mapView: mapView, clusterIconGenerator: iconGenerator)
clusterManager = GMUClusterManager(map: mapView, algorithm: algorithm, renderer: renderer)
getAllMapsData()
clusterManager.cluster()
// Register self to listen to both GMUClusterManagerDelegate and GMSMapViewDelegate events.
clusterManager.setDelegate(self, mapDelegate: self)
}
func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("You tapped at \(coordinate.latitude), \(coordinate.longitude)")
var alert = UIAlertController(title: "Choose Media type" , message: "post any" , preferredStyle: .actionSheet);
var photos = UIAlertAction(title : "Photos" , style: .default , handler: nil)
var videos = UIAlertAction(title : "Videos" , style: .default , handler: nil)
var cancel = UIAlertAction(title : "Calcel" , style: .default , handler: nil)
alert.addAction(photos)
alert.addAction(videos)
alert.addAction(cancel)
present(alert, animated: true , completion: nil)
}
// MARK: - GMUClusterManagerDelegate
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
// MARK: - GMUMapViewDelegate
func mapView(mapView: GMSMapView, didTapMarker marker: GMSMarker) -> Bool {
if let poiItem = marker.userData as? POIItem {
NSLog("Did tap marker for cluster item \(poiItem.operationName)")
print("clicked cluster")
} else {
NSLog("Did tap a normal marker")
print("clicked marker")
}
return false
}
var allDataList = [[String:AnyObject]]()
public func getAllMapsData(){
Alamofire.request("http://coinatmfinder.com/getDatas").responseJSON { response in
if let JSON = response.result.value {
self.allDataList = JSON as! [[String : AnyObject]]
for eachData in self.allDataList {
//let allDataModel = AllDataModel()
// allDataModel.address = eachData["address"] as! String
var operatorName = eachData["operatorName"]
var lat = eachData["latitude"]
var latDouble = 0.0;
if let lats = lat {
latDouble = (lats as! NSString).doubleValue
}
var longs = eachData["longitude"]
var longsDouble = 0.0;
if let longs = longs {
longsDouble = (longs as! NSString).doubleValue
}
let item = POIItem(position: CLLocationCoordinate2DMake(latDouble, longsDouble), operationName: operatorName as! String)
self.clusterManager.add(item)
}
print("JSON: \(self.allDataList)")
}
}
}
}
Was working on this today and also tried the same function and found it wasn't working because it was the wrong one. Instead of:
func clusterManager(clusterManager: GMUClusterManager, didTapCluster cluster: GMUCluster) {
let newCamera = GMSCameraPosition.camera(withTarget: cluster.position,
zoom: mapView.camera.zoom + 1)
let update = GMSCameraUpdate.setCamera(newCamera)
mapView.moveCamera(update)
}
Try this one: (it looks almost the same as the one above except it has a return type of Bool).
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
}
(Using Swift 3)