bubble icon factory for google maps in iOS - ios

I had earlier used the google map util library to plot bubble icon in google maps and now I would like to do the same for iOS
https://developers.google.com/maps/documentation/android/utility/
I found this pod for iOS https://github.com/googlemaps/google-maps-ios-utils
Has anybody used it to create bubble icons and if so how do i create a bubble icons for iOS ?

Unfortunately, there is no such utility for IOS. I needed a similar solution, so I've come with the below.
The below function draws a left arrow and returns an UIImage. Note that one can draw the arrow by using only paths. Also, one can also use a pre created image from assets instead of creating all the time or create one re-use it.
func drawArrow() ->UIImage {
//the color of the arrow
let color = UIColor.yellow
// First create empty image choose the size according to your need.
let width = 84
let height = 24
let size = CGSize( width: width, height: height)
//let image: UIImage = makeEmptyImage(size: size)
let rectangle = CGRect(x: 12, y: 0, width: 72, height: 24)
UIGraphicsBeginImageContextWithOptions( size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
//clear the background
context?.clear(CGRect(x: 0, y: 0, width: width, height: height))
//draw the rectangle
context?.addRect(rectangle)
context?.setFillColor(color.cgColor)
context?.fill(rectangle)
//draw the triangle
context?.beginPath()
context?.move( to: CGPoint(x : 12 ,y : 0) )
context?.addLine(to: CGPoint(x : 0, y : 12) )
context?.addLine(to: CGPoint(x : 12, y : 24) )
context?.closePath()
context?.setFillColor(color.cgColor)
context?.fillPath()
// get the image
let image2 = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image2
}
The next function, takes the image and draw a text onto it.
func drawText(text:NSString, inImage:UIImage) -> UIImage? {
let font = UIFont.systemFont(ofSize: 11)
let color = UIColor.black
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 : color
]
let myInsets = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 0)
let newImage = inImage.resizableImage(withCapInsets: myInsets)
print("rect.width \(inImage.size.width) rect.height \(inImage.size.height)")
let textSize = text.size(withAttributes: attributes as? [NSAttributedString.Key : Any] )
let textRect = CGRect(x: 10, y: 5, width: textSize.width, height: textSize.height)
UIGraphicsBeginImageContextWithOptions(CGSize(width: textSize.width + 20, height: textSize.height + 10), false, 0.0)
newImage.draw(in: CGRect(x: 0, y: 0, width: textSize.width + 20 , height: textSize.height + 10))
text.draw(in: textRect.integral, withAttributes: attributes as? [NSAttributedString.Key : Any] )
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
so we can use it like
marker.icon = drawText(text: eLoc.name as NSString, inImage: drawArrow() )
marker.groundAnchor = CGPoint(x: 0, y: 0.5)
//If you need some rotation
//let degrees = CLLocationDegrees(exactly: 90.0)
//marker.rotation = degrees!
groundAnchor moves the position of the marker icon, see this anwser. With degree you can rotate the arrow.
Note : Unfortunately, IOS doesn't support 9-patch images. Therefore, not all of the bubble icon factory can be programmed by using resizableImage, see this nice web site and also see this answer
An example output is;

Have you check out the customize icon documentation for google maps ios sdk? It's a good start.

Related

When using UIGraphicsBeginImageContextWithOptions, the text color cannot be adjusted

hi i am a beginner in speed writing
I refer to this writing method and apply it in my segment controller
I am using swift 5.7 method
This can be successfully used and executed with
But i have a problem that i can't change the color of my text and icon
I try to add something in "string.draw" but it dosen't work
The method I use is as follows
class func textEmbededImage(image: UIImage, string: String, color: UIColor, imageAlignment: Int = 0) -> UIImage {
let font = UIFont.systemFont(ofSize: 13.0)
let expectedTextSize: CGSize = (string as NSString).size(withAttributes: [NSAttributedString.Key.font: font])
let width: CGFloat = expectedTextSize.width + image.size.width + 5.0
let height: CGFloat = max(expectedTextSize.height, image.size.width)
let size: CGSize = CGSize(width: width, height: height)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
let context: CGContext = UIGraphicsGetCurrentContext()!
context.setFillColor(color.cgColor)
let fontTopPosition: CGFloat = (height - expectedTextSize.height) / 2.0
let textOrigin: CGFloat = (imageAlignment == 0) ? image.size.width + 5 : 0
let textPoint: CGPoint = CGPoint.init(x: textOrigin, y: fontTopPosition)
string.draw(at: textPoint, withAttributes: [.font : font , .foregroundColor : UIColor.gray])
let flipVertical: CGAffineTransform = CGAffineTransform(a: 1, b: 0, c: 0, d: -1, tx: 0, ty: size.height)
context.concatenate(flipVertical)
let alignment: CGFloat = (imageAlignment == 0) ? 0.0 : expectedTextSize.width + 5.0
context.draw(image.cgImage!, in: CGRect.init(x: alignment, y: ((height - image.size.height) / 2.0), width: image.size.width, height: image.size.height))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext(
return newImage!
}
and i use like this
customSegment.setImage(UIImage.textEmbededImage(image: UIImage.add, string: "QQQ", color: .red),forSegmentAt: 0)
customSegment.setImage(UIImage.textEmbededImage(image: UIImage.add, string: "AAA", color: .white),forSegmentAt: 1)
customSegment.setImage(UIImage.textEmbededImage(image: UIImage.add, string: "BBB", color: .red),forSegmentAt: 2)
but this doesn't work for
I went into debug view hierarchy xcode to try to find the problem
debug view hierarchy
But what puzzles me even more is that what I see on the right layer is the gray color I set, but what I see on the screen is the blue color set by the system
How can I solve it
In addition, I also want to ask, how to make different text colors to achieve the effect when selecting

Error - Expressions are not allowed at the top level

I am having some trouble running this code, I found it on another Stack Overflow post. ARKit: How can I add a UIView to ARKit Scene?
Would like to see if it works for me. But I can't figure out why there is the error "Expressions are not allowed at the top level".
let text = "Hello, Stack Overflow."
let font = UIFont(name: "Arial", size: CGFloat(12))
let width = 128
let height = 128
let fontAttrs: [NSAttributedString.Key: Any] =
[NSAttributedString.Key.font: font as! UIFont]
let renderer = UIGraphicsImageRenderer(size: CGSize(width: CGFloat(width),
height: CGFloat(height)))
let image = renderer.image { context in
let color = UIColor.blue.withAlphaComponent(CGFloat(0.5))
color.setFill()
context.fill(CGRect(
origin: CGPoint(x: 0, y: 0),
size: UIScreen.main.bounds.size))
text.draw(with: (CGRect( origin: CGPoint(x: 0, y: 0), size:
UIScreen.main.bounds.size)), options: .usesLineFragmentOrigin, attributes:
fontAttrs, context: nil)
}
let plane = SCNPlane(width: CGFloat(0.1), height: CGFloat(0.1))
plane.firstMaterial?.diffuse.contents = image
let node = SCNNode(geometry: plane)
The error "Expressions are not allowed at the top level"
is found on the line of code that says:
plane.firstMaterial?.diffuse.contents = image
Thank you in advance.

iOS swift: Image from label in TabBarItem

I'm trying to create a gray circle with name initials indie of it. And use it as tabBarItem image.
This is the function that I use to create the circle, and it almost works, as you can see in the image below, but it's a square not a circle:
static func createAvatarWithInitials(name: String, surname: String, size: CGFloat) -> UIImage {
let initialsLabel = UILabel()
initialsLabel.frame.size = CGSize(width: size, height: size)
initialsLabel.textColor = UIColor.white
initialsLabel.text = "\(name.characters.first!)\(surname.characters.first!)"
initialsLabel.textAlignment = .center
initialsLabel.backgroundColor = UIColor(red: 74.0/255, green: 77.0/255, blue: 78.0/255, alpha: 1.0)
initialsLabel.layer.cornerRadius = size/2
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(initialsLabel.frame.size, false, scale)
initialsLabel.layer.render(in: UIGraphicsGetCurrentContext()!)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
image.withRenderingMode(.alwaysOriginal)
return image
}
and this is how I call it inside the viewDidLoad of the tabBarController:
if index == positionForProfileItem {
tabBarItem.image = UIImage.createAvatarWithInitials(name: "John", surname: "Doe", size: CGFloat(20))
}
but I got an empty squared rectangle in the tab bar.. And I can't figure it out the reason, any help?
The UIImage withRenderingMode method returns a new image. It does not modify the sender.
Change the last two lines to just one:
return image.withRenderingMode(.alwaysOriginal)
But that is not what you wish to do. Since you want a template image of a circle with the text in the middle, you need to do the following:
static func createAvatarWithInitials(name: String, surname: String, size: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSize(width: size, height: size), false, 0)
let ctx = UIGraphicsGetCurrentContext()!
// Draw an opague circle
UIColor.white.setFill()
let circle = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: size, height: size))
circle.fill()
// Setup text
let font = UIFont.systemFont(ofSize: 17)
let text = "\(name.characters.first!)\(surname.characters.first!)"
let textSize = (text as NSString).size(withAttributes: [ .font: font ])
// Erase text
ctx.setBlendMode(.clear)
let textRect = CGRect(x: (size - textSize.width) / 2, y: (size - textSize.height) / 2, width: textSize.width, height: textSize.height)
(text as NSString).draw(in: textRect, withAttributes: [ .font: font ])
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
You have to add clipsToBounds=YES on your label for cornerRadius to have efect on your screen shoot, and also try to increase a little bit the size of your image from 20 to 30.

Draw image and text into a single image

I'm attempting to combine both an image and a text field into a single image whilst still keeping the texts initial positioning.
I'm using UIGraphicsBeginImageContext to create a bitmap context and UIGraphicsGetImageFromCurrentImageContext to draw the final image.
So far, I have the following contained within a function:
let size = CGSize(width: self.takenImage.size.width, height: self.takenImage.size.height)
UIGraphicsBeginImageContextWithOptions(takenImage.size, false, takenImage.scale)
let areaSize = CGRect(x: 0, y: 0, width: self.takenImage.size.width, height: self.takenImage.size.height)
takenImage.draw(in: areaSize)
let imageViewSize = self.takenImage.size
let multiWidth = areaSize.width / imageViewSize.width
let multiHeight = areaSize.height / imageViewSize.height
print ("multiWidth = \(multiWidth)")
print ("multiHeight = \(multiHeight)")
let textSize = CGRect(x: textOverlay.frame.origin.x * multiWidth, y: textOverlay.frame.origin.y * multiHeight, width: textOverlay.frame.size.width * multiWidth, height: textOverlay.frame.size.height * multiHeight)
textOverlay.drawText(in: textSize)
let outputImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.finalImage = outputImage
takenImage is the taken image from my camera (never null) and textOverlay is a UITextField containing the wanted text.
I first create the bitmap and draw takenImage using both its original width/height.
If I draw just this to my finalImage, all works fine. The problem stems from trying to add the text and keep it in the same position.
I've tried to create a second CGRect with x, y, w, h coordinates from the UITextField : textOverlay but when viewing the final image, I'm getting weird results.
The images can be seen here.
How would I go about preserving the text's position in the merged image?
you need recalculate area size to draw text.
let areaSize = CGRect(x: 0, y: 0, width: self.takenImage.size.width, height: self.takenImage.size.height)
takenImage.draw(in: areaSize)
let imageViewSize = self.imageView.size
let multiWidth = areaSize.width / imageViewSize.width
let multiHeight = areaSize.height / imageViewSize.height
let textSize = CGRect(x: textOverlay.frame.origin.x * multiWidth, y: textOverlay.frame.origin.y * multiHeight, width: textOverlay.frame.size.width * multiWidth, height: textOverlay.frame.size.height * multiHeight)
textOverlay.drawText(in: textSize)
Here, I use self.imageView - it is imageView contains your takenImage.
Hope it help.

google maps iOS SDK: custom icons to be used as markers

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:
is a simple rectangle with the color of my choosing.
resizes to hold text of any length.
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.
// 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
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
And my marker image was
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!!
Here is what i have done for solving the same issue, that you are facing.
I have added below image in my image assets,
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:
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
}
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
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
}
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
}
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.
//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")!)
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.

Resources