I want to replace a substring (e.g. @\"replace\"
) of an NSAttributedString
with another NSAttributedString
.
I am looking for a
Swift 4: Updated sunkas excellent solution to Swift 4 and wrapped in "extension". Just clip this into your ViewController (outside the class) and use it.
extension NSAttributedString {
func stringWithString(stringToReplace: String, replacedWithString newStringPart: String) -> NSMutableAttributedString
{
let mutableAttributedString = mutableCopy() as! NSMutableAttributedString
let mutableString = mutableAttributedString.mutableString
while mutableString.contains(stringToReplace) {
let rangeOfStringToBeReplaced = mutableString.range(of: stringToReplace)
mutableAttributedString.replaceCharacters(in: rangeOfStringToBeReplaced, with: newStringPart)
}
return mutableAttributedString
}
}
Here is how you can change the string of NSMutableAttributedString, while preserving its attributes:
Swift:
// first we create a mutable copy of attributed text
let originalAttributedText = nameLabel.attributedText?.mutableCopy() as! NSMutableAttributedString
// then we replace text so easily
let newAttributedText = originalAttributedText.mutableString.setString("new text to replace")
Objective-C:
NSMutableAttributedString *newAttrStr = [attribtedTxt.mutableString setString:@"new string"];
Convert your attributed string into an instance of NSMutableAttributedString
.
The mutable attributed string has a mutableString
property. According to the documentation:
"The receiver tracks changes to this string and keeps its attribute mappings up to date."
So you can use the resulting mutable string to execute the replacement with replaceOccurrencesOfString:withString:options:range:
.
I have a specific requirement and fixed like below. This might help someone.
Requirement: In the storyboard, rich text directly added to UITextView's attribute which contains a word "App Version: 1.0". Now I have to dynamise the version number by reading it from info plist.
Solution: Deleted version number 1.0 from the storyboard, just kept "App Version:" and added below code.
NSAttributedString *attribute = self.firsttextView.attributedText;
NSMutableAttributedString *mutableAttri = [[NSMutableAttributedString alloc] initWithAttributedString:attribute];
NSString *appVersionText = @"App Version:";
if ([[mutableAttri mutableString] containsString:appVersionText]) {
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSString* version = [infoDict objectForKey:@"CFBundleShortVersionString"];
NSString *newappversion = [NSString stringWithFormat:@"%@ %@",appVersionText,version] ;
[[mutableAttri mutableString] replaceOccurrencesOfString:appVersionText withString:newappversion options:NSCaseInsensitiveSearch range:NSMakeRange(0, mutableAttri.length)];
self.firsttextView.attributedText = mutableAttri;
}
Done!! Updated/modified attributedText.
NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:@"I am a boy."];
[result addAttribute:NSForegroundColorAttributeName value:[UIColor blackColor] range:NSMakeRange(0, [result length])];
NSMutableAttributedString *replace = [[NSMutableAttributedString alloc] initWithString:@"a"];
[replace addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0, [replace length])];
[result replaceCharactersInRange:NSMakeRange(5, [replace length]) withAttributedString:replace];
In my case, the following way was the only (tested on iOS9):
NSAttributedString *attributedString = ...;
NSAttributedString *anotherAttributedString = ...; //the string which will replace
while ([attributedString.mutableString containsString:@"replace"]) {
NSRange range = [attributedString.mutableString rangeOfString:@"replace"];
[attributedString replaceCharactersInRange:range withAttributedString:anotherAttributedString];
}
Of course it will be nice to find another better way.