Reputation: 331
I have a UILabel with text "hello world, hello". There are 2 hello words. And I want to replace the only 'bold hello' to 'thanks' without bold.
I use this code:
uiLabel1.text = "hello world, hello"
let target = "hello"
let replace = "thanks"
uiLabel1.text.replacingOccurrences(of: target, with: replace, options: NSString.CompareOptions.literal, range: nil)
And the result is: "thanks world, thanks"
The result I want: "hello world, thanks"
Upvotes: 0
Views: 1176
Reputation: 347314
Okay, so there's probably an easier way to do this...
So, I went through the API (like super quick) and looked for something like lastIndexOf
, which lead me on a little trail to String#range(of:options)
, which allows you to search backwards, hmmm, interesting.
This returns a Range<String.Index>
... okay, so how can I use that?! Hmm, maybe String#replacingOccurrences(of:with:options:range:)
🤔
So, crack open a play ground and...
var str = "hello world, hello"
let lastIndexOf = str.range(of: "hello", options: .backwards)
str = str.replacingOccurrences(of: "hello", with: "thanks", options: .caseInsensitive, range: lastIndexOf)
str
now equals "hello world, thanks"
Hi @MadProgrammer, your code is to replace the last hello word to thanks, right? But my question is to replace hello with the bold attribute, it may in the first, middle or at the end of a string.
Okay, so clearly we're missing some context...
Assuming, now, you're using a NSAttributedString
, it becomes slightly more complicated
Building the string itself is not hard, figuring out how to find string components by attribute, a little more difficult.
Lucky for us, we have the Internet. So, the following is based on ideas I got from:
One of the important things to remember when trying to solve an issue, you'll be lucky to find a single answer which does it all, instead, you need to break your issue down and focus on solving individual elements, and be prepared to go back to the start 😉
So, once again, unto the play ground...
import UIKit
var str = "hello world, "
//let lastIndexOf = str.range(of: "hello", options: .backwards)
//str = str.replacingOccurrences(of: "hello", with: "thanks", options: .caseInsensitive, range: lastIndexOf)
extension UIFont {
var isBold: Bool {
return fontDescriptor.symbolicTraits.contains(.traitBold)
}
var isItalic: Bool {
return fontDescriptor.symbolicTraits.contains(.traitItalic)
}
}
// Just so I can see that the style ;)
let fontSize = CGFloat(24.0)
let boldAttrs = [
NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: fontSize),
NSAttributedString.Key.foregroundColor: UIColor.white // Playground
]
// Playground only
let plainAttrs = [
NSAttributedString.Key.foregroundColor: UIColor.white // Playground
]
let boldText = NSMutableAttributedString(string: "hello", attributes: boldAttrs)
let styledText = NSMutableAttributedString(string: str, attributes: plainAttrs)
let someMoreBoldText = NSMutableAttributedString(string: "not to be replaced", attributes: boldAttrs)
// Attributes can be combined with their appear together ;)
styledText.append(boldText)
styledText.append(NSMutableAttributedString(string: " ", attributes: plainAttrs))
styledText.append(someMoreBoldText)
styledText.append(NSMutableAttributedString(string: " ", attributes: plainAttrs))
styledText.append(boldText)
styledText.enumerateAttribute(NSAttributedString.Key.font, in: NSRange(0..<styledText.length)) { (value, range, stop) in
guard let font = value as? UIFont, font.isBold else {
return;
}
let subText = styledText.attributedSubstring(from: range)
guard subText.string == "hello" else {
return
}
styledText.replaceCharacters(in: range, with: "thanks")
}
styledText
Which outputs...
The important things for me are:
Upvotes: 2
Reputation: 16180
Here is the code. But actually this is hardcoded. If the target
enclosed in between <b></b>
, it will work.
var text = "hello world, <b>hello</b>"
let target = "hello"
let replace = "thanks"
text = text.replacingOccurrences(of: "<b>\(target)</b>", with: replace, options: .literal, range: nil) //hello world, thanks
Upvotes: 0