This article will show you how to create a custom image from combination of background image and text over it (= draw text on image) and use it as a Google Maps Marker icon in GMSMarker object.
There is a lot of ways to implement this. I have come across at least five of them. The only problem is that they were not memory-friendly, cause a lot of them caused memory leaks. I have an application where I draw from 300 to 500 markers on the map every 5 seconds and in case of 350 markers, the application takes up 305 MB of RAM. Every time I’d redraw a marker, this error would come up:
Reached the max number of texture atlases, can not allocate more.
Failed to allocate texture space for marker
I will show you an approach which in case of 500 markers takes 205 MB. It is still a lot,ย so there could be some optimization, but it is a good start. Let’s go!
Create a marker and set its position
Use your own latitude and longitude.
let lat = your_latitude let long = your_longitude let marker = GMSMarker() marker.position = CLLocationCoordinate2DMake(lat,long)
Create a dynamic view
… and specify your own icon size. You will convert this view into an icon later during the process.
let DynamicView=UIView(frame: CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 40))) DynamicView.backgroundColor=UIColor.clear
Create UIImageView from image file
Select your own file name, in this case we use file called ‘mrk‘.
var imageViewForPinMarker : UIImageView imageViewForPinMarker = UIImageView(frame:CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 40))) imageViewForPinMarker.image = UIImage(named:"mrk")
Create your label with custom text, set its size and alignment
let text = UILabel(frame:CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 40, height: 30))) text.text = "your custom text" text.font = UIFont(name: text.font.fontName, size: 10) text.textAlignment = NSTextAlignment.center
Add the label to UIImageView and add this view into DynamicView (draw text on image)
Adding the subview (your label) to UIImageView will make it appear on the top of the image – basically it means you draw text on image. By adding subview, you specify that the image will have z-index of 0 and label will have z-index of 1, which will make it on top. After that, you add it to DynamicView in order to compress it to icon.
imageViewForPinMarker.addSubview(text) DynamicView.addSubview(imageViewForPinMarker)
Convert DynamicView to icon and insert it into the marker
UIGraphicsBeginImageContextWithOptions(DynamicView.frame.size, false, UIScreen.main.scale) DynamicView.layer.render(in: UIGraphicsGetCurrentContext()!) let imageConverted: UIImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() marker.icon = imageConverted marker.map = self.mapView
And that is all. If you check the resources of your app, it will now have much smaller RAM consumption and will not run into any memory problems, no matter if you add 10 of these markers, or 1000. The memory consumption using native marker icons would in case of 350 markers be around 80-90 MB, so there is still a room for optimization, but it is much more acceptable than the previous result.
The whole code:
let lat = your_latitude let long = your_longitude let marker = GMSMarker() marker.position = CLLocationCoordinate2DMake(lat,long) let DynamicView=UIView(frame: CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 40))) DynamicView.backgroundColor=UIColor.clear var imageViewForPinMarker : UIImageView imageViewForPinMarker = UIImageView(frame:CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 40))) imageViewForPinMarker.image = UIImage(named:"mrk") let text = UILabel(frame:CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 40, height: 30))) text.text = "your custom text" text.font = UIFont(name: text.font.fontName, size: 10) text.textAlignment = NSTextAlignment.center imageViewForPinMarker.addSubview(text) DynamicView.addSubview(imageViewForPinMarker) UIGraphicsBeginImageContextWithOptions(DynamicView.frame.size, false, UIScreen.main.scale) DynamicView.layer.render(in: UIGraphicsGetCurrentContext()!) let imageConverted: UIImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() marker.icon = imageConverted marker.map = self.mapView
I wrote this article as simple as I possibly could, so I hope you can understand it and it helped you.
For more articles fromย iOS category, proceed here.
Thanks Vladimir,
I also think showing a picture of what the final result looks like would be helpful. I’ve been looking for a solution for a while now I am going to give it a try anyway.. but I don’t exactly know what I am getting in to ๐
Thanks!
Ryan
I think it would be a good idea if you posted the whole code as well, so the reader wouldn’t have to paste individual snippets to run it.
Thanks for the feedback, added the whole code at the end of article.