Make part of a string bold that matches a search string
Asked Answered
T

2

5

I have a table of items and each item has a label. I also have a search bar that is used to filter the items in the table based on whether mySearchBar.text is a substring of myLabel.text.

That is all working fine, but I'd like to bold the portions of label text that match the search string.

The end product would be something similar to Google Maps search.

enter image description here

Tapia answered 13/1, 2017 at 0:42 Comment(0)
T
4

Here's a sample of what I ended up implementing:

@IBOutlet weak var mySearchBar: UISearchBar!
@IBOutlet weak var myLabel: UILabel!

...

func makeMatchingPartBold(searchText: String) {

    // check label text & search text
    guard
        let labelText = myLabel.text,
        let searchText = mySearchBar.text
    else {
        return
    }

    // bold attribute
    let boldAttr = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: myLabel.font.pointSize)]

    // check if label text contains search text
    if let matchRange: Range = labelText.lowercased().range(of: searchText.lowercased()) {

        // get range start/length because NSMutableAttributedString.setAttributes() needs NSRange not Range<String.Index>
        let matchRangeStart: Int = labelText.distance(from: labelText.startIndex, to: matchRange.lowerBound)
        let matchRangeEnd: Int = labelText.distance(from: labelText.startIndex, to: matchRange.upperBound)
        let matchRangeLength: Int = matchRangeEnd - matchRangeStart

        // create mutable attributed string & bold matching part
        let newLabelText = NSMutableAttributedString(string: labelText)
        newLabelText.setAttributes(boldAttr, range: NSMakeRange(matchRangeStart, matchRangeLength))

        // set label attributed text
        myLabel.attributedText = newNameText
    }
}
Tapia answered 13/1, 2017 at 0:42 Comment(1)
Instead of using '.lowercased().range(of: searchText.lowercased())' you can use ".range(of: searchText, options: .caseInsensitive)"Sergeant
E
9

Swift 4 : XCode 9.x

private func filterAndModifyTextAttributes(searchStringCharacters: String, completeStringWithAttributedText: String) -> NSMutableAttributedString {

    let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: completeStringWithAttributedText)
    let pattern = searchStringCharacters.lowercased()
    let range: NSRange = NSMakeRange(0, completeStringWithAttributedText.characters.count)
    var regex = NSRegularExpression()
    do {
        regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options())
        regex.enumerateMatches(in: completeStringWithAttributedText.lowercased(), options: NSRegularExpression.MatchingOptions(), range: range) {
            (textCheckingResult, matchingFlags, stop) in
            let subRange = textCheckingResult?.range
            let attributes : [NSAttributedStringKey : Any] = [.font : UIFont.boldSystemFont(ofSize: 17),.foregroundColor: UIColor.red ]
            attributedString.addAttributes(attributes, range: subRange!)
        }
    }catch{
        print(error.localizedDescription)
    }
    return attributedString
}

How to use :

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
     let cell = UITableViewCell(style: .subtitle , reuseIdentifier: "Cell")
     cell.textLabel?.attributedText = self.filterAndModifyTextAttributes(searchStringCharacters: self.textFromSearchBar, completeStringWithAttributedText: searchResultString)
     return cell

}

RESULT

Excoriation answered 23/3, 2018 at 9:47 Comment(0)
T
4

Here's a sample of what I ended up implementing:

@IBOutlet weak var mySearchBar: UISearchBar!
@IBOutlet weak var myLabel: UILabel!

...

func makeMatchingPartBold(searchText: String) {

    // check label text & search text
    guard
        let labelText = myLabel.text,
        let searchText = mySearchBar.text
    else {
        return
    }

    // bold attribute
    let boldAttr = [NSFontAttributeName: UIFont.boldSystemFont(ofSize: myLabel.font.pointSize)]

    // check if label text contains search text
    if let matchRange: Range = labelText.lowercased().range(of: searchText.lowercased()) {

        // get range start/length because NSMutableAttributedString.setAttributes() needs NSRange not Range<String.Index>
        let matchRangeStart: Int = labelText.distance(from: labelText.startIndex, to: matchRange.lowerBound)
        let matchRangeEnd: Int = labelText.distance(from: labelText.startIndex, to: matchRange.upperBound)
        let matchRangeLength: Int = matchRangeEnd - matchRangeStart

        // create mutable attributed string & bold matching part
        let newLabelText = NSMutableAttributedString(string: labelText)
        newLabelText.setAttributes(boldAttr, range: NSMakeRange(matchRangeStart, matchRangeLength))

        // set label attributed text
        myLabel.attributedText = newNameText
    }
}
Tapia answered 13/1, 2017 at 0:42 Comment(1)
Instead of using '.lowercased().range(of: searchText.lowercased())' you can use ".range(of: searchText, options: .caseInsensitive)"Sergeant

© 2022 - 2024 — McMap. All rights reserved.