I'm using NSMutableAttributedString to change the text in a UITextView as the user types. When the user types '#HELLO#' or "#TEST#" or '#test#', those strings should be red (just an example).
- (void)textViewDidChange:(UITextView *)textView
{
NSString *textViewText = textView.text;
NSMutableAttributedString * string = [[NSMutableAttributedString alloc]initWithString:textViewText];
NSString *space = #" ";
NSArray *words =[textView.text componentsSeparatedByString:space];
for (NSString *word in words) {
if ([word isEqualToString:#"#HELLO#"] || [word isEqualToString:#"#TEST#"] || [word isEqualToString:#"#test#"]) {
NSRange range=[textView.text rangeOfString:word];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:range];
}
else{
NSRange range=[textView.text rangeOfString:word];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:range];
}
}
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"HelveticaNeue-Light" size:20.0]
range:NSMakeRange(0, [textView.text length])];
[textView setAttributedText:string];
}
This works for almost every word, except 'in'. When I type that, 'in' is black rather than white ([UIColor whiteColor]). If I type 't', the 't' in "#test#' turns white.
I'm really confused, can somebody help me out? I thought the else part should catch these strings. Thanks.
I tried your code. I guess the issue is the range you are setting . Because it is always one word it sets color attribute of the first occurrence of that particular word . Whether it be #HELLO# or in . Try typing a particular string repeatedly separated by space you will always get the same output. I have made a few changes in your code and you can see it below . Try it out.
- (void)textViewDidChange:(UITextView *)textView
{
NSString *textViewText = textView.text;
NSLog(#"Text view Text %#" , textViewText );
NSMutableAttributedString * string = [[NSMutableAttributedString alloc]initWithString:textViewText];
NSString *space = #" ";
NSArray *words =[textView.text componentsSeparatedByString:space];
for(NSString *word in words){
NSLog(#"WORD %#" , word);
if ([word isEqualToString:#"#HELLO#"] || [word isEqualToString:#"#TEST#"] || [word isEqualToString:#"#test#"]) {
NSRange range = NSMakeRange(0, string.length);
while(range.location != NSNotFound)
{
range = [[string string] rangeOfString:word options:0 range:range];
if(range.location != NSNotFound)
{
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(range.location, word.length)];
range = NSMakeRange(range.location + range.length, string.length - (range.location + range.length));
}
}
}
else{
NSRange range = NSMakeRange(0,string.length);
while(range.location != NSNotFound)
{
range = [[string string] rangeOfString:word options:0 range:range];
if(range.location != NSNotFound)
{
[string addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:range];
range = NSMakeRange(range.location + range.length, string.length - (range.location + range.length));
}
}
}
}
[string addAttribute:NSFontAttributeName
value:[UIFont fontWithName:#"HelveticaNeue-Light" size:20.0]
range:NSMakeRange(0, [textView.text length])];
[textView setAttributedText:string];
}
Related
I am using TTTAttributedLabel and set text like "text with #tag and www.example.com".
My need is set redColor for "www.example.com" and greenColor for "#tag".
But it set blue color.
Following is my code:
[label setText:#"text with #tag and www.example.com" afterInheritingLabelAttributesAndConfiguringWithBlock:^ NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
NSRange rangeUrl = [[mutableAttributedString string] rangeOfString:#"www.example.com" options:NSCaseInsensitiveSearch];
NSRange rangeTag = [[mutableAttributedString string] rangeOfString:#"#tag" options:NSCaseInsensitiveSearch];
UIFont *boldSystemFont = [UIFont systemFontOfSize:IS_IPAD?16:14 weight:UIFontWeightRegular];
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
if (font) {
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:rangeUrl];
[mutableAttributedString addAttribute:(NSString *)kCTForegroundColorAttributeName value:[UIColor redColor] range:rangeUrl];
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:rangetag];
[mutableAttributedString addAttribute:(NSString *)kCTForegroundColorAttributeName value:[UIColor greenColor] range: rangeTag];
[mutableAttributedString addAttribute:(NSString *)kCTUnderlineColorAttributeName value:[UIColor clearColor] range: rangeTag];
CFRelease(font);
}
return mutableAttributedString;
}];
How to solve this problem.
Please help me!
You can use this way to change text color of NSMutableAttributedString
NSMutableAttributedString *attributedstring = [[NSMutableAttributedString alloc] initWithString:#"text with #tag and www.example.com"];
attributedstring = [self updateString:attributedstring withChangeColorForText:#"#tag" withColor:[UIColor redColor]];
attributedstring = [self updateString:attributedstring withChangeColorForText:#"www.example.com" withColor:[UIColor greenColor]];
label.attributedText = attributedstring;
updateString Method:
- (NSMutableAttributedString *)updateString:(NSMutableAttributedString *)mainAttributedString withChangeColorForText:(NSString*)searchText withColor:(UIColor*) color
{
NSRange range = [mainAttributedString.string rangeOfString:searchText options:NSCaseInsensitiveSearch];
if (range.location != NSNotFound) {
[mainAttributedString addAttribute:NSForegroundColorAttributeName value:color range:range];
}
return mainAttributedString;
}
If the line for which you want to add the attribute is at the beginning of the line, then the attribute is applied to the entire line. And if it is in another place, it works correctly.
Code:
- (void)applyStyleLinkWithRange:(NSRange)range andAttributes:(NSDictionary *)attributes {
if (NSLocationInRange(range.location + range.length, NSMakeRange(0, [self.attributedString length]))) {
NSMutableAttributedString *mutableAttributedString = [self.attributedString mutableCopy];
for (NSString *key in _activeLinkAttributes) {
[mutableAttributedString removeAttribute:key range:range];
}
for (NSString *key in _inactiveLinkAttributes) {
[mutableAttributedString removeAttribute:key range:range];
}
[mutableAttributedString addAttributes:attributes range: range];
self.attributedString = [mutableAttributedString copy];
mutableAttributedString = nil;
}}
Number is at the beginning of the line:
Number is at the other place:
How i can fix this?
I want to set gray color for alphabets in uilabel. how to set range value for that in NSMutableAttributedString.
Example :
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:#"1.2 Sun - 3.4 Mon"];
[attrString beginEditing];
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range: ]; // how to set range here for alphabets
[attrString endEditing];
Try this.
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:#"1.2 Sun - 3.4 Mon"];
if([[attrString string] rangeOfString:#"Sun"].location != NSNotFound)
{
NSRange range2 = [[attrString string] rangeOfString:#"Sun"];
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:range2];
}
if([[attrString string] rangeOfString:#"Mon"].location != NSNotFound)
{
NSRange range2 = [[attrString string] rangeOfString:#"Mon"];
[attrString addAttribute:NSForegroundColorAttributeName value:[UIColor grayColor] range:range2];
}
Happy coding :)
Particular characters are to be highlighted in red color on the label so I wrote below function which works well, but I want to confirm, is there any other efficient way of doing this ? e.g.
-(NSMutableAttributedString*)getAttributeText:(NSString*)string forSubstring:(NSString*)searchstring {
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:_lblName.text];
NSRange searchRange = NSMakeRange(0,string.length);
for (NSInteger charIdx=0; charIdx<searchstring.length; charIdx++){
NSString *substring = [searchstring substringWithRange:NSMakeRange(charIdx, 1)];
NSRange foundRange;
searchRange.location = 0;
while (searchRange.location < string.length) {
searchRange.length = string.length-searchRange.location;
foundRange = [string rangeOfString:substring options:1 range:searchRange];
[text addAttribute: NSForegroundColorAttributeName value: [UIColor redColor] range:foundRange];
if (foundRange.location != NSNotFound) {
searchRange.location = foundRange.location+foundRange.length;
} else {
// no more substring to find
break;
}
}
}
return text;
}
Below is the code how I use it, and result as well
NSString *string = #"James Bond Always Rocks";
_lblName.text = string;
_lblAttributedName.attributedText = [self getAttributeText:string forSubstring:#"ao"];
Update
NSString *string = #"James Bond Always Rocks";
NSRange range = [string rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"J"] options:NSCaseInsensitiveSearch];
NSLog(#"range->%#",NSStringFromRange(range)); //This prints range->{0, 1}
NSString *string = #"James Bond Always Rocks";
NSRange range = [string rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:#"j"] options:NSCaseInsensitiveSearch];
NSLog(#"range->%#",NSStringFromRange(range)); //This prints range->{2147483647, 0}
You can simplify it by searching for a pattern ("[ao]+" in your example) to eliminate
the outer loop:
NSString *string = #"James Bond Always Rocks";
NSString *searchstring = #"ao";
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:string];
// A regular expression pattern that matches a sequence of the characters in "searchString":
NSString *pattern = [NSString stringWithFormat:#"[%#]+", [NSRegularExpression escapedPatternForString:searchstring]];
NSRange foundRange = [string rangeOfString:pattern options:NSRegularExpressionSearch|NSCaseInsensitiveSearch];
while (foundRange.location != NSNotFound) {
[text addAttribute: NSForegroundColorAttributeName value: [UIColor redColor] range:foundRange];
NSRange nextRange = NSMakeRange(foundRange.location + foundRange.length, string.length - foundRange.location - foundRange.length);
foundRange = [string rangeOfString:pattern options:NSRegularExpressionSearch|NSCaseInsensitiveSearch range:nextRange];
}
Here is my version, this is very basic approach using simple loops.
Why I posted it because I tracked the efficiency and please see the time taken by each of the implementations.
2014-03-14 15:48:42.792 TimeEfficiency[1166:303] My: 0.000073 seconds
2014-03-14 15:48:45.319 TimeEfficiency[1166:303] martin: 0.000278 seconds
2014-03-14 15:48:48.263 TimeEfficiency[1166:303] avt: 0.000029 seconds
2014-03-14 15:48:51.152 TimeEfficiency[1166:303] janak: 0.000092 seconds
Hence Avt's is best in time-performance.
NSString *string = #"James Bond Always Rocks";
NSString *searchstring = #"ao";
NSMutableArray *characters = [NSMutableArray new];
for (NSInteger i=0; i<searchstring.length; i++) {
[characters addObject:[searchstring substringWithRange:NSMakeRange(i, 1)]]; //ao
}
//store all the location of each of the char
NSMutableArray *locations = [NSMutableArray new];
for (NSInteger i=0; i<string.length; i++) {
if ([characters containsObject: [string substringWithRange:NSMakeRange(i, 1)]] ){
[locations addObject:#(i)];
}
}
//loop for string and for each location change the color
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]initWithString:string];
for (NSInteger i=0; i<locations.count; i++) {
NSRange range=NSMakeRange([locations[i] intValue], 1);
[text addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:range];
}
My variant with NSCharacterSet
- (NSMutableAttributedString*)getAttributeText:(NSString*)string forSubstring:(NSString*)searchstring {
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:string];
NSRange searchRange = NSMakeRange(0,string.length);
NSString *allCaseString = [[searchstring uppercaseString] stringByAppendingString:[searchstring lowercaseString]];
NSCharacterSet *chSet = [NSCharacterSet characterSetWithCharactersInString:allCaseString];
NSRange foundRange;
while (foundRange = [string rangeOfCharacterFromSet:chSet options:NSCaseInsensitiveSearch range:searchRange],
foundRange.location != NSNotFound) {
[text addAttribute: NSForegroundColorAttributeName value: [UIColor redColor] range:foundRange];
NSUInteger newStart = foundRange.location + foundRange.length;
searchRange = (NSRange){newStart, string.length - newStart};
}
return text;
}
I have two NSStrings: orgText and searchLetter.
I want to highlight every occurrences of the searchLetter in the orgText with a red color.
How can I get the NSRange of all occurrences of the searchLetter ?
for eg :
suppose: orgText = "abcahaiapaoiuiapplma"
searchLetter = "a".
I want to hightlight all "a" occurrences in "abcahaiapaoiuiapplma" with red color.
Thanks.
I wrote this method for my project - SUITextView with highlight:
- (NSMutableAttributedString*) setColor:(UIColor*)color word:(NSString*)word inText:(NSMutableAttributedString*)mutableAttributedString {
NSUInteger count = 0, length = [mutableAttributedString length];
NSRange range = NSMakeRange(0, length);
while(range.location != NSNotFound)
{
range = [[mutableAttributedString string] rangeOfString:word options:0 range:range];
if(range.location != NSNotFound) {
[mutableAttributedString setTextColor:color range:NSMakeRange(range.location, [word length])];
range = NSMakeRange(range.location + range.length, length - (range.location + range.length));
count++;
}
}
return mutableAttributedString;
}
And in my category of NSMutableAttributedString:
- (void) setTextColor:(UIColor*)color range:(NSRange)range {
// kCTForegroundColorAttributeName
[self removeAttribute:(NSString*)kCTForegroundColorAttributeName range:range]; // Work around for Apple leak
[self addAttribute:(NSString*)kCTForegroundColorAttributeName value:(id)color.CGColor range:range];
}
I'm not seeing any solution with regular expression, so I've created an elegant one, it may be useful for someone in the future.
- (BOOL)highlightString:(NSString *)string inText:(NSMutableAttributedString *)attributedString withColour:(UIColor *)color {
NSError *_error;
NSRegularExpression *_regexp = [NSRegularExpression regularExpressionWithPattern:string options:NSRegularExpressionCaseInsensitive error:&_error];
if (_error == nil) {
[_regexp enumerateMatchesInString:attributedString.string options:NSMatchingReportProgress range:NSMakeRange(0, attributedString.string.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
if (result.numberOfRanges > 0) {
for (int i = 0; i < result.numberOfRanges; i++) {
[attributedString addAttribute:NSBackgroundColorAttributeName value:color range:[result rangeAtIndex:i]];
}
}
}];
return TRUE;
} else {
return FALSE;
}
}
Code crash at "setTextColor" for MutableAttributeString
instead of it use below code
NSDictionary *tempdict=[NSDictionary dictionaryWithObjectsAndKeys:[UIFont boldSystemFontOfSize:12.0],NSFontAttributeName,color,NSForegroundColorAttributeName, nil];
[mutableAttributedString setAttributes:tempdict range:NSMakeRange(range.location, [word length])];
this is an easier way of doing it
NSString *str = #"hello world";
NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithString:str];
[attr addAttributes:#{NSForegroundColorAttributeName : [UIColor redColor]}
range:[str rangeOfString:#"world"]];