Guide Area
Guidearea banner

Swift Draw text on image programmatically and use it as Google Maps Marker

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.

Rate this post

Vladimir Marton

Software Developer focused on Java, Python, PHP and SQL. Guidearea is my oldest project where I write articles about programming, marketing, SEO and others.

3 comments

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • 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.