20

The Android API has a very convenient class for this, IconGenerator. Using the IconGenerator in my Android app, I can easily make a marker that:

  1. is a simple rectangle with the color of my choosing.
  2. resizes to hold text of any length.
  3. is NOT an info window - I'd like the marker itself to contain the text as shown in the image below from the android version.

enter image description here

// Android - problem solved with IconGenerator
IconGenerator iconGenerator = new IconGenerator(context);
iconGenerator.setStyle(IconGenerator.STYLE_GREEN); // or any other color
Bitmap iconBitmap = iconGenerator.makeIcon(myString);
Marker m = new MarkerOptions().icon(BitmapDescriptorFactory.fromBitmap(iconBitmap))
                              .position(myLatLng);
map.addMarker(m); // map is a com.google.android.gms.maps.GoogleMap

Is there a way to do something as simple as this in iOS using Swift? There has been a recent release of the iOS api that allows "marker customization", but I don't see how to apply it to this use case.

// iOS (Swift) - I don't know how to create the icon as in code above
let marker = GMSMarker(position: myLatLng)
marker.icon = // How can I set to a rectangle with color/text of my choosing?
marker.map = map // map is a GMSMapView
lf215
  • 1,185
  • 7
  • 41
  • 83
  • 1
    Thats what I have mentioned, create a `UIView` same as your android which consits of your $ price and then create a `UIImage` from it, which I have mentioned the same function for. – Rajan Maheshwari Oct 24 '16 at 06:40
  • I edited my question before I saw your updated answer. I'll implement this tomorrow. In the meantime can you please be a little more specific on what the UIVIew/label code would look like. – lf215 Oct 24 '16 at 06:44
  • 1
    Ok, I ll try to make a similar view what you want. It is just nothing but a view which will be converted to the image. You can use BezierPaths to achieve the down arrow point shape like view. Also if you don't want what I did on top of marker, simply delete `mapView.selectedMarker = marker` line and it will be a simple marker – Rajan Maheshwari Oct 24 '16 at 06:45

9 Answers9

39

Here is what I have done

let marker = GMSMarker()

// I have taken a pin image which is a custom image
let markerImage = UIImage(named: "mapMarker")!.withRenderingMode(.alwaysTemplate)

//creating a marker view
let markerView = UIImageView(image: markerImage)

//changing the tint color of the image
markerView.tintColor = UIColor.red

marker.position = CLLocationCoordinate2D(latitude: 28.7041, longitude: 77.1025)

marker.iconView = markerView
marker.title = "New Delhi"
marker.snippet = "India"
marker.map = mapView

//comment this line if you don't wish to put a callout bubble
mapView.selectedMarker = marker

The output is

enter image description here

And my marker image was

enter image description here

You can change your color as per your need. Also if you want something in rectange, you can just create a simple small rectangular image and use it like I did above and change the color of your need.

Or if you want a rectangle with text within it, you can just create a small UIView with some label and then convert that UIView in UIImage and can do the same thing.

//function to convert the given UIView into a UIImage
func imageWithView(view:UIView) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, 0.0)
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return image!
}

Hope it helps!!

Rajan Maheshwari
  • 14,465
  • 6
  • 64
  • 98
  • I've tried to use `imageWithView` with [this constraint code](http://stackoverflow.com/a/40567858/1168364): `let label = UILabel()` and `label.widthAnchor.constraintEqualToConstant(50.0).active = true` but I get crash due to `UIGraphicsGetCurrentContext()` inside `imageWithView` being nil. – lf215 Nov 13 '16 at 01:10
  • How to setting center title and snippet ? – Puji Wahono Jul 09 '18 at 08:34
6

Here is what i have done for solving the same issue, that you are facing.

I have added below image in my image assets,

enter image description here

Now i added below method in my code:

-(UIImage*)drawText:(NSString*)text inImage:(UIImage*)image
{
    UIFont *font = [UIFont boldSystemFontOfSize:11];
    CGSize size = image.size;
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
    [image drawInRect:CGRectMake(0, 0, size.width, size.height)];
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);

    NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
    paragraphStyle.alignment = NSTextAlignmentCenter;
    NSDictionary *attributes = @{
                                 NSFontAttributeName : font,
                                 NSParagraphStyleAttributeName : paragraphStyle,
                                 NSForegroundColorAttributeName : [UIColor redColor]
                                 };
    CGSize textSize = [text sizeWithAttributes:attributes];
    CGRect textRect = CGRectMake((rect.size.width-textSize.width)/2, (rect.size.height-textSize.height)/2 - 2, textSize.width, textSize.height);
    [text drawInRect:CGRectIntegral(textRect) withAttributes:attributes];

    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}

Now, I called this method, while assigning icon to GMSMarker, like this:

marker.icon = [self drawText:@"$33.6" inImage:[UIImage imageNamed:@"icon-marker"]];

It will generate the image icon like below:

enter image description here

Here, I kept the background Image size fixed, as i needed. You can still customize it to adjust it according to text size, as well as multiple lines.

UPDATE

Updated code in Swift:

func drawText(text:NSString, inImage:UIImage) -> UIImage? {

        let font = UIFont.systemFont(ofSize: 11)
        let size = inImage.size

        //UIGraphicsBeginImageContext(size)
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(inImage.size, false, scale)
        inImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let style : NSMutableParagraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
        style.alignment = .center
        let attributes:NSDictionary = [ NSAttributedString.Key.font : font, NSAttributedString.Key.paragraphStyle : style, NSAttributedString.Key.foregroundColor : UIColor.black ]

        let textSize = text.size(withAttributes: attributes as? [NSAttributedString.Key : Any])
        let rect = CGRect(x: 0, y: 0, width: inImage.size.width, height: inImage.size.height)
        let textRect = CGRect(x: (rect.size.width - textSize.width)/2, y: (rect.size.height - textSize.height)/2 - 2, width: textSize.width, height: textSize.height)
        text.draw(in: textRect.integral, withAttributes: attributes as? [NSAttributedString.Key : Any])
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()

        return resultImage
}
Mehul Thakkar
  • 12,440
  • 10
  • 52
  • 81
5

You can simply add a custom view as marker in Google Map.

let marker = GMSMarker(position: coordinate)
marker.iconView = view // Your Custom view here

You can use imageView (for containing that orange color box) and label (for text) above it

Bibek
  • 3,689
  • 3
  • 19
  • 28
5

I tried to rewrite Mehul Thakkar answer to Swift 3. Hope it will work for you. But it really easier to make custom view as Dari said.

func drawText(text:NSString, inImage:UIImage) -> UIImage? {

        let font = UIFont.systemFont(ofSize: 11)
        let size = inImage.size

        UIGraphicsBeginImageContext(size)

        inImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let style : NSMutableParagraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
        style.alignment = .center
        let attributes:NSDictionary = [ NSFontAttributeName : font, NSParagraphStyleAttributeName : style, NSForegroundColorAttributeName : UIColor.red ]

        let textSize = text.size(attributes: attributes as? [String : Any])
        let rect = CGRect(x: 0, y: 0, width: inImage.size.width, height: inImage.size.height)
        let textRect = CGRect(x: (rect.size.width - textSize.width)/2, y: (rect.size.height - textSize.height)/2 - 2, width: textSize.width, height: textSize.height)
        text.draw(in: textRect.integral, withAttributes: attributes as? [String : Any])
        let resultImage = UIGraphicsGetImageFromCurrentImageContext()

        UIGraphicsEndImageContext()

        return resultImage
    }
Community
  • 1
  • 1
Eridana
  • 2,418
  • 23
  • 26
2

Here a Swift 5 version of Eridana's Swift conversion of Mehul Thakkar's answer.

func drawTextT(text:NSString, inImage:UIImage) -> UIImage? {

    let font = UIFont.systemFont(ofSize: 11)
    let size = inImage.size

    UIGraphicsBeginImageContext(size)

    inImage.draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
    let style : NSMutableParagraphStyle = NSMutableParagraphStyle.default.mutableCopy() as! NSMutableParagraphStyle
    style.alignment = .center
    let attributes:NSDictionary = [ NSAttributedString.Key.font : font, NSAttributedString.Key.paragraphStyle : style, NSAttributedString.Key.foregroundColor : UIColor.red ]

    //let textSize = text.size(attributes: attributes as? [String : Any])
    let textSize = text.size(withAttributes: attributes as? [NSAttributedString.Key : Any] )

    let rect = CGRect(x: 0, y: 0, width: inImage.size.width, height: inImage.size.height)
    let textRect = CGRect(x: (rect.size.width - textSize.width)/2, y: (rect.size.height - textSize.height)/2 - 2, width: textSize.width, height: textSize.height)
    text.draw(in: textRect.integral, withAttributes: attributes as? [NSAttributedString.Key : Any]  )

    let resultImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()

    return resultImage
}
Yunus Karakaya
  • 531
  • 3
  • 16
0

Simplest way to achieve if you have just 1 image :

 marker.icon = #imageLiteral(resourceName: "fault_marker")

1) In latest XCode write marker.icon = "imageLiteral".

2) Double click the dummy image icon appeared just now.

3) select desired image.

steveSarsawa
  • 1,559
  • 2
  • 14
  • 31
jeet.chanchawat
  • 5,842
  • 5
  • 38
  • 59
0
                    //func to get Image view 
                // Url String :-  Your image coming from server
//image :- Background image
                    func drawImageWithProfilePic(urlString:String, image: UIImage) -> UIImageView {

                        let imgView = UIImageView(image: image)
                        imgView.frame = CGRect(x: 0, y: 0, width: 90, height: 90)

                        let picImgView = UIImageView()
                        picImgView.sd_setImage(with:URL(string: urlString))
                        picImgView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
                        imgView.addSubview(picImgView)

                        picImgView.center.x = imgView.center.x
                        picImgView.center.y = imgView.center.y-10
                        picImgView.layer.cornerRadius = picImgView.frame.width/2
                        picImgView.clipsToBounds = true
                        imgView.setNeedsLayout()
                        picImgView.setNeedsLayout()

                //        let newImage = imageWithView(view: imgView)
                //        return newImage

                        return imgView
            }


    //SHOW ON MAP

         let marker = GMSMarker()
         marker.position = CLLocationCoordinate2D(latitude: Double(lat)!, longitude:  Double(long)!)
        marker.iconView = self.drawImageWithProfilePic(urlString:getProviderImage,image: UIImage.init(named: "red")!)
Davender Verma
  • 503
  • 2
  • 12
0

Simple and easiest way to change icon. just replace these 3 icon (default marker.png) to your icon (1x,2x,3x).

In Google Cluster there was a problem with marker (icon) change.

[1]: https://i.stack.imgur.com/pR3Rx.png

Yehor Androsov
  • 4,885
  • 2
  • 23
  • 40
0

This is how I achieved it, completely programatically:

//custom markers for the map
class CustomMarker: GMSMarker {

    var label: UILabel!

    init(labelText: String, imageName: String) {
        super.init()

        let iconView = UIImageView(frame: CGRect(origin: .zero, size: CGSize(width: 60, height: 60)))
        iconView.image = UIImage(named: imageName)//Assign image to ImageView
        
        
        if(labelText != "1"){
            label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 25, height: 25)))
            label.frame.origin.x = 25;
            
            label.text = labelText
            label.layer.cornerRadius =  label.frame.width/2
            label.layer.masksToBounds = true
            label.backgroundColor = .white
            label.applyBorder(width: 0.5, color: .black)
            label.textAlignment = .center
            iconView.addSubview(label)
        }

        
        self.iconView = iconView
    }
}

Function Call:

let marker = CustomMarker(labelText: addressCount.description, imageName: mapItem.cMapType!.lowercased())

Result:

enter image description here

TharakaNirmana
  • 10,237
  • 8
  • 50
  • 69