How to change case of an NSMutableAttributedString in Objective-C? - ios

I have a NSMutableAttributedString that contain values in lower case letters. I need to convert all the lowercase letters to uppercase. We can solve this using uppercaseString for normal string as:
[string uppercaseString];
How can I change my case for NSMutableAttributedString? Thanks!

Hope the below code snippet may help you
NSMutableAttributedString * linkString = [[NSMutableAttributedString alloc]initWithString:#"Google"];
NSString * strings = [[linkString string]uppercaseString];
[linkString replaceCharactersInRange:NSMakeRange(0, [linkString length]) withString:strings];
NSLog(#"UpperCase %#",linkString);

NSMutableAttributedString class doesn't have uppercaseString method. So you can use like this..
NSString *str = #"objective-c";
str = [str uppercaseString];
NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc] initWithString:str];
NSLog(#"Attributed String %#",attStr);
And You wanna upper latter in particular range then do something like this...
[attStr replaceCharactersInRange:NSMakeRange(0, 1) withString:#"O"];

Related

Objective-C: Equivalent to stringWithFormat for NSMutableAttributedString

I am trying to add a hyperlink in a string. I have a localized string and I put %# to format my string. When I add the attributed string into my string, the attributed format gives the raw result which is NSLink = "https://www.example.com". I could not find an attributed string formatter the same as the string formatter. How can I achieve the same behaviour in my case?
Code:
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:#"Example"];
[str addAttribute: NSLinkAttributeName value: #"https:/www.example.com" range: NSMakeRange(0, str.length)];
NSMutableAttributedString *originalStr = [[NSMutableAttributedString alloc] initWithString: self.pageDescriptions[3].localized];
pageContentViewController.messageText = [NSString stringWithFormat:self.pageDescriptions[index].localized, str];
stringWithFormat: is only doing substitutions of formatters. So if your formatters are simple enough, like a %#, you can just search for it with rangeOfString: and do the substitution yourself with replaceCharactersInRange:withAttributedString:.
NSMutableAttributedString *strWithLink = [[NSMutableAttributedString alloc] initWithString:#"Example"];
[strWithLink addAttribute:NSLinkAttributeName value:#"https:/www.example.com" range:NSMakeRange(0, strWithLink.length)];
NSMutableAttributedString *strWithFormat = [[NSMutableAttributedString alloc] initWithString:#"hello %# world"];
[strWithFormat replaceCharactersInRange:[strWithFormat.string rangeOfString:#"%#"] withAttributedString:strWithLink];
The result here in strWithFormat kept the attributes of strWithLink.
Note: this won't work correctly if your format is complex, like with %%# %# %%#, because it will replace the first occurrence of %#, while stringWithFormat: would have replaced the middle occurrence.
There isn't one. You create the NSAttributedString by hand, or you create an extension that does what you want if you do this a lot.

Dynamic Number of UILabels in custom tableviewcell

I created a custom tableview cell and I want to display some strings in that cell. I fetch strings from backend thus I don't know how many labels I needed. I tried to concat strings in one label and implemented like below however I want to display strings with different attributes after char ":".
for (AttributesModel* attribute in model.attributes) {
NSString *attributeName = attribute.name;
attributeString = [[attributeString stringByAppendingString: attributeName] mutableCopy];
attributeString = [[attributeString stringByAppendingString: #" : "] mutableCopy];
for (NSDictionary *value in attribute.options) {
attributeString = [[attributeString stringByAppendingString: [value objectForKey:#"name"] ] mutableCopy];
attributeString = [[attributeString stringByAppendingString: #", "] mutableCopy];
}
attributeString = [[attributeString stringByAppendingString: #"\n"] mutableCopy];
}
I could not change attributes of strings thats located after char ":".
Are there any way to do that? Can I create dynamic number of labels in cell or only change attributes of strings that are only located after ":" ?
It sounds like you want to change the attributes such as formatting of a section of text within a UILabel. You can do this by:
Creating an attributed, mutable copy of your string (a.k.a converting an NSString into an NSMutableAttributedString).
Changing the attributes to parts of this copy.
Setting your label's attributedText property to your attributed string.
NSString *myString = #"This is my string";
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:myString];
NSMutableAttributedString *mutableAttributedString = [attributedString mutableCopy];
// The range of text to change, i.e. start from the 5th index
// (starting from 0 like arrays), and continue for 2 characters:
NSRange rangeOfSecondWord = NSMakeRange(5, 2);
// The list of attributes to apply to that range:
NSDictionary *myAttributes = #{
NSForegroundColorAttributeName : [UIColor redColor],
};
// Actually apply the attributes:
[mutableAttributedString setAttributes:myAttributes range:rangeOfSecondWord];
// Set the text of the label to the attributed string:
myLabel.attributedText = mutableAttributedString;
For a list of attributes that you can set in your dictionary, see the Character Attributes reference.
Since you're downloading your strings, you may not know the range beforehand. Since you're concatenating them, here is how you can find the range dynamically:
NSString *stringOne = #"My name is ";
NSString *stringTwo = #"John Citizen";
NSString *joinedStrings = [stringOne stringByAppendingString:stringTwo];
NSRange rangeOfStringTwo = [joinedStrings rangeOfString:stringTwo];

Replace character in NSMutableAttributedString

This works for a regular NSString:
NSString *newString = [myString stringByReplacingOccurrencesOfString:#"," withString:#""];
But there is no such method for NSMutableAttributedString. How could I remove all instances of a comma in an NSMutableAttributedString?
let attrString = NSMutableAttributedString(string: "Hello <b>friend<b>")
attrString.mutableString.replaceOccurrencesOfString("<b>", withString: "", options: NSStringCompareOptions.CaseInsensitiveSearch, range: NSRange(location: 0, length: attrString.length))
Try this :)
Do it before you create the attributed string, if you can or depending on how you source it. If you can't then you can use replaceCharactersInRange:withString: (or replaceCharactersInRange:withAttributedString:), but you need to know the range so you need to search and iterate yourself.
NSString *newString= "I want to ,show, you how to achieve this";
NSMutableAttributedString *displayText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"%#",newString]];
[[displayText mutableString] replaceOccurrencesOfString:#"," withString:#"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, displayText.string.length)];
You can initialize the attributed string with the stripped string with the designed init. No?
The code could be applied from my answer here:
NSAttributedString *attributedString = ...;
NSAttributedString *anotherAttributedString = ...; //the string or characters which will replace
while ([attributedString.mutableString containsString:#"replace"]) {
NSRange range = [attributedString.mutableString rangeOfString:#"replace"];
[attributedString replaceCharactersInRange:range withAttributedString:anotherAttributedString];
}

Display edited NSString in order

I have been working on this for a few days with help from this great community.
I have a NSArray that I need to edit NSStrings within. I have managed to detect a marker in the string and make it bold. However now I am trying to display the strings in the order that they are within the NSArray whilst maintaining the Bold that was added to the specific strings.
I can display the individual Bold String 'string' but I need it to be in order that it is within the array. I know of stringByAppendingString but this would put it at the end.
Any directions would be brilliant.
for (NSString *testWord in legislationArray) {
if ([testWord rangeOfString:#"BOLDME"].location != NSNotFound) {
//Remove Marker
NSString *stripped = [testWord stringByReplacingOccurrencesOfString:#"BOLDME" withString:#""];
//Get string and add bold
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:stripped];
NSRange selectedRange = [stripped rangeOfString:(stripped)];
[string beginEditing];
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica-Bold" size:18.0]
range:selectedRange];
[string endEditing];
//Where to go now with string?
}
}
cell.dynamicLabel.text = [legislationArray componentsJoinedByString:#"\n"];
EDIT
Based on the answers below I got it working however the bold method invokes this error:
componentsJoinedByString return a NSString, when you want a NSAttributedString.
Plus, you're setting your text to a receiver that awaits a NSString (cell.dynamicLabel.text), where what you want should be cell.dynamicLabel.attributedText.
Since there is no equivalent to componentsJoinedByString for a NSAttributedString return, you have to do it the oldway, with a for loop, starting with initializing a NSMutableAttributedString, and adding to it each components (that you may "transform") to it.
Here is a example and related question.
Just use additional array. Change your code to
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
for (NSString *testWord in legislationArray) {
if ([testWord rangeOfString:#"BOLDME"].location != NSNotFound) {
//Remove Marker
NSString *stripped = [testWord stringByReplacingOccurrencesOfString:#"BOLDME" withString:#""];
//Get string and add bold
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:stripped];
NSRange selectedRange = [stripped rangeOfString:(stripped)];
[string beginEditing];
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"Helvetica-Bold" size:18.0]
range:selectedRange];
[string endEditing];
//Where to go now with string?
[attrString appendAttributedString:string];
}
else
{
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:testWord]];
}
// NEW LINE
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n"]];
}
cell.dynamicLabel.attributedText = attrString;
UPDATE:
Your additional issue is not a error - this is a way how XCode shows attributed strings in debug window:

Change attributes of substrings in a NSAttributedString

This question may be a duplicate of this one. But the answers don't work for me and I want to be more specific.
I have a NSString, but I need a NS(Mutable)AttributedString and some of the words in this string should be given a different color. I tried this:
NSString *text = #"This is the text and i want to replace something";
NSDictionary *attributes = # {NSForegroundColorAttributeName : [UIColor redColor]};
NSMutableAttributedString *subString = [[NSMutableAttributedString alloc] initWithString:#"AND" attributes:attributes];
NSMutableAttributedString *newText = [[NSMutableAttributedString alloc] initWithString:text];
newText = [[newText mutableString] stringByReplacingOccurrencesOfString:#"and" withString:[subString mutableString]];
The "and" should be uppercase an red.
The documentation says that mutableString keeps the attribute mappings. But with my replacing-thing, I have no more attributedString on the right side of the assignment (in the last line of my code-snippet).
How can I get what I want? ;)
#Hyperlord's answer will work, but only if there is one occurence of the word "and" in the input string. Anyway, what I would do is use NSString's stringByReplacingOccurrencesOfString: initially to change every "and" to an "AND", then use a little regex to detect matches in attributed string, and apply NSForegroundColorAttributeName at that range. Here's an example:
NSString *initial = #"This is the text and i want to replace something and stuff and stuff";
NSString *text = [initial stringByReplacingOccurrencesOfString:#"and" withString:#"AND"];
NSMutableAttributedString *mutableAttributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(AND)" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,text.length);
[regex enumerateMatchesInString:text options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:1];
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:subStringRange];
}];
And finally, just apply the attributed string to your label.
[myLabel setAttributedText:mutableAttributedString];
I think you should create a NSMutableAttributedString using the existing NSString and then add the style attributes with the appropriate NSRange in order to colorize the parts you want to emphasize for example:
NSString *text = #"This is the text and i want to replace something";
NSMutableAttributedString *mutable = [[NSMutableAttributedString alloc] initWithString:text];
[mutable addAttribute: NSForegroundColorAttributeName value:[UIColor redColor] range:[text rangeOfString:#"and"]];
Be aware: this is just from my head and not tested at all ;-)
Please try this code in Swift 2
var someStr = "This is the text and i want to replace something"
someStr.replaceRange(someStr.rangeOfString("and")!, with: "AND")
let attributeStr = NSMutableAttributedString(string: someStr)
attributeStr.setAttributes([NSForegroundColorAttributeName: UIColor.yellowColor()], range: NSMakeRange(17, 3) )
testLbl.attributedText = attributeStr
Here's another implementation (in Swift) that's helpful if you're doing some more complex manipulations (such as adding/deleting characters) with your attributed string:
let text = "This is the text and i want to replace something"
let mutAttrStr = NSMutableAttributedString(string: text)
let pattern = "\\band\\b"
let regex = NSRegularExpression(pattern: pattern, options: .allZeros, error: nil)
while let result = regex!.firstMatchInString(mutAttrStr.string, options: .allZeros, range:NSMakeRange(0, count(mutAttrStr.string)) {
let substring = NSMutableAttributedString(attributedString: mutAttrStr.attributedSubstringFromRange(result.range))
// manipulate substring attributes here
substring.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range NSMakeRange(0, count(substring.string))
mutAttrStr.replaceCharactersInRange(result.range, withAttributedString: substring)
}
Your final attributed string should be:
let finalAttrStr = mutAttrStr.copy() as! NSAttributedString

Resources