Replace substring of NSAttributedString with another NSAttributedString
I want to replace a substring (e.g. @"replace"
) of an NSAttributedString
with another NSAttributedString
.
I am looking for an equivalent method to NSString
's stringByReplacingOccurrencesOfString:withString:
for NSAttributedString
.
Solution 1:
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:
.
Solution 2:
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.
Solution 3:
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"];
Solution 4:
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
}
}
Solution 5:
With Swift 4 and iOS 11, you can use one of the 2 following ways in order to solve your problem.
#1. Using NSMutableAttributedString
replaceCharacters(in:with:)
method
NSMutableAttributedString
has a method called replaceCharacters(in:with:)
. replaceCharacters(in:with:)
has the following declaration:
Replaces the characters and attributes in a given range with the characters and attributes of the given attributed string.
func replaceCharacters(in range: NSRange, with attrString: NSAttributedString)
The Playground code below shows how to use replaceCharacters(in:with:)
in order to replace a substring of an NSMutableAttributedString
instance with a new NSMutableAttributedString
instance:
import UIKit
// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)
// Set new attributed string
let newString = "new"
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
let newAttributedString = NSMutableAttributedString(string: newString, attributes: newAttributes)
// Get range of text to replace
guard let range = mutableAttributedString.string.range(of: "initial") else { exit(0) }
let nsRange = NSRange(range, in: mutableAttributedString.string)
// Replace content in range with the new content
mutableAttributedString.replaceCharacters(in: nsRange, with: newAttributedString)
#2. Using NSMutableString
replaceOccurrences(of:with:options:range:)
method
NSMutableString
has a method called replaceOccurrences(of:with:options:range:)
. replaceOccurrences(of:with:options:range:)
has the following declaration:
Replaces all occurrences of a given string in a given range with another given string, returning the number of replacements.
func replaceOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> Int
The Playground code below shows how to use replaceOccurrences(of:with:options:range:)
in order to replace a substring of an NSMutableAttributedString
instance with a new NSMutableAttributedString
instance:
import UIKit
// Set initial attributed string
let initialString = "This is the initial string"
let attributes = [NSAttributedStringKey.foregroundColor : UIColor.red]
let mutableAttributedString = NSMutableAttributedString(string: initialString, attributes: attributes)
// Set new string
let newString = "new"
// Replace replaceable content in mutableAttributedString with new content
let totalRange = NSRange(location: 0, length: mutableAttributedString.string.count)
_ = mutableAttributedString.mutableString.replaceOccurrences(of: "initial", with: newString, options: [], range: totalRange)
// Get range of text that requires new attributes
guard let range = mutableAttributedString.string.range(of: newString) else { exit(0) }
let nsRange = NSRange(range, in: mutableAttributedString.string)
// Apply new attributes to the text matching the range
let newAttributes = [NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue]
mutableAttributedString.setAttributes(newAttributes, range: nsRange)