Attributed string color- Objective-c - ios

I have a display like in the picture
Im getting this from a string str which is like below
Code I have tried
lbl1.text=newStr;
NSString *textxtra = #"Xtra";
NSString *textremove = #"Remove";
NSMutableAttributedString *attrsString = [[NSMutableAttributedString alloc] initWithAttributedString:lbl1.attributedText];
// search for word occurrence
NSRange range = [lbl1.text rangeOfString:textxtra];
NSRange range1 = [lbl1.text rangeOfString:textremove];
if (range.location != NSNotFound) {
[attrsString addAttribute:NSForegroundColorAttributeName value:[UIColor systemGreenColor] range:range];
}
if (range1.location != NSNotFound) {
[attrsString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:range1];
}
// set attributed text
lbl1.attributedText = attrsString;
How to get the string before Xtra (American Cheese) and after Xtra($1) in green color ?
The part before Remove(House Mayo) in red color?
i.e The whole string American Cheese: Xtra $1 should be in green color.
I get the idea to take the string in between \n.
i.e string before Xtra upto \n and after Xtra upto \n
But couldn't understand exactly how to implement
Any ideas/suggestions will be helpful

Not tested, but this should do the trick:
NSString *initialSting = #"";
NSArray *lines = [initialSting componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableArray *attributedLines = [[NSMutableArray alloc] init];
NSDictionary *redAttributes = #{};
NSDictionary *greenAttributes = #{};
for (NSString *aLine in lines)
{
NSAttributedString *aLineAttributedString;
//Here, you could also check, that the string is like "someString: Xtra someOtherString", because if Xtra is misplaced...
if ([aLine containsString:#"Xtra"])
{
aLineAttributedString = [[NSAttributedString alloc] initWithString:aLine attributes:greenAttributes];
//Here, you could also check, that the string is like "someString: Remove someOtherString", because if Remove is misplaced...
}
else if ([aLine containsString:#"Remove"])
{
aLineAttributedString = [[NSAttributedString alloc] initWithString:aLine attributes:redAttributes];
}
else
{
aLineAttributedString = [[NSAttributedString alloc] initWithString:aLine];
}
[attributedLines addObject:aLineAttributedString];
}
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init];
NSAttributedString *newLine = [[NSAttributedString alloc] initWithString:#"\n"];
if ([attributedLines count] > 0)
{
for (NSUInteger i = 0; i < [attributedLines count] - 1; i++)
{
[attributedString appendAttributedString:attributedLines[i]];
[attributedString appendAttributedString:newLine];
}
[attributedString appendAttributedString:[attributedLines lastObject]];
}
The logic:
Get an array of NSString for each line with componentsSeparatedByCharactersInSet:
Create an array of NSAttributedString, that will be populated for each line.
For each line, color it if needed, then add it to the array.
Finally, there is no componentsJoinedByString: for NSAttributedString, so do a manual for loop to reconstruct the final NSAttributedString.

Related

Add a nsstring at the end of attributed string in textview iOS

I have a text like "Hello There" which will be a attributed string with red color. Then at the end of that attributed string I want to append a nsstring 'Who are you?' But whenever I append a nsstring, the whole string becomes normal nsstring and the property of the attributed string is being removed. My attempt so far:
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:#"Hello There"];
[attributeString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(0,[attributeString length])];
NSMutableAttributedString *previousAttrString = [[NSMutableAttributedString alloc] initWithString:messageTextView.text];
[previousAttrString insertAttributedString: attributeString atIndex:location];
messageTextView.attributedText = previousAttrString;
messageTextView.font = [UIFont fontWithName:#"Helvetica" size:15];
NSString *messageWithContact = [NSString stringWithFormat: #"%# %#", messageTextView.text, #"Who are you?"];
messageTextView.text=messageWithContact;
What I did wrong? Please help me.
Replace the bottom lines
NSString *messageWithContact = [NSString stringWithFormat: #"%# %#", messageTextView.text, #"Who are you?"];
messageTextView.text=messageWithContact;
with
NSMutableAttributedString *newString = messageTextView.attibutedText;
[newString appendAttributedString: [[NSAttributedString alloc] initWithString: #"Who are you?"];
messageTextView.attibutedText = newString;

How to Convert HTML String to the Formatted String in Objective C

I am using the following code it just eliminating the HTML tags but does not format string. How to format as it is shown in Html.
-(NSString *)convertHTML:(NSString *)html {
NSScanner *myScanner;
NSString *text = nil;
myScanner = [NSScanner scannerWithString:html];
while ([myScanner isAtEnd] == NO) {
[myScanner scanUpToString:#"<" intoString:NULL] ;
[myScanner scanUpToString:#">" intoString:&text] ;
html = [html stringByReplacingOccurrencesOfString:[NSString stringWithFormat:#"%#>", text] withString:#""];
}
//
html = [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
return html;
}
Try this one.this might be helpful
textview= [[UITextView alloc]initWithFrame:CGRectMake(10, 130, 250, 170)];
NSString *str = [NSString stringWithFormat:#"<font color='red'>A</font><br/> shared photo of <font color='red'>B</font> with <font color='red'>C</font>, <font color='red'>D</font> "];
[textview setValue:str forKey:#"contentToHTMLString"];
textview.textAlignment = NSTextAlignmentLeft;
textview.editable = NO;
textview.font = [UIFont fontWithName:#"Verdana" size:20.0];
i would recommend using a third party library for that like https://github.com/mwaterfall/MWFeedParser/blob/master/Classes/NSString%2BHTML.m
and than later use it like this:
NSString *string = [#"<b>Your HTML String</b>" stringByConvertingHTMLToPlainText];
There might be a better way of doing this but here is my run on this.
1. scan the html string for the html tags
2. create attributed string for each html tag
3. find all the occurrences of the tag in the string and apply the attributes to them in attributed string.
Here is my sample code to detect break and bold tag
//This method returns an array of occurrence of the tag in string
- (NSArray *)arrayOfStringFromString:(NSString *)string enclosedWithinString:(NSString *)stringOne andString:(NSString *)stringTwo{
NSError *error = NULL;
NSString *pattern =[NSString stringWithFormat:#"%#(.*?)%#",stringOne,stringTwo];
NSRange range = NSMakeRange(0, string.length);
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionCaseInsensitive error:&error];
NSArray *matches = [regex matchesInString:string options:NSMatchingReportProgress range:range];
NSMutableArray *subStringArray = [NSMutableArray array];
[matches enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSTextCheckingResult class]])
{
NSTextCheckingResult *match = (NSTextCheckingResult *)obj;
NSRange matchRange = match.range;
[subStringArray addObject:[string substringWithRange:matchRange]];
}
}];
return subStringArray;
}
//This method returns the attributed string
- (NSMutableAttributedString*)attributedStringFromHTMLString:(NSString *)htmlString andFontSize:(float)size
{
htmlString = [[self stringByDecodingXMLEntities:htmlString] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSArray *subStringArray = [self arrayOfStringFromString:htmlString enclosedWithinString:#"<b>" andString:#"</b>"];
UIFont *lightFont = [UIFont fontWithName:#"HelveticaNeue-Light" size:size];
UIFont *mediumFont = [UIFont fontWithName:#"HelveticaNeue-Medium" size:size];
htmlString = [[[htmlString stringByReplacingOccurrencesOfString:#"<b>" withString:#""] stringByReplacingOccurrencesOfString:#"</b>" withString:#""] stringByReplacingOccurrencesOfString:#"<br>" withString:#"\n"];
NSArray *otherHtmlTags = [self arrayOfStringFromString:htmlString enclosedWithinString:#"<" andString:#">"];
for (NSString *otherHtmlString in otherHtmlTags) {
[htmlString stringByReplacingOccurrencesOfString:otherHtmlString withString:#""];
}
htmlString = [htmlString stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc]initWithString:htmlString attributes:#{NSFontAttributeName:lightFont}];
for (NSString *string in subStringArray) {
[attributedString addAttributes:#{NSFontAttributeName:mediumFont} range:[htmlString rangeOfString:string]];
}
return attributedString;
}
-(NSString *) stringByStrippingHTML {
NSRange r;
NSString *s = [[self copy] autorelease];
while ((r = [s rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
s = [s stringByReplacingCharactersInRange:r withString:#""];
return s;
}
use this method its work for me
NSAttributedString can be initialised with HTML and will display it just fine.
If you want to remove tags, code that replaces tags in the string repeatedly runs in O (n^2), that is it will crawl for large strings. You need to have a mutable output string, and append bits to it as needed, to get linear time.
You can look for "<" and ">" characters. You then need to find which tags you have, because for some tags, everything between the start and end tag has to be deleted as well, or you end up with random rubbish.
You need to handle somehow.
And when you are done, you need to replace all & escape sequences with the correct characters.

How the find the all the ranges of all strings between quotation marks

I have been working on this problem for a couple of days but can't find a solution to it. I have a very long string that contains a lot of quotes and I want to be able to find all of the strings (the ranges of this strings) so that I can bold them using NSMutableAttributedString
Example
This is a string "with quoted" text and there is "some more" quoted text.
I want to be able to turn this string into the following:
This is a string "with quoted" text and there is "some more" quoted text.
This is what I have so far but it won't do the rest of the string:
- (void)stringBetweenString:(NSString*)start andString:(NSString*)end inString:(NSMutableAttributedString *)text
{
NSRange startRange = [[text string] rangeOfString:start];
if (startRange.location != NSNotFound)
{
NSRange targetRange;
targetRange.location = startRange.location + startRange.length;
targetRange.length = [text length] - targetRange.location;
NSRange endRange = [[text string] rangeOfString:end options:0 range:targetRange];
if (endRange.location != NSNotFound)
{
targetRange.length = endRange.location - targetRange.location;
[text addAttribute:NSFontAttributeName value:[NSFont fontWithName:#"Helvetica-Bold" size:12.0] range:targetRange];
}
}
}
If text is too long this could be little bit slow however it works. Working on Regex solution.
NSString *string = #"Example This is a string \"with quoted1\" text and there is \"some more1\" quoted text. I want to be able to turn this string into the following: This is a string \"with quoted2\" text and there is \"some more2\" quoted text.";
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:string attributes:nil];
int leftFromLeft = 0;
while ([string rangeOfString:#"\""].location != NSNotFound) {
NSRange quoteLocationFirst = [string
rangeOfString:#"\""
options:0
range:NSMakeRange(leftFromLeft, string.length - leftFromLeft)
];
leftFromLeft = quoteLocationFirst.location + quoteLocationFirst.length;
NSRange quoteLocationSecond = [string
rangeOfString:#"\""
options:0
range:NSMakeRange(leftFromLeft, string.length - leftFromLeft)
];
NSRange quotedTextRange = NSMakeRange(
quoteLocationFirst.location,
quoteLocationSecond.location - quoteLocationFirst.location + 1
);
UIFont *font = [UIFont fontWithName:#"Helvetica-Bold" size:30.0f];
[attString addAttribute:NSFontAttributeName value:font range:quotedTextRange];
NSLog(#"%# \r\n\r\n", [string substringWithRange:quotedTextRange]);
leftFromLeft = quoteLocationSecond.location + quoteLocationSecond.length;
if ([string rangeOfString:#"\"" options:0 range:NSMakeRange(leftFromLeft, string.length - leftFromLeft)].location == NSNotFound) {
string = #"";
}
}
Edit
Regex solution appears to be better/faster.
NSString *string = #"Example This is a string \"with quoted1\" text and there is \"some more1\" quoted text. I want to be able to turn this string into the following: This is a string \"with quoted2\" text and there is \"some more2\" quoted text. Example This is a string \"with quoted3\" text and there is \"some more3\" quoted text. I want to be able to turn this string into the following: This is a string \"with quoted4\" text and there is \"some more4\" quoted text.";
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:string attributes:nil];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\"([^\"]*)\"" options:NSRegularExpressionCaseInsensitive error:nil];
NSArray *arrayOfAllMatches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)];
for (NSTextCheckingResult *match in arrayOfAllMatches) {
UIFont *font = [UIFont fontWithName:#"Helvetica-Bold" size:30.0f];
[attString addAttribute:NSFontAttributeName value:font range:match.range];
//NSLog(#"%#", [string substringWithRange:match.range]);
}

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:

Select Bold and Italicized text from textField

How can I select only bold and italicized text entered by user in a textField/textView ?
We can make a selected text bold, italicized, underline and any combination of these three but what about its vice-versa.
*This is not specific to Mac OSX or iOS, solution in either one is good for me.
EDIT:
I tried by reading the text in attributed string as :
NSAttributedString *string=self.textView.string;
But as textView and textField returns NSString so all formatting is gone.
on iOS use attributedText properties with labels/textfields
on OSX use attributedStringValue
you can then enumerate through the attributedText's attributes and check each attribute. Ill whip up some code (osx & iOS)
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:#"none "];
id temp = [[NSAttributedString alloc] initWithString:#"bold " attributes:#{NSFontAttributeName: [UIFont boldSystemFontOfSize:12]}];
[str appendAttributedString:temp];
temp = [[NSAttributedString alloc] initWithString:#"italic " attributes:#{NSFontAttributeName: [UIFont italicSystemFontOfSize:12]}];
[str appendAttributedString:temp];
temp = [[NSAttributedString alloc] initWithString:#"none " attributes:#{NSFontAttributeName: [UIFont systemFontOfSize:12]}];
[str appendAttributedString:temp];
temp = [[NSAttributedString alloc] initWithString:#"bold2 " attributes:#{NSFontAttributeName: [UIFont boldSystemFontOfSize:12]}];
[str appendAttributedString:temp];
self.label.attributedText = str;
NSMutableString *italics = [NSMutableString string];
NSMutableString *bolds = [NSMutableString string];
NSMutableString *normals = [NSMutableString string];
for (int i=0; i<str.length; i++) {
//could be tuned: MOSTLY by taking into account the effective range and not checking 1 per 1
//warn: == might work now but maybe i'd be cooler to check font traits using CoreText
UIFont *font = [str attribute:NSFontAttributeName atIndex:i effectiveRange:nil];
if(font == [UIFont italicSystemFontOfSize:12]) {
[italics appendString:[[str mutableString] substringWithRange:NSMakeRange(i, 1)]];
} else if(font == [UIFont boldSystemFontOfSize:12]){
[bolds appendString:[[str mutableString] substringWithRange:NSMakeRange(i, 1)]];
} else {
[normals appendString:[[str mutableString] substringWithRange:NSMakeRange(i, 1)]];
}
}
NSLog(#"%#", italics);
NSLog(#"%#", bolds);
NSLog(#"%#", normals);
Now here is how to find it. Deducing a selection range from this should be easy peasy :)
Note: you can only have a continuous selection! neither on osx nor on ios can you select n parts of a textfield/textview

Resources