189

I need to embed small icons ( sort of custom bullets ) to my UILabel in iOS7. How can I do this in interface designer? Or at least in code?

In Android there are leftDrawable and rightDrawable for labels, but how it is done in iOS? Sample in android :

android sample

Jayesh Thanki
  • 2,037
  • 2
  • 23
  • 32
AVEbrahimi
  • 17,993
  • 23
  • 107
  • 210

21 Answers21

314

You can do this with iOS 7's text attachments, which are part of TextKit. Some sample code:

NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
attachment.image = [UIImage imageNamed:@"MyIcon.png"];

NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment];

NSMutableAttributedString *myString= [[NSMutableAttributedString alloc] initWithString:@"My label text"];
[myString appendAttributedString:attachmentString];

myLabel.attributedText = myString;
kelin
  • 11,323
  • 6
  • 67
  • 104
Scott Berrevoets
  • 16,921
  • 6
  • 59
  • 80
  • How about iOS6? Do you have any suggestion?? Thx – Steven Jiang Jun 05 '14 at 05:05
  • 1
    @StevenJiang: You'll have to just add a `UIImageView` to your label – Scott Berrevoets Jun 05 '14 at 12:20
  • 1
    Unfortunately this places the icon after the text. Any chance that we can move this before the text because I can't find a way?! – reVerse Jul 28 '14 at 09:58
  • 4
    @reVerse Instead of appending the image (attachment string) to your textual string, you could try that the other way around, so appending the textual string to the attachment string. – Scott Berrevoets Jul 28 '14 at 16:00
  • 12
    Already tried this yesterday. Seems like a missed something because now it works. Thanks. Just in case for everyone who's trying to accomplish the same (since it's slightly different): `NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:attachment]; NSMutableAttributedString *myString = [[NSMutableAttributedString alloc] initWithAttributedString:attachmentString]; NSAttributedString *myText = [[NSMutableAttributedString alloc] initWithString:text]; [myString appendAttributedString:myText];` – reVerse Jul 29 '14 at 08:07
  • but image is displaying right side..... How i need to keep on specific position ...Like [image Drawable Left] – Yalamandarao Feb 22 '15 at 17:39
  • To rectify the position you can take ref from : http://stackoverflow.com/questions/26105803/center-nstextattachment-image-next-to-single-line-uilabel – Vinay Chopra Jul 15 '15 at 16:38
  • Its adding icon to right of the text how can we make it left align? – Tarun Seera Feb 27 '16 at 07:18
  • @TarunSeera, see reVerse's solution about changing the append order, just above in the comments. I just want to know, is it possible to add padding to the image before the text is placed? – Chucky Jun 20 '16 at 13:52
  • @Chucky: You can try messing with the bounds of the image, but that may distort your image in ways you don't want to. There is no `contentMode` or anything like that as far as I know, so if you want that you'll have to find a different solution unfortunately. – Scott Berrevoets Jun 20 '16 at 21:47
  • How about adding 2 images in the label. I have tried like your solution, but both images not show, only show one default image. – Twitter khuong291 Jun 22 '16 at 13:56
  • You'd have to show some code. Probably in a separate question on SO. – Scott Berrevoets Jun 22 '16 at 23:09
211

Here is the way to embed icon in UILabel.

Also to Align the Icon use attachment.bounds


Swift 5.1

// Create Attachment
let imageAttachment = NSTextAttachment()
imageAttachment.image = UIImage(named:"iPhoneIcon")
// Set bound to reposition
let imageOffsetY: CGFloat = -5.0
imageAttachment.bounds = CGRect(x: 0, y: imageOffsetY, width: imageAttachment.image!.size.width, height: imageAttachment.image!.size.height)
// Create string with attachment
let attachmentString = NSAttributedString(attachment: imageAttachment)
// Initialize mutable string
let completeText = NSMutableAttributedString(string: "")
// Add image to mutable string
completeText.append(attachmentString)
// Add your text to mutable string
let textAfterIcon = NSAttributedString(string: "Using attachment.bounds!")
completeText.append(textAfterIcon)
self.mobileLabel.textAlignment = .center
self.mobileLabel.attributedText = completeText

Objective-C Version

NSTextAttachment *imageAttachment = [[NSTextAttachment alloc] init];
imageAttachment.image = [UIImage imageNamed:@"iPhoneIcon"];
CGFloat imageOffsetY = -5.0;
imageAttachment.bounds = CGRectMake(0, imageOffsetY, imageAttachment.image.size.width, imageAttachment.image.size.height);
NSAttributedString *attachmentString = [NSAttributedString attributedStringWithAttachment:imageAttachment];
NSMutableAttributedString *completeText = [[NSMutableAttributedString alloc] initWithString:@""];
[completeText appendAttributedString:attachmentString];
NSAttributedString *textAfterIcon = [[NSAttributedString alloc] initWithString:@"Using attachment.bounds!"];
[completeText appendAttributedString:textAfterIcon];
self.mobileLabel.textAlignment = NSTextAlignmentRight;
self.mobileLabel.attributedText = completeText;

enter image description here

enter image description here

Michal Šrůtek
  • 1,647
  • 16
  • 17
Tarun Seera
  • 4,212
  • 4
  • 27
  • 41
  • 32
    Vote up for attachment.bounds – Peter Zhao May 30 '16 at 07:46
  • 4
    Great call on using attachment.bounds. Thats exactly what I was looking for. – Geoherna Oct 08 '16 at 18:33
  • 5
    In fact, the imageOffsetY can be calculated instead of using a fixed value of -5.0. let imageOffsetY:CGFloat = -(imageAttachment.image!.size.height - self.mobileLabel.font.pointSize) / 2.0; – JonSlowCN Jan 04 '18 at 07:47
  • 1
    **Note: it slow down compile time** – Jack Apr 27 '18 at 08:03
  • I can do it on storyboard? – Augusto Mar 08 '19 at 11:51
  • How could we increase the horizontal spacing between icon and text. I used `completeText.addAttribute(NSAttributedString.Key.kern, value: 12, range: NSRange(location: 0, length: 1))` but it seems not worked. – Zhou Haibo Aug 11 '21 at 05:29
  • It seems to be broken with Xcode 13 RC. The image is cut, the label height bounds does not increase with the image height (it worked great with Xcode 12.X) – Ded77 Sep 16 '21 at 13:31
64

Swift 4.2:

let attachment = NSTextAttachment()        
attachment.image = UIImage(named: "yourIcon.png")
let attachmentString = NSAttributedString(attachment: attachment)
let myString = NSMutableAttributedString(string: price)
myString.append(attachmentString)
label.attributedText = myString
radioaktiv
  • 2,437
  • 4
  • 27
  • 36
24

Swift 3 version

let attachment = NSTextAttachment()
attachment.image = UIImage(named: "plus")
attachment.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
let attachmentStr = NSAttributedString(attachment: attachment)
let myString = NSMutableAttributedString(string: "")
myString.append(attachmentStr)
let myString1 = NSMutableAttributedString(string: "My label text")
myString.append(myString1)
lbl.attributedText = myString

UILabel Extension

extension UILabel {

    func set(text:String, leftIcon: UIImage? = nil, rightIcon: UIImage? = nil) {

        let leftAttachment = NSTextAttachment()
        leftAttachment.image = leftIcon
        leftAttachment.bounds = CGRect(x: 0, y: -2.5, width: 20, height: 20)
        if let leftIcon = leftIcon {
            leftAttachment.bounds = CGRect(x: 0, y: -2.5, width: leftIcon.size.width, height: leftIcon.size.height)
        }
        let leftAttachmentStr = NSAttributedString(attachment: leftAttachment)

        let myString = NSMutableAttributedString(string: "")

        let rightAttachment = NSTextAttachment()
        rightAttachment.image = rightIcon
        rightAttachment.bounds = CGRect(x: 0, y: -5, width: 20, height: 20)
        let rightAttachmentStr = NSAttributedString(attachment: rightAttachment)


        if semanticContentAttribute == .forceRightToLeft {
            if rightIcon != nil {
                myString.append(rightAttachmentStr)
                myString.append(NSAttributedString(string: " "))
            }
            myString.append(NSAttributedString(string: text))
            if leftIcon != nil {
                myString.append(NSAttributedString(string: " "))
                myString.append(leftAttachmentStr)
            }
        } else {
            if leftIcon != nil {
                myString.append(leftAttachmentStr)
                myString.append(NSAttributedString(string: " "))
            }
            myString.append(NSAttributedString(string: text))
            if rightIcon != nil {
                myString.append(NSAttributedString(string: " "))
                myString.append(rightAttachmentStr)
            }
        }
        attributedText = myString
    }
}
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70
23

Your reference image looks like a button. Try (can also be done in Interface Builder):

enter image description here

UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
[button setFrame:CGRectMake(50, 50, 100, 44)];
[button setImage:[UIImage imageNamed:@"img"] forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(0, -30, 0, 0)];
[button setTitle:@"Abc" forState:UIControlStateNormal];
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setBackgroundColor:[UIColor yellowColor]];
[view addSubview:button];
SomeGuy
  • 9,670
  • 3
  • 32
  • 35
16

I've made an implementation of this feature in swift here: https://github.com/anatoliyv/SMIconLabel

Code is as simple as it's possible:

var labelLeft = SMIconLabel(frame: CGRectMake(10, 10, view.frame.size.width - 20, 20))
labelLeft.text = "Icon on the left, text on the left"

// Here is the magic
labelLeft.icon = UIImage(named: "Bell") // Set icon image
labelLeft.iconPadding = 5               // Set padding between icon and label
labelLeft.numberOfLines = 0             // Required
labelLeft.iconPosition = SMIconLabelPosition.Left // Icon position
view.addSubview(labelLeft)

Here is how it looks:

SMIconLabel image

anatoliy_v
  • 1,782
  • 15
  • 24
15

Swift 5 Easy Way Just CopyPaste and change what you want

let fullString = NSMutableAttributedString(string:"To start messaging contacts who have Talklo, tap ")

 // create our NSTextAttachment
let image1Attachment = NSTextAttachment() 
image1Attachment.image = UIImage(named: "chatEmoji")
image1Attachment.bounds = CGRect(x: 0, y: -8, width: 25, height: 25)

// wrap the attachment in its own attributed string so we can append it
let image1String = NSAttributedString(attachment: image1Attachment)

 // add the NSTextAttachment wrapper to our full string, then add some more text.

 fullString.append(image1String)
 fullString.append(NSAttributedString(string:" at the right bottom of your screen"))

 // draw the result in a label
 self.lblsearching.attributedText = fullString

enter image description here

Shakeel Ahmed
  • 5,361
  • 1
  • 43
  • 34
14

Swift 4 UIlabel Extension to add Image to Label with reference to above answers

extension UILabel {
  func set(image: UIImage, with text: String) {
    let attachment = NSTextAttachment()
    attachment.image = image
    attachment.bounds = CGRect(x: 0, y: 0, width: 10, height: 10)
    let attachmentStr = NSAttributedString(attachment: attachment)

    let mutableAttributedString = NSMutableAttributedString()
    mutableAttributedString.append(attachmentStr)

    let textString = NSAttributedString(string: text, attributes: [.font: self.font])
    mutableAttributedString.append(textString)

    self.attributedText = mutableAttributedString
  }
}
Agent Smith
  • 2,873
  • 17
  • 32
10

In Swift 5, By using UILabel extensions to embed icon in leading as well as trailing side of the text as follows:-

extension UILabel {
    
    func addTrailing(image: UIImage, text:String) {
        let attachment = NSTextAttachment()
        attachment.image = image

        let attachmentString = NSAttributedString(attachment: attachment)
        let string = NSMutableAttributedString(string: text, attributes: [:])

        string.append(attachmentString)
        self.attributedText = string
    }
    
    func addLeading(image: UIImage, text:String) {
        let attachment = NSTextAttachment()
        attachment.image = image

        let attachmentString = NSAttributedString(attachment: attachment)
        let mutableAttributedString = NSMutableAttributedString()
        mutableAttributedString.append(attachmentString)
        
        let string = NSMutableAttributedString(string: text, attributes: [:])
        mutableAttributedString.append(string)
        self.attributedText = mutableAttributedString
    }
}

To use above mentioned code in your desired label as:-

Image in right of text then:-

statusLabel.addTrailing(image: UIImage(named: "rightTick") ?? UIImage(), text: " Verified ")

Image in left of text then:-

statusLabel.addLeading(image: UIImage(named: "rightTick") ?? UIImage(), text: " Verified ")

Output:-

enter image description here

enter image description here

Ashutosh Mishra
  • 1,679
  • 1
  • 13
  • 16
5

try this way...

  self.lbl.text=@"Drawble Left";
    UIImageView *img=[[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 20, 20)];
    img.image=[UIImage imageNamed:@"Star.png"];
    [self.lbl addSubview:img];
user1673099
  • 3,293
  • 7
  • 26
  • 57
3

Swift 2.0 version:

//Get image and set it's size
let image = UIImage(named: "imageNameWithHeart")
let newSize = CGSize(width: 10, height: 10)

//Resize image
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
let imageResized = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

//Create attachment text with image
var attachment = NSTextAttachment()
attachment.image = imageResized
var attachmentString = NSAttributedString(attachment: attachment)
var myString = NSMutableAttributedString(string: "I love swift ")
myString.appendAttributedString(attachmentString)
myLabel.attributedText = myString
Phil
  • 4,730
  • 1
  • 41
  • 39
3

Swift 5+

If you want to keep the ratio of the image and the image always centered with the text, then, this is my solution:

extension UILabel {
    var mutableAttributedString: NSMutableAttributedString? {
        let attributedString: NSMutableAttributedString
        if let labelattributedText = self.attributedText {
            attributedString = NSMutableAttributedString(attributedString: labelattributedText)
        } else {
            guard let labelText = self.text else { return nil }
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.alignment = self.textAlignment
            attributedString = NSMutableAttributedString(string: labelText)
            attributedString.addAttribute(NSAttributedString.Key.paragraphStyle,
                                          value: paragraphStyle,
                                          range: NSRange(location: 0, length: attributedString.length))
        }
        return attributedString
    }

    func addImage(_ image: UIImage, toEndWith height: CGFloat) {
        let fullAttributedString = mutableAttributedString
        let imageAttachment = NSTextAttachment()
        imageAttachment.image = image

        let yImage = (font.capHeight - height).rounded() / 2
        let ratio = image.size.width / image.size.height
        imageAttachment.bounds = CGRect(x: 0, y: yImage, width: ratio * height, height: height)
        
        let imageString = NSAttributedString(attachment: imageAttachment)
        fullAttributedString?.append(imageString)
        attributedText = fullAttributedString
    }
    
    func addImage(_ image: UIImage, toStartWith height: CGFloat) {
        let imageAttachment = NSTextAttachment()
        imageAttachment.image = image

        let yImage = (font.capHeight - height).rounded() / 2
        let ratio = image.size.width / image.size.height
        imageAttachment.bounds = CGRect(x: 0, y: yImage, width: ratio * height, height: height)
        
        let fullAttributed = NSMutableAttributedString(attachment: imageAttachment)
        if let rawAttributed = mutableAttributedString {
            fullAttributed.append(rawAttributed)
        }
        attributedText = fullAttributed
    }
}

And this is how to use the above extension:

let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20))
label.font = .systemFont(ofSize: 20)
let image = UIImage(systemName: "square.and.pencil")!
label.text = "Hi, "
label.addImage(image, toEndWith: 10)

These are some examples:

enter image description here

enter image description here

enter image description here

Using with an attributed string:

let myString = "Hi, "
let myAttribute: [NSAttributedString.Key: UIColor] = [.foregroundColor: .blue]
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
label.attributedText = myAttrString
label.addImage(image, toEndWith: 15)

enter image description here

Tuan Nguyen
  • 2,624
  • 1
  • 17
  • 15
2

Try dragging a UIView onto the screen in IB. From there you can drag a UIImageView and UILabel into the view you just created. Set the image of the UIImageView in the properties inspector as the custom bullet image (which you will have to add to your project by dragging it into the navigation pane) and you can write some text in the label.

Jayesh Thanki
  • 2,037
  • 2
  • 23
  • 32
nanothread
  • 908
  • 3
  • 10
  • 25
2

You can extent UILabe pass the flag for the image add-in Leading or Trailing also set imageBounds if needed.

Swift 5+

extension UILabel {
    func add(image: UIImage, text: String, isLeading: Bool = true, imageBounds: CGRect = CGRect(x: 0, y: 0, width: 16, height: 12)) {
        let imageAttachment = NSTextAttachment()
        imageAttachment.bounds = imageBounds
        
        imageAttachment.image = image
        
        let attachmentString = NSAttributedString(attachment: imageAttachment)
        let string = NSMutableAttributedString(string: text)
        
        let mutableAttributedString = NSMutableAttributedString()
        
        if isLeading {
            mutableAttributedString.append(attachmentString)
            mutableAttributedString.append(string)
            attributedText = mutableAttributedString
        } else {
            string.append(attachmentString)
            attributedText = string
        }
    }
    }
Binoy jose
  • 461
  • 4
  • 9
2

For somebody who wants to have an icon on the right end of their label, not necessarily immediately after the text, you can use this technique based on the idea in this answer: https://stackoverflow.com/a/19318843/826946 (Note there are some constants here you will probably want to adjust, but the general idea should be clear). This will not work if your label is being sized using its implicit size, only if you have some other constraint on the width that you are confident will leave room for your icon.

    let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
    imgView.image = UIImage(named: "arrow")
    myLabel.addSubview(imgView)
    imgView.translatesAutoresizingMaskIntoConstraints = false
    imgView.centerYAnchor.constraint(equalTo: myLabel.centerYAnchor, constant: 0).isActive = true
    imgView.rightAnchor.constraint(equalTo: myLabel.rightAnchor, constant: -20).isActive = true
Andy Weinstein
  • 2,639
  • 3
  • 21
  • 32
1

you have to make a custom object where you used a UIView and inside you put a UIImageView and a UILabel

Mirko Catalano
  • 3,850
  • 2
  • 26
  • 39
1

You could use a UITextField with the leftView property and then set the enabled property to NO

Or use a UIButton and setImage:forControlState

liamnichols
  • 12,419
  • 2
  • 43
  • 62
1
 func atributedLabel(str: String, img: UIImage)->NSMutableAttributedString
{   let iconsSize = CGRect(x: 0, y: -2, width: 16, height: 16)
    let attributedString = NSMutableAttributedString()
    let attachment = NSTextAttachment()
    attachment.image = img
    attachment.bounds = iconsSize
    attributedString.append(NSAttributedString(attachment: attachment))
    attributedString.append(NSAttributedString(string: str))

    return attributedString
} 

You can use this function to add images or small icons to the label

Ayush Dixit
  • 467
  • 4
  • 10
  • Call this in viewdidload() – Ayush Dixit Feb 23 '18 at 11:01
  • let emojisCollection = [UIImage(named: "ic_place"), UIImage(named: "ic_group"), UIImage(named: "ic_analytics")] lbl1.attributedText = atributedLabel(str: " Howath, Dublin", img: emojisCollection[0]!) lbl2.attributedText = atributedLabel(str: " Difficulty: 18+", img: emojisCollection[2]!) lbl3.attributedText = atributedLabel(str: " Maximum group size: 10", img: emojisCollection[1]!) – Ayush Dixit Feb 23 '18 at 11:02
  • you can edit you original answer to include those comments above. – Moondra Aug 25 '18 at 19:12
0

In Swift 2.0,

My solution to the problem is a combination of a couple of answers on this question. The problem I faced in @Phil's answer was that I couldn't change the position of the icon, and it always appeared in right the corner. And the one answer from @anatoliy_v, I couldn't resize the icon size I want to append to the string.

To make it work for me, I first did a pod 'SMIconLabel' and then created this function:

func drawTextWithIcon(labelName: SMIconLabel, imageName: String, labelText: String!,  width: Int, height: Int) {

        let newSize = CGSize(width: width, height: height)
        let image = UIImage(named: imageName)
        UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
        image?.drawInRect(CGRectMake(0, 0, newSize.width, newSize.height))
        let imageResized = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        labelName.text = " \(labelText)"
        labelName.icon = imageResized
        labelName.iconPosition = .Left
    }

This solution will not only help you place the image but will also allow you to make necessary changes to the icon size and other attributes.

Thank You.

tush4r
  • 867
  • 12
  • 21
0

Swift 3 UILabel extention

Tip: If you need some space between the image and the text just use a space or two before the labelText.

extension UILabel {
    func addIconToLabel(imageName: String, labelText: String, bounds_x: Double, bounds_y: Double, boundsWidth: Double, boundsHeight: Double) {
        let attachment = NSTextAttachment()
        attachment.image = UIImage(named: imageName)
        attachment.bounds = CGRect(x: bounds_x, y: bounds_y, width: boundsWidth, height: boundsHeight)
        let attachmentStr = NSAttributedString(attachment: attachment)
        let string = NSMutableAttributedString(string: "")
        string.append(attachmentStr)
        let string2 = NSMutableAttributedString(string: labelText)
        string.append(string2)
        self.attributedText = string
    }
}
Gary Mansted
  • 269
  • 3
  • 9
  • 1
    I used this and it worked perfectly. The other ones above actually flipped the image to the end of the string. – mondousage Jun 09 '18 at 18:43
0

If you don't absolutely need a label (and there may be some situations where you do), you can use a view hierarchy with constraints to create a UIView that can be placed where a label would go. And, of course the image and label text of the composite view can be updated dynamically.

func configureTitleLabelWithIcon(text: String, parent: UIView) {

    let iconAndLabel = UIView()
    let label = UILabel()
    let icon  = UIImageView(image:UIImage(systemName: "globe"))

    label.text = text
    label.textColor = UIColor.secondaryLabel
    
    label.translatesAutoresizingMaskIntoConstraints = false
    ico .translatesAutoresizingMaskIntoConstraints = false
    iconAndLabel.translatesAutoresizingMaskIntoConstraints = false

    iconAndLabel.addSubview(icon)
    iconAndLabel.addSubview(label)

    icon.widthAnchor.constraint(  equalToConstant: 14).isActive = true
    icon.heightAnchor.constraint( equalToConstant: 14).isActive = true

    iconAndLabel.widthAnchor.constraint(  equalToConstant: 200).isActive = true
    iconAndLabel.heightAnchor.constraint( equalToConstant: 22).isActive = true

    icon.leadingAnchor.constraint(equalTo:   iconAndLabel.leadingAnchor).isActive = true
    icon.trailingAnchor.constraint(equalTo:  label.leadingAnchor, constant: -7).isActive = true
    label.trailingAnchor.constraint(equalTo: iconAndLabel.trailingAnchor).isActive = true
    icon.centerYAnchor.constraint(equalTo:   iconAndLabel.centerYAnchor).isActive = true
    view.addSubview(iconAndLabel)

    label.centerXAnchor.constraint(equalTo: parent, constant: 10).isActive = true
    label.bottomAnchor.constraint(equalTo:  parent, constant: -75).isActive = true
}
clearlight
  • 12,255
  • 11
  • 57
  • 75