Border around every word of UILabel
Asked Answered
O

4

2

Is there a way i can draw border around every word of UILabel. Suppose UILabel contains the string " This is the Line 1".

I want 5 different border around 5 words

  1. This
  2. Is
  3. the
  4. Line
  5. 1
Orientalism answered 2/12, 2015 at 9:36 Comment(1)
#16362907 ?Selfassurance
R
10

I am not aware of an easy to use code for UILabel, but for UITextView:

Swift playground

setup:

import UIKit

let string = "Lorem ipsum dolor sit amet"

let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
textView.text = string

use regular expressions get a match for every word:

let pattern = "[a-zA-Z0-9]+"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matchesInString(string, options: [], range: NSMakeRange(0, string.characters.count))

function to get a rect for each match (ported from this answer):

func frameOfTextInRange(range:NSRange, inTextView textView:UITextView) -> CGRect {
    let beginning = textView.beginningOfDocument
    let start = textView.positionFromPosition(beginning, offset: range.location)!
    let end = textView.positionFromPosition(start, offset: range.length)!
    let textRange = textView.textRangeFromPosition(start, toPosition: end)!
    let rect = textView.firstRectForRange(textRange)
    return textView.convertRect(rect, fromView: textView)
}

iterate over each match, get its frame, use it to create a view for the background, add it to the text view:

for m in matches {
    let range = m.range
    let frame = frameOfTextInRange(range, inTextView: textView)
    let v = UIView(frame: frame)
    v.layer.borderWidth = 1
    v.layer.borderColor = UIColor.blueColor().CGColor
    textView.addSubview(v)

}

result


But possibly this doesn't give the result you were expecting. To get a better controls, you could use attributed strings.

Here the same code with using attributed strings

import UIKit

let string = "Lorem ipsum dolor sit amet"

let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

let attributedString = NSMutableAttributedString(string: string)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.25
attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, string.characters.count))

textView.attributedText = attributedString

let pattern = "[a-zA-Z0-9]+"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matchesInString(string, options: [], range: NSMakeRange(0, string.characters.count))

func frameOfTextInRange(range:NSRange, inTextView textView:UITextView) -> CGRect {
    let beginning = textView.beginningOfDocument
    let start = textView.positionFromPosition(beginning, offset: range.location)!
    let end = textView.positionFromPosition(start, offset: range.length)!
    let textRange = textView.textRangeFromPosition(start, toPosition: end)!
    let rect = textView.firstRectForRange(textRange)
    return textView.convertRect(rect, fromView: textView)
}

for m in matches {
    let range = m.range
    var frame = frameOfTextInRange(range, inTextView: textView)
    frame = CGRectInset(frame, CGFloat(-1.2), CGFloat(2))
    frame = CGRectOffset(frame, CGFloat(0), CGFloat(2))
    let v = UIView(frame: frame)
    v.layer.borderWidth = 1
    v.layer.borderColor = UIColor.blueColor().CGColor
    textView.addSubview(v)

}

result


Also helpful to create beautiful styles would be to add the views to a background view and add that textview on top

import UIKit

let string = "Lorem ipsum dolor sit amet"

let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let textViewBG = UIView(frame: textView.bounds)
textViewBG.backgroundColor = UIColor.whiteColor()
let attributedString = NSMutableAttributedString(string: string)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineHeightMultiple = 1.25
attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, string.characters.count))
attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSMakeRange(0, string.characters.count))
textView.attributedText = attributedString
textView.backgroundColor = UIColor.clearColor()

let pattern = "[a-zA-Z0-9]+"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matchesInString(string, options: [], range: NSMakeRange(0, string.characters.count))

func frameOfTextInRange(range:NSRange, inTextView textView:UITextView) -> CGRect {
    let beginning = textView.beginningOfDocument
    let start = textView.positionFromPosition(beginning, offset: range.location)!
    let end = textView.positionFromPosition(start, offset: range.length)!
    let textRange = textView.textRangeFromPosition(start, toPosition: end)!
    let rect = textView.firstRectForRange(textRange)
    return textView.convertRect(rect, fromView: textView)
}

for m in matches {
    let range = m.range
    var frame = frameOfTextInRange(range, inTextView: textView)
    frame = CGRectInset(frame, CGFloat(-1.2), CGFloat(2))
    frame = CGRectOffset(frame, CGFloat(0), CGFloat(2))
    let v = UIView(frame: frame)
    v.layer.cornerRadius = 2
    v.backgroundColor = UIColor(hue: 0.66, saturation: 0.6, brightness: 1, alpha: 1)
    textViewBG.addSubview(v)

}
textViewBG.addSubview(textView)

enter image description here


to increase the space between the words we can alter the kerning of the whitespace

import UIKit

func frameOfTextInRange(range:NSRange, inTextView textView:UITextView) -> CGRect {
    let beginning = textView.beginningOfDocument
    let start = textView.positionFromPosition(beginning, offset: range.location)!
    let end = textView.positionFromPosition(start, offset: range.length)!
    let textRange = textView.textRangeFromPosition(start, toPosition: end)!
    let rect = textView.firstRectForRange(textRange)
    return textView.convertRect(rect, fromView: textView)
}

let string = "Lorem ipsum dolor sit amet"

let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
textView.backgroundColor = UIColor.clearColor()

textView.attributedText = {
    let attributedString = NSMutableAttributedString(string: string)
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineHeightMultiple = 1.25
    attributedString.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSMakeRange(0, string.characters.count))
    attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSMakeRange(0, string.characters.count))

    let regex = try! NSRegularExpression(pattern: "\\s", options: [])
    let matches = regex.matchesInString(string, options: [], range: NSMakeRange(0, string.characters.count))
    for m in matches {
        attributedString.addAttribute(NSKernAttributeName, value: 6, range: m.range)
    }
    return NSAttributedString(attributedString: attributedString)
}()

let textViewBG = UIView(frame: textView.bounds)
textViewBG.backgroundColor = UIColor.whiteColor()


let pattern = "[^ ]+"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
let matches = regex.matchesInString(string, options: [], range: NSMakeRange(0, string.characters.count))

for m in matches {
    textViewBG.addSubview({
        let range = m.range
        var frame = frameOfTextInRange(range, inTextView: textView)
        frame = CGRectInset(frame, CGFloat(-3), CGFloat(2))
        frame = CGRectOffset(frame, CGFloat(0), CGFloat(3))
        let v = UIView(frame: frame)
        v.layer.cornerRadius = 2
        v.backgroundColor = UIColor(hue: 211.0/360.0, saturation: 0.35, brightness: 0.78    , alpha: 1)
        return v
    }())
}

textViewBG.addSubview(textView)

result

Revisionist answered 2/12, 2015 at 18:33 Comment(5)
Asked about UIlabel , answer about UITextView ?!Hobard
@TomSawyer: feel free to post a UILabel answerRevisionist
Thanks for a thorough response, very helpful!Lir
@vikingosegundo. Hello vikingosegundo , when i change y position the content is not displaying correctContrayerva
@vikingosegundo. Please help me to achieve thisContrayerva
P
2

SWIFT 4 Version

This is the swift 4 version of vikingosegundo Answer using a UITextView including a few other tweaks including:
• list of words coming from an array
• Centering in the textView
• Adjustable word limit
• Custom font

//: Playground - noun: a place where people can play

import UIKit

var array = [String]()

array = ["WORD1", "WORD2", "WORD3", "WORD4", "WORD4", "LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONGWORD", "WORD5"]

var stringarr = String()

for i in 0...(array.count-1) {

if array[i].count > 20 {
    let count = array[i].count
    let dropCount = count-20
    let dropCharacters = array[i].dropLast(dropCount)
    var shortenedWord = "\(dropCharacters)"
    shortenedWord = "\(shortenedWord)..."
    print(shortenedWord)
    array[i] = shortenedWord

    }
}

stringarr = array.joined(separator: " ")


func frameOfTextInRange(range:NSRange, inTextView textView:UITextView) -> CGRect {
    let beginning = textView.beginningOfDocument
    let start = textView.position(from: beginning, offset: range.location)
    let end = textView.position(from: start!, offset: range.length)
    let textRange = textView.textRange(from: start!, to: end!)
    let rect = textView.firstRect(for: textRange!)

    return textView.convert(rect, from: textView)
}

let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 375, height: 200))
textView.backgroundColor = UIColor.clear

let lineSpacing: CGFloat = 0.0

textView.attributedText = {
    let attributedString = NSMutableAttributedString(string: stringarr)
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineHeightMultiple = 1.5
    paragraphStyle.lineSpacing = lineSpacing
    attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: stringarr.count))
    attributedString.addAttribute(.foregroundColor, value: UIColor.white, range: NSRange(location: 0, length: stringarr.count))
    attributedString.addAttribute(.font, value: UIFont(name: "AvenirNext-Regular", size: 10.0)!, range: NSRange(location: 0, length: stringarr.count))


let regex = try! NSRegularExpression(pattern: "\\s", options: [])
let matches = regex.matches(in: stringarr, options: [], range: NSRange(location: 0, length: stringarr.count))

for m in matches {
    attributedString.addAttribute(.kern, value: 6, range: m.range)
}

return NSAttributedString(attributedString: attributedString)
}()


textView.textAlignment = .center

let textViewBG = UIView(frame: textView.bounds)
textViewBG.backgroundColor = UIColor.white

let pattern = "[^ ]+"
let regex = try! NSRegularExpression(pattern: pattern, options: [])
 let matches = regex.matches(in: stringarr, options: [], range: NSRange(location: 0, length: stringarr.count))

for m in matches {
textViewBG.addSubview({
    let range = m.range
    let frame = frameOfTextInRange(range: range, inTextView: textView).insetBy(dx: CGFloat(-2), dy: CGFloat(3)).offsetBy(dx: CGFloat(0), dy: CGFloat(3))
    let v = UIView(frame: frame)
    v.layer.cornerRadius = 0
    v.backgroundColor = UIColor.black
    return v
    }())
}

textViewBG.addSubview(textView)

//: End of playground



Result

Hope that helps!

Paprika answered 2/7, 2018 at 2:52 Comment(0)
A
2

SWIFT 5. Alternative solution

The Solution Logic

  1. Converting Strings to Labels
  2. Customizing Labels
  3. Converting Labels to Images
  4. Converting Images to Attributed text
  5. Creating TextView
  6. Setting Attributed text to TextView

Final TextView

You can find implementation here:

https://github.com/vivatum/WordsAsTagLabels-Example

Abet answered 13/6, 2020 at 10:14 Comment(0)
P
1

You will have to create one label for each word....do it programmatically! I did it now, test please! hope u enjoy :-)

import UIKit

class ViewController: UIViewController {

var arrayStrings = [String]()
var x : CGFloat = 0
var labelReference = 0

override func viewDidLoad() {
    super.viewDidLoad()


    let space = " "
    let string = "This is the line 1"
    var word = string.componentsSeparatedByString(space)
    print (word[0]) // prints "This"
    print(word[1]) // print "is"

    for var i = 0; i < word.count ; i++ {


        arrayStrings.append(word[i])


        let characteresCount = word[i].characters.count


        // change de "9" based on your font size
        let label = UILabel(frame: CGRectMake(CGFloat(32 + x), 30, CGFloat(characteresCount * 9), 25))
        x += label.frame.size.width + 2
        label.text = word[i]
        label.layer
        label.layer.borderWidth = 1.0
        label.layer.cornerRadius = 10
        view.addSubview(label)


      }

   }

}
Pyxis answered 2/12, 2015 at 12:26 Comment(2)
This should only yield acceptable results for mono spaced fonts, such as courier, as otherwise the width of different glyph differ a lot, such as the with for an i and a m.Revisionist
to fix this you could call sizeToFit() on each labelRevisionist

© 2022 - 2024 — McMap. All rights reserved.