skip to Main Content

I have a string value containing an amount, where I specified the decimal part as after "," and I want to change the font of the part after "," to be half of the other part

private func getText(text: String, size: CGFloat) -> NSAttributedString {
    let attributedString = NSMutableAttributedString(
        string: text,
        attributes: [
            .font: UIFont.themeFont(ofSize: size, weight: .medium)
        ]
    )
    
    if let commaRange = text.range(of: ",") {
        let startIndex = text.index(commaRange.upperBound, offsetBy: 1)
        let substring = text[startIndex...]
        
        attributedString.addAttributes(
            [
                .font: UIFont.themeFont(ofSize: size / 2, weight: .medium)
            ],
            range: NSRange(location: text.distance(from: text.startIndex, to: startIndex), length: substring.count)
        )
    }
    
    return attributedString
}

2

Answers


  1. The upperbound of a range is the index after the index of the last character in the range, so the offset is not needed.

    The substring is not needed either, and use the addAttribute(_:value:range:) API to apply one attribute

    private func getText(text: String, size: CGFloat) -> NSAttributedString {
        let attributedString = NSMutableAttributedString(
            string: text,
            attributes: [
                .font: UIFont.themeFont(ofSize: size, weight: .medium)
            ]
        )
        
        if let commaRange = text.range(of: ",") {
            let nsRange = NSRange(commaRange.upperBound..., in: text)
            attributedString.addAttribute(.font,
                                          value: UIFont.themeFont(ofSize: size / 2, weight: .medium),
                                          range: nsRange)
        }
        return attributedString
    }
    

    Or determine the range with Regex

    private func getText(text: String, size: CGFloat) -> NSAttributedString {
        let attributedString = NSMutableAttributedString(
            string: text,
            attributes: [
                .font: UIFont.themeFont(ofSize: size, weight: .medium)
            ]
        )
    
        if let match = text.firstMatch(of: /,(d+)/) {
            let nsRange = NSRange(match.output.1.startIndex..., in: text)
            attributedString.addAttribute(.font,
                                          value: UIFont.themeFont(ofSize: size / 2, weight: .medium),
                                          range: nsRange)
        }
        return attributedString
    }
    

    A still more convenient way is Swift’s native AttributedString

    func getText(text: String, size: CGFloat) -> NSAttributedString {
        var attributedString = AttributedString(text)
        attributedString.font = UIFont.themeFont(ofSize: size, weight: medium)
        if let range = attributedString.characters.firstRange(of: ",") {
            attributedString[range.upperBound...].font = UIFont.themeFont(ofSize: size/2, weight: .medium)
        }
        return NSAttributedString(attributedString)
    }
    

    Side note: Converting Range<String.Index> to NSRange with NSRange(location:length:) is strongly discouraged.

    Login or Signup to reply.
  2. I think it would be fun to provide a solution using modern Swift. Attributed strings are now native (AttributedString). Regular expressions are now native (Regex). So we can write it like this:

    func getText(text: String, size: CGFloat) -> NSAttributedString {
        var result = AttributedString(text)
        result.font = UIFont.themeFont(
            ofSize: size, weight: .medium
        )
        if let regex = try? Regex("(,)(?<postcomma>.*)$"),
           let match = try? regex.firstMatch(in: text),
           let range = match["postcomma"]?.range,
           let attributedRange = Range(range, in: result) {
            result[attributedRange].font = UIFont.themeFont(
                ofSize: size/2, weight: .medium
            )
        }
        return NSAttributedString(result)
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search