NSMutableAttributedString Append in UITextView - objective-c

I need to append 2 NSMutableAttributedString for my UITextview when user selection different words like the example.
string = #"blabla1 blabla2 blabla3"
in first time the user select #"blabla1"
and the text looks like that #"blabla1 blabla2 blabla3"
and after I select #"blabla3" the result I want to get in My UITextview is #"blabla1 blabla2 blabla3"
now the result I get is #"blabla1 blabla2 blabla3 blabla1 blabla2 blabla3"
that my code :
-(NSMutableAttributedString*)getNSMutableAttributedString:(NSString*)string withRange:(NSRange)range withTextView:(UITextView*)textView
{
if (!str)
{
str = [[NSMutableAttributedString alloc] initWithString:string];
UIFont *font = [UIFont boldSystemFontOfSize:16];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(range.location, range.length)];
}
else
{
NSMutableAttributedString *mutableAttString = [[NSMutableAttributedString alloc] initWithString:string];
UIFont *font = [UIFont boldSystemFontOfSize:16];
[mutableAttString addAttribute:NSFontAttributeName value:font range:NSMakeRange(range.location, range.length)];
NSMutableAttributedString *first = str;
NSMutableAttributedString *second = mutableAttString;
NSMutableAttributedString* result = [first mutableCopy];
[result appendAttributedString:second];
str = result;
}
return str;
}

Attributes can be added multiply times to one string.And you create a new attributedString from string, which don't have attributes.
In result you receive #"blabla1 blabla2 blabla3 blabla1 blabla2 blabla3"
-(NSMutableAttributedString*)getNSMutableAttributedString:(NSMutableAttributedString*)string withRange:(NSRange)range withTextView:(UITextView*)textView
{
if (!str)
{
str = [[NSMutableAttributedString alloc] initWithAttributedString:string];
UIFont *font = [UIFont boldSystemFontOfSize:16];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(range.location, range.length)];
}
else
{
UIFont *font = [UIFont boldSystemFontOfSize:16];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(range.location, range.length)];
}
return str;
}

What you should be doing is get str (the existing attributedText from the UITextView) and then add an attribute to the specific range
str = [textView attributedText];
UIFont *font = [UIFont boldSystemFontOfSize:16];
[str addAttribute:NSFontAttributeName value:font range:NSMakeRange(range.location, range.length)];
return str;
What you are doing is creating a new attributed string with the same content but different attributes and then appending to the existing attributedText. That is why you see the text repeated twice.

Related

objective c - HTML to NSAttributedString

I have written a function that converts HTML text to NSAttributedString. It is working fine. However, I have noticed that some tags when nested inside another tag, their fonts get overwritten.
Here's my code.
+(NSMutableAttributedString*) replaceHTMLTags : (NSString*) text : (NSString*) fontName : (CGFloat) fontSize
{
UIFont* font = [UIFont fontWithName:fontName size:fontSize];
NSMutableParagraphStyle* paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentJustified;
text = [text stringByReplacingOccurrencesOfString:#"<br>" withString:#"\n"];
NSMutableAttributedString* finalText = [[NSMutableAttributedString alloc]initWithString:text];
[finalText setAttributes:#{NSFontAttributeName:font} range:NSMakeRange(0, [finalText string].length)];
finalText = [self recurseFunc:finalText :#"" : font : paragraphStyle];
return finalText;
}
+(NSMutableAttributedString*) recurseFunc : (NSMutableAttributedString*) text : (NSString*) tag : (UIFont*) font : (NSMutableParagraphStyle*) paragraphStyle
{
NSMutableAttributedString* finalText = text;
NSRange newOpenTagRange;
//RECURSE IF THERE ARE MORE TAGS
while((newOpenTagRange = [[text string] rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
{
NSString* openTagName = [[text string] substringWithRange:newOpenTagRange];
NSString* closeTagName = [self getCloseTagName: openTagName];
NSRange newCloseTagRange = [[text string ]rangeOfString:closeTagName];
if(newCloseTagRange.location != NSNotFound)
{
NSString* textWithTags = [[text string] substringWithRange:NSMakeRange(newOpenTagRange.location, newCloseTagRange.location - newOpenTagRange.location + newCloseTagRange.length)];
NSString* newPlainText = [textWithTags stringByReplacingOccurrencesOfString:openTagName withString:#""];
newPlainText = [newPlainText stringByReplacingOccurrencesOfString:closeTagName withString:#""];
NSMutableAttributedString* newText = [[NSMutableAttributedString alloc]initWithString:newPlainText attributes:#{NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
newText = [self recurseFunc:newText :openTagName : font : paragraphStyle];
[finalText replaceCharactersInRange:NSMakeRange(newOpenTagRange.location, newCloseTagRange.location - newOpenTagRange.location + newCloseTagRange.length) withAttributedString:newText];
}
else
{
NSLog(#"Cannot find closing tag for tag %#", openTagName);
}
}
//FORMAT HTML TAGS
if([tag containsString:#"<p"])
{
[finalText.mutableString appendString:#"\n\n"];
}
else if ([tag isEqualToString:#"<i>"])
{
UIFont* italicFont = [UIFont fontWithName:#"Arial-ItalicMT" size:DEFAULT_FONT_SIZE];
[finalText addAttribute:NSFontAttributeName value:italicFont range:NSMakeRange(0, [finalText string].length)];
}
else if ([tag isEqualToString:#"<b>"])
{
UIFont* boldFont = [UIFont fontWithName:#"Arial-BoldMT" size:DEFAULT_FONT_SIZE];
[finalText addAttribute:NSFontAttributeName value:boldFont range:NSMakeRange(0, [finalText string].length)];
}
else if([tag isEqualToString:#"<ul>"])
{
NSMutableParagraphStyle* tempStyle = [[NSMutableParagraphStyle alloc]init];
tempStyle.headIndent = 30;
tempStyle.firstLineHeadIndent = 10;
tempStyle.lineBreakMode = NSLineBreakByWordWrapping;
tempStyle.alignment = NSTextAlignmentJustified;
NSString* temp = [[finalText string]stringByReplacingOccurrencesOfString:#"###" withString:#"•\t"];
temp = [NSString stringWithFormat:#"\n%#", temp];
[finalText setAttributedString:[[NSAttributedString alloc] initWithString:temp]];
[finalText addAttribute:NSParagraphStyleAttributeName value:tempStyle range:NSMakeRange(0, [finalText string].length)];
}
else if ([tag isEqualToString:#"<li>"])
{
NSMutableAttributedString* tempAS = [[NSMutableAttributedString alloc]initWithString:#"###$$$\n"];
NSRange r = [[tempAS string]rangeOfString:#"$$$"];
[tempAS replaceCharactersInRange:r withAttributedString:finalText];
[finalText setAttributedString:tempAS];
}
return finalText;
}
This does exactly what it is supposed to do, except for one specific case.
For instance, if I have a <b> or an <i> tag inside a <ul><li> tag, the <b> or <i> don't get rendered.
For converting HTML to NSAttributedString you can use the following code:
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];

changing colour of a part of string objective c [duplicate]

This question already has answers here:
Are there any analogues of [NSString stringWithFormat:] for NSAttributedString
(4 answers)
Closed 6 years ago.
I have a string like this
NSString * string = [[NSString alloc]initwithformat: #"The current balance left is %# out of %#",leftAmount,totalAmount];
How can i change the colour of string recieved as %# without knowing the range of the recieved string.
Do something like this,
NSString *leftAmount = #"1000";
NSString *totalAmount = #"2000";
UIColor *color = [UIColor redColor];
NSDictionary *attrs = #{ NSForegroundColorAttributeName : color };
NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:leftAmount attributes:attrs];
NSAttributedString *attrStr1 = [[NSAttributedString alloc] initWithString:totalAmount attributes:attrs];
NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:#"The current balance left is "];
[string appendAttributedString:attrStr];
NSMutableAttributedString * string1 = [[NSMutableAttributedString alloc] initWithString:#" out of "];
[string1 appendAttributedString:attrStr1];
[string appendAttributedString:string1];
NSLog(#"Your Full String - %#", string);
Hope this will help you.
do something like this,
NSString *leftAmount,*totalAmount;
leftAmount = #"1000";
totalAmount = #"2000";
NSString * string = [NSString stringWithFormat:#"The current balance left is %# out of %#",leftAmount,totalAmount];
NSDictionary *attribs = #{
NSForegroundColorAttributeName: [UIColor blueColor],
NSFontAttributeName: [UIFont systemFontOfSize:12]
};
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:string
attributes:attribs];
UIColor *grayColor = [UIColor colorWithRed:186.0f/255.0f green:186.0f/255.0f blue:186.0f/255.0f alpha:1];
NSRange leftAmountTextRange = [string rangeOfString:leftAmount];
[attributedText setAttributes:#{NSForegroundColorAttributeName:grayColor}
range:leftAmountTextRange];
It'll also calculate the dynamic data value range.
I hope it will helps you,
Thanks.

NSMutableAttributedString with NSRange issue

I have the following string
22\nShaʻban\n1435
and i'm using NSMutableAttributedString to format the above string using multiple fonts as follows:
NSString* orgString=#"22\nShaʻban\n1435";
NSMutableAttributedString *attString=[[NSMutableAttributedString alloc] initWithString:[dateStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
UIFont *dayFont=[UIFont fontWithName:#"Helvetica-Bold" size:40.0f];
UIFont *monthFont=[UIFont fontWithName:#"Arial" size:22.0f];
UIFont *yearFont=[UIFont fontWithName:#"Arial" size:20.0f];
//format day part
[attString addAttribute:NSFontAttributeName value:dayFont range:NSMakeRange(0,2)];
//format month part
[attString addAttribute:NSFontAttributeName value:monthFont range:NSMakeRange(3,[self indexOf:[dateStr substringFromIndex:3] andSearchChar:#"\n"])];
//format year part, app crashes here
[attString addAttribute:NSFontAttributeName value:yearFont range:NSMakeRange([self indexOf:[dateStr substringFromIndex:3] andSearchChar:#"\n"]+1,[dateStr length])];
- (int) indexOf:(NSString*)orgStr andSearchChar:(NSString *)charToSearc {
NSRange range = [orgStr rangeOfString:charToSearc];
if ( range.length > 0 ) {
return range.location;
} else {
return -1;
}
}
i don't know why it crashes when trying to format the last part, i made arrange from the last position in part two +1 to the length of the string, any help please
NSRange NSMakeRange (
NSUInteger loc,
NSUInteger len
);
A range is a location and a length, not a start and end location. So you need to change how you calculate the range content.
Or, split the source string apart, create an attributed string for each part and then append them together.
I'd suggest this:
NSString* orgString=#"22\nShaʻban\n1435";
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] init];
UIFont *dayFont = [UIFont fontWithName:#"Helvetica-Bold" size:40.0f];
UIFont *monthFont = [UIFont fontWithName:#"Arial" size:22.0f];
UIFont *yearFont = [UIFont fontWithName:#"Arial" size:20.0f];
NSArray *array = [orgString componentsSeparatedByString:#"\n"];
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:[array objectAtIndex:0] attributes:#{NSFontAttributeName: dayFont}]];
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n"]];
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:[array objectAtIndex:1] attributes:#{NSFontAttributeName: monthFont}]];
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:#"\n"]];
[attrString appendAttributedString:[[NSAttributedString alloc] initWithString:[array objectAtIndex:2] attributes:#{NSFontAttributeName: yearFont}]];
So, no NSRange issue. Plus as said by #Wain, you misunderstood what's a NSRange.
Instead of what you're doing, once you found the location, you had to put, as second parameter of NSMakeRange: nextLocation-currentLocation. Id est, for last one, something like this:
NSMakeRange([self indexOf:[dateStr substringFromIndex:3] andSearchChar:#"\n"]+1,
[dateStr length]-[self indexOf:[dateStr substringFromIndex:3] andSearchChar:#"\n"]+1)

How to combine 2 strings into one nsatributed string?

NSString * strTimeBefore = [timeBefore componentsJoinedByString:#" "];
NSString * strTimeAfter = [timeAfter componentsJoinedByString:#" "];
I want the resulting string to be an NSAttributedString where the time in strTimeAfter is in bold
You probably want something like:
NSString *boldFontName = [[UIFont boldSystemFontOfSize:12] fontName];
NSString *yourString = [NSString stringWithFormat:#"%# %#", strTimeBefore, strTimeAfter;
// start at the end of strTimeBefore and go the length of strTimeAfter
NSRange boldedRange = NSMakeRange([strTimeBefore length] + 1, [strTimeAfter length]);
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:yourString];
[attrString beginEditing];
[attrString addAttribute:NSFontAttributeName
value:boldFontName
range:boldedRange];
[attrString endEditing];
And my answer is cribbed from Jacob's answer to this very closely related question.
Take two attribute string in that store your first string into one attribute string without changing its attributes, In second attribute string store your second string with changing its attibutes and then append both attribute string into one NSMutableAttributeString Try like this below:-
NSString * strTimeBefore = [timeBefore componentsJoinedByString:#" "];
NSString * strTimeAfter = [timeAfter componentsJoinedByString:#" "];
NSAttributedString *attrBeforeStr=[[NSAttributedString alloc]initWithString:strTimeBefore];
NSMutableDictionary *attributes = [[NSMutableDictionary alloc] init];
[attributes setObject:[NSColor yellowColor] forKey:NSBackgroundColorAttributeName];
NSFont *font = [[NSFontManager sharedFontManager] fontWithFamily:#"Arial" traits:NSBoldFontMask weight:5 size:14];
[attributes setObject:font forKey:NSFontAttributeName];
NSAttributedString *attrAftStr=[[NSAttributedString alloc]initWithString:strTimeAfter attributes:];
NSMutableAttributedString *string=[[NSMutableAttributedString alloc] init];
[string appendAttributedString:attrBeforeStr];
[string appendAttributedString:strTimeAfter];
Note: You can change font color as well in attribute string, if it is required.

NSAttributedString with dynamic ranges

I have to format a string with 3 styles. The string is something like:
1.0000 of 2.000
1.0000 has a foreground red color, of has a smaller font and 2.000 has to be green.
The problem is that the numbers could be in any range, so the first and the second numbers could be composed by 4,5,6 whatever chars.
How can I perform formatting of a string like that?
Edit-----------------
I add some more info: the string maintains its format. So for example this could be its template: N of N
Lets suppose you have these three :
NSString *string0 = #"1.0000"; NSString *string1 = #"of"; NSString
*string2 = #"2.000";
NSString *text = [NSString stringWithFormat:#"%# %# %#",
string0 ,string1,string2];
//whole String attribute
NSDictionary *attribs = #{
NSForegroundColorAttributeName:[UIColor whiteColor],
NSFontAttributeName:[UIFont systemFontOfSize:10]
};
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text attributes:attribs];
NSRange string0Range = [text rangeOfString:string0];
NSRange string1Range = [text rangeOfString:string1];
NSRange string2Range = [text rangeOfString:string2];
[attributedText setAttributes:#{NSForegroundColorAttributeName:[UIColor redColor], NSFontAttributeName:[UIFont systemFontOfSize:15] range:string0Range];
[attributedText setAttributes:#{NSForegroundColorAttributeName:[UIColor blackColor], NSFontAttributeName:[UIFont systemFontOfSize:12] range:string1Range]; [attributedText setAttributes:#{NSForegroundColorAttributeName:[UIColor greenColor], NSFontAttributeName:[UIFont systemFontOfSize:15] range:string2Range];
[yourLabel setAttributedText:attributedText];
Use NSScanner or NSRegularExpression to find the numeric expression and its pieces.
The simplest solution would be :
NSString* myString = #"12.345 of 56.789";
NSMutableAttributedString * tempString = [[NSMutableAttributedString alloc] initWithString:myString];
NSRange midrange = [tempString.string rangeOfString:#"of"];
[tempString addAttributes:#{NSFontAttributeName : [UIFont fontWithName:#"HelveticaNeue" size:16.6],
NSForegroundColorAttributeName : [UIColor redColor]}
range:NSMakeRange(0, midrange.location)];
[tempString addAttributes:#{NSFontAttributeName : [UIFont fontWithName:#"HelveticaNeue" size:16.6],
NSForegroundColorAttributeName : [UIColor blackColor]}
range:midrange];
[tempString addAttributes:#{NSFontAttributeName : [UIFont fontWithName:#"HelveticaNeue" size:16.6],
NSForegroundColorAttributeName : [UIColor greenColor]}
range:NSMakeRange(midrange.location + midrange.length, tempString.length - midrange.location - midrange.length)];
yourElement.attributedText = tempString;