How to generate annotation image dynamically in Swift - ios

I am displaying custom roads on the map and to display details about road, I need to display annotation with custom images (a shield).
But now I want to display shields with road numbers. As there are so many different roads, I cannot add all these shield with numbers in app. I want to keep only one shield image in app and write number on the shield dynamically while setting as annotation image.
There is a IconGenerator library in Google Maps API which can do this task. So I want to do the same with MKMapKit in my iOS Swift application.
Thanks in advance.

If you know that your image is always going to be certain dimensions (say, a square) then you know where the white space for the number will be. You can create a DynamicShield class which has the common image and a UILabel which positions the text in accordance with the dimensions of the common shield image.

I have created imageview and added image and label in imageview and set imageview as annotation image.
let annotationview = MKAnnotationView.init(annotation: annotationPoint, reuseIdentifier: "xyz")
annotationview.canShowCallout = true
var imageview = UIImageView(frame: CGRect(x: -15, y: -15, width: 30, height: 30))
var label = UILabel()
label.frame = CGRect(x: 0, y: 1, width: 30, height: 30)
label.textAlignment = .Center
label.font = UIFont.boldSystemFontOfSize(12)
label.textColor = UIColor.GrayColor()
label.text = “my-text”
imageview.addSubview(label)
imageview.image = UIImage(named: “image-name”)
annotationview.addSubview(imageview)
return annotationview

Related

How to set image size and ratio between text into UIButton

I have a UIButton. And finally, I should get this:
No matter how I try, I can't get the desired result. I have read many questions regarding this topic. I tried to experiment with titleEdgeInsets and imageEdgeInsets, but it did not help. The fact is that when I set the image for the button, it takes up the entire content of the button and the text remains behind. Below how i set the title
let button = UIButton()
button.frame = CGRect(x: 150, y: 300, width: 100, height: 20)
button.backgroundColor = .blue
button.layer.borderColor = UIColor.green.cgColor
button.layer.borderWidth = 2
button.setImage(UIImage(named: "baseline_play_arrow_black_48.png"), for: .normal)
button.imageView?.backgroundColor = .red
button.imageView?.contentMode = .scaleAspectFit
button.setTitle("Play", for: .normal)
button.setTitleColor(.green, for: .normal)
button.titleLabel?.backgroundColor = .yellow
As an experiment I had tried in the playground. And code above gave the following result:
.
How can i get the desired result ?
I believe there are a few ways you can achieve this Ahmet, I will go over one idea.
The interesting challenge from your question is that you don't only want to left align the image.
You want to:
Left align the image with respect to the button title only
However, the image along with the text should be center aligned as a whole within the UIButton
I created the below extension which gets you close to your desired result with some comments to explain my thought process:
extension UIButton
{
func configureWithLeftImage(_ imageName: String)
{
// Retrieve the desired image and set it as the button's image
let buttonImage = UIImage(named: imageName)?.withRenderingMode(.alwaysOriginal)
// Resize the image to the appropriate size if required
let resizedButtonImage = resizeImage(buttonImage)
setImage(resizedButtonImage, for: .normal)
// Set the content mode
contentMode = .scaleAspectFit
// Align the content inside the UIButton to be left so that
// image can be left and the text can be besides that on it's right
contentHorizontalAlignment = .left
// Set or compute the width of the UIImageView within the UIButton
let imageWidth: CGFloat = imageView!.frame.width
// Specify the padding you want between UIButton and the text
let contentPadding: CGFloat = 10.0
// Get the width required for your text in the button
let titleFrame = titleLabel!.intrinsicContentSize.width
// Keep a hold of the button width to make calculations easier
let buttonWidth = frame.width
// The UIImage and the Text combined should be centered so we need to calculate
// the x position of the image first.
let imageXPos = (buttonWidth - (imageWidth + contentPadding + titleFrame)) / 2
// Adjust the content to be centered
contentEdgeInsets = UIEdgeInsets(top: 0.0,
left: imageXPos,
bottom: 0.0,
right: 0.0)
}
// Make sure the image is sized properly, I have just given 50 x 50 as random
// Code taken from: https://www.createwithswift.com/uiimage-resize-resizing-an-uiimage/
private func resizeImage(_ image: UIImage?,
toSize size: CGSize = CGSize(width: 50, height: 50)) -> UIImage?
{
if let image = image
{
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
image.draw(in: CGRect(origin: CGPoint.zero, size: size))
let resizedImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return resizedImage
}
return nil
}
}
Then when you want to use it:
// Initialize a UIButton and set its frame
let customButton: UIButton = UIButton(type: .custom)
customButton.frame = CGRect(x: 100, y: 200, width: 150, height: 40)
// Customize the button as you wish
customButton.backgroundColor = .white
customButton.layer.cornerRadius = 5.0
customButton.setTitleColor(.black, for: .normal)
customButton.setTitle("смотрите", for: .normal)
// Call the function we just created to configure the button
customButton.configureWithLeftImage("play_icon")
// Finally add the button to your view
view.addSubview(customButton)
This is the end result produced which I believe is close to your desired goal:
This is the image I used as well inside the UIButton if you want to test it out
Update
The issue with your scenario was correctly identified by you in the comments, the size of your image was too large.
I have updated the above UIButton extension to include an image resize function which should solve the issue.
I have given random sizing for the UIImage inside the button, however you need to size it appropriate to your situation or dynamically calculate size of the image based on available space based on button size.
I also added some code for title insets to make final result better.
Now result works in PlayGround and iOS app:
App using your image
Playground using your image
Update 2.0
As Ahmet pointed out, there were issues calculating the right positions for the image and the title when the length of the string changes using titleInsets and imageInsets, sometimes leading to overlapping of the button and image.
Instead, Adjust the content inset instead as follows (updated in the extension above as well)
// The UIImage and the Text combined should be centered so we need to calculate
// the x position of the image first.
let imageXPos = (buttonWidth - (imageWidth + contentPadding + titleFrame)) / 2
// Adjust the content to be centered
contentEdgeInsets = UIEdgeInsets(top: 0.0,
left: imageXPos,
bottom: 0.0,
right: 0.0)
This will work if the text length of the button title is 1 or several characters

Drawing multiple UIImageViews with the same UIImage

I am trying to draw a line using multiple UIImageView of the same circle image.
I have tried the following:
let imageName = "circle.jpg"
let image = UIImage(named: imageName)
for var i in 0..<100 {
i += 1
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: i, y: 200, width: 25, height: 25)
view.addSubview(imageView)
}
This produces one circle at the position (100, 200). This happens because the same UIImageView is being added to the subview so it is only updating the position rather than adding a new UIImageView. If I create a new UIImageView named "imageView1" and add it to the subview, it will create a new circle.
For the purpose of forming a line by overlapping the UIImageViews of these circles, manually creating a UIImageView for each circle is obviously inefficient.
So, how can I use the same UIImage to draw multiple UIImageViews?
Any other suggestions on how I can get around accomplishing this?
You need a circle to form a line like this
Well here the code I used for this using While loop
let imageName = "synergy-many-people-turning-in-gears_gg55375354.jpg"
let image = UIImage(named: imageName)
var i = 0
while(i<100)
{
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: i, y: 200, width: 25, height: 25)
view.addSubview(imageView)
i = i + 25//Here I set it to the width so that the images don't overlap
//you can use any value for the desired effect it just sets the location
//of X-Co-ordinate where the next image would be added
}
I would quickly try out something like this (didn't test it though):
var imageView = UIImageView(image: image!)
Creating multiple UIImageViews is the thing you need to do. If this will be to slow (btw you should place your image inside assets catalogue to allow caching and improve performance), you should switch to lower level api. Check iOS - An easy way to draw a circle using CAShapeLayer
And, btw, why do you change i variable inside loop, this is very bad practice

Creating shapes with text in the center

I want to create a circle which contains text in its center which is aligned horizontally in its View Controller. Something similar like this:
I'm not sure how to go about doing this. I would expect to create a custom UIView which contains a subview of a rounded CGRect and also a subview of a TextView although I'm not sure this is the more efficient way forward. Would this way forward be considered best practice and how would I implement it in Swift? Thanks.
You can achieve this with a simple UILabel!
let diameter = 50
var label = UILabel(frame: CGRect(x: 0, y: 0, width: diameter, height:diameter))
label.text = "Hello World"
label.textAlignment = .Center
label.backgroundColor = UIColor.redColor()
// The magic to create a circle
label.layer.cornerRadius = diameter / 2.0
label.clipsToBounds = true

How to add overlay to an UIImageView with tint and some text in Swift?

I created a UIStackView which contains UIImageViews to launch the gallery. However, this UIStackView will only show 3 items, and the third item will have an overlay with the number of images in the gallery. Similar to the image bellow:
The question is; how do I add the black tint and "+3" text on the UIImageView in Swift?
Create UIView "dynamicView" it like a mainView
Create A UIImageView add to "dyanamicView"
Create a another View for transparentColor shadow.named as "tranpView"
Create Label and center the position. Label is added to "tranpView"
And Last add the "dynamicView" view to self.view
let dynamicView=UIView(frame: CGRectMake(0, 200, 200, 200))
dynamicView.backgroundColor=UIColor.clearColor()
let imageView=UIImageView(frame: CGRectMake(0, 0, 200, 200))
imageView.image = UIImage(named: "bg.png")
dynamicView.addSubview(imageView)
let tranpView=UIView(frame: CGRectMake(0, 0, 200, 200))
tranpView.backgroundColor = UIColor.whiteColor()
tranpView.backgroundColor = tranpView.backgroundColor!.colorWithAlphaComponent(0.3)
let label = UILabel(frame: CGRectMake(0, 0, 50, 50))
label.text = "+3"
label.font = UIFont.systemFontOfSize(20)
label.center = tranpView.center;
tranpView.addSubview(label)
dynamicView.addSubview(tranpView)
self.view.addSubview(dynamicView)
//Just add to stackView directly replace the "self.view"
You can use a UIView that has 3 subviews:
The UIImage gallery which is UIImageView
UIView with black backgroundcolor and alpha 0.3
UILabel with the +3 text.

How to center an image in navigationBar across all UIViewControllers? Swift / Obj-C

Problem visually:
I have tried putting the image in the center of its own frame with no luck. I have also tried to center it with playing the x of the CGRect with no luck either. I presume I can just put an empty icon with the same background as the navigation bar; however, I don't want to do it that way. I might have 2-3 icons on the right; then what?
let image = UIImage(named: "some_logo")!
let imageSize = CGSizeMake(60, 42)
let marginX: CGFloat = (self.navigationController!.navigationBar.frame.size.width / 2) - (imageSize.width / 2)
let imageView = UIImageView(frame: CGRect(x: marginX, y: 0, width: imageSize.width, height: imageSize.height))
imageView.image = image
imageView.contentMode = .ScaleAspectFit
self.navigationItem.titleView = imageView
I prefer swift but obj-c solutions are welcomed as well.
Any pointers appreciated.
This app has nothing to do with KIA, it is just some logo I got off the google search, searching "some logo".
I have faced the same issue. Then i tried one code shown below.
override func viewDidAppear(animated: Bool) {
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 40))
imageView.contentMode = .ScaleAspectFit
let image = UIImage(named: "googlePlus")
imageView.image = image
navigationItem.titleView = imageView
}
This Code working fine when i tested with Left & Right Bar Button.
But in my previous code there is no Right Bar Button.
So the image is moving towards right.
For solving this i created a Right Bar Button & change the Tint color to clear color.
So everything seems to be working fine. This is one Temporary Solution for your problem.
The easiest way of doing this is in Interface Builder.
Simply drag a 'NavigationItem' from the object library and place it into your ViewController, then place a UIView where the title goes (ensure you set the background to 'clear')
Then place a UIImageView into that view and set the image in the Attributes Inspector to your required image. Scale your UIImage accordingly and set your your constraints accordingly.
I created an extension for solving this problem using the hint of #idrougge.
In order to center the title view image no matter what buttons you have, a content view is set as title view, then the image view is added as child of the content view. Finally, using constraints the image view is aligned inside its parent (content view).
import UIKit
extension UIViewController {
func addLogoToNavigationBarItem() {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.heightAnchor.constraint(equalToConstant: <your_height>).isActive = true
imageView.contentMode = .scaleAspectFit
imageView.image = <your_image>
//imageView.backgroundColor = .lightGray
// In order to center the title view image no matter what buttons there are, do not set the
// image view as title view, because it doesn't work. If there is only one button, the image
// will not be aligned. Instead, a content view is set as title view, then the image view is
// added as child of the content view. Finally, using constraints the image view is aligned
// inside its parent.
let contentView = UIView()
self.navigationItem.titleView = contentView
self.navigationItem.titleView?.addSubview(imageView)
imageView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
imageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
}
}
I hope this helps someone,
Xavi
As question heading stated "Swift / Obj-C" so I am sharing code of Obj-C :
UIImageView *titleImage = (UIImageView *)self.navigationItem.titleView;
titleImage = [[UIImageView alloc]initWithFrame:CGRectMake((self.navigationController.navigationBar.frame.size.width/2) - (100/2), 0, 100,self.navigationController.navigationBar.frame.size.height)];
//setting the image for UIImageView
titleImage.image = [UIImage imageNamed:#"someLogo"];
titleImage.contentMode = UIViewContentModeCenter;
self.navigationItem.titleView = titleImage;
Had same issue on phones with smaller sizes. Image in title was moving to right. Causing this issue back button -> [back_button][title_view]. Its centered when there is no back button or there is right bar button. Richard Hope's was right, you just need to put UIView first, and then put UIImageView as subview. Programmatically could be done like this.
private var imageView: UIView {
let bannerWidth = navigationBar.frame.size.width * 0.5 // 0.5 its multiplier to get correct image width
let bannerHeight = navigationBar.frame.size.height
let view = UIView()
view.backgroundColor = .clear
view.frame = CGRect(x: 0, y: 0, width: bannerWidth, height: bannerHeight)
let image = UIImage(named: "your_image_name")
let imageView = UIImageView(image: image)
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
view.addSubview(imageView)
return view
}
The just change titleView
navigationItem.titleView = imageView
What about setting the center of your image equals to the navigationBar.center instead of setting a margin?
//assuming we already have our navigationController
let myNicelLogoWidth = 100
let myNiceLogoHeight = 50
//start positioning your logo at 0.0, 0.0
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: myNicelLogoWidth, height: myNiceLogoHeight))
imageView.contentMode = .scaleAspectFit
imageView.center = navigationBar.center //the put your image at the center
let image = UIImage(named: "myNiceLogoImage")
imageView.image = image
navigationItem.titleView = imageView
I once face with this problem, and finally i found out that the problem is the previous navigation bar title still located next to burger button, but it's invisible.
Fast solution but not sure if it's the best is to change the previous navigation bar title to empty string before show the next view controller.
Hope it's help.

Resources