-1

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
}

3 Answers 3

0

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)
}
Sign up to request clarification or add additional context in comments.

Comments

0

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.

1 Comment

One should never, ever assume that a character is one unit. Your code is correctly written and will work with characters that are more than one unit long, and also with strings that are not single characters, say if you want to make a change after the first "::".
-1
private func getText(text: String, size: CGFloat) -> NSAttributedString {
    let attributedText = NSMutableAttributedString(string: text)
    let comaRange = (text as NSString).range(of: ",")
    let afterComaRange = NSRange(location: comaRange.location  + 1 , length: text.count - comaRange.location - 1)
    let font = UIFont.themeFont(ofSize: size, weight: .medium)
    let changedFont = UIFont.themeFont(ofSize: 14, weight: .medium)
    attributedText.addAttribute(.font, value: font, range: NSRange(location: 0, length: comaRange.location))
    attributedText.addAttribute(.font, value: changedFont, range: afterComaRange)
    return attributedText
}

this is my answer and it's work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.