Highlight selected character dynamically in iOS - ios

I want to highlight selected character in iOS, as you an see in iPhone iOS 7 Notes app.
When you search for particular text the search character will be highlighted in the result displayed in UITableView.
Example:
"This is my name"
h - should 've blue color
y - should 've red color
The character customisation should be dynamic. I hope I've briefed enough.
Looking for excellent response buddies!

Try having a look at TextKit
This is a nice website that shows you the basics
I hope I understood your question..

You can easily achieve this by setting an attributed string to the text label in a UITableViewCell that shows an entry of your search results.
You would need to calculate ranges of the substrings that should be highlighted. You can do it using regular expressions as I wrote in my sample code. This way you can support multiple occurrences of the substring in a string.
Then, when you have the ranges you just apply special attributes and set the cell label's attributedText property.
The code could look like this:
NSString *searchTerm = ...;
NSString *text = #"This is a sample text that is super cool";
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];
NSString *pattern = [NSString stringWithFormat:#"(%#)", searchTerm];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:pattern
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];
[attributedString addAttribute:NSForegroundColorAttributeName
value:[UIColor blueColor]
range:subStringRange];
}];
Then it's easy. You just set the UILabel's attributedText when creating a cell in tableView:cellForRowAtIndexPath: method.
This should point you to the right direction.

Use NsMutableAttributedString to highlight characters
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:display];
[str addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange([displayString length], ([details.notificationCount length]+2))];
displayLabel.attributedText = str;

Related

Detect underscore from a particular word of a UITextView's text

I want to detect "_" from a particular word of a UITextView's text.
I already tried this :
But this calculates the range of first underscore only.
NSMutableAttributedString *string = [[NSMutableAttributedString alloc]initWithString:textView.text];
NSRange range=[textView.text rangeOfString:#"_"];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor clearColor] range:range];
textView.attributedText=string;
My output is coming like this
Tadeusz_Kościuszko was a military leader who became a national hero in
Poland. A poster for the Paris premiere of Jules_Massenet's 1910 opera
Don_Quichotte.
And I want something like this:
Tadeusz Kościuszko was a military leader who became a national hero in
Poland. A poster for the Paris premiere of Jules Massenet's 1910 opera
Don Quichotte.
Also I want to clear color the underscores just from the names. Not from the other text of UITextView.
I dont want to remove it from the whole text of UITextView but just from the particular words.
Anyone having suggestions?
Thanks.
textView.text = [textView.text stringByReplacingOccurrencesOfString:#"_" withString:#" "];
NSMutableAttributedString *string = [[NSMutableAttributedString alloc]initWithString:textView.text];
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"(_)" options:kNilOptions error:nil];
NSRange range = NSMakeRange(0,string.length);
[regex enumerateMatchesInString:string options:kNilOptions range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange subStringRange = [result rangeAtIndex:1];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:subStringRange];
}];
textView.attributedText=string;
try this -
NSString *str = #"Tadeusz_Kościuszko was a military leader who became a national hero in Poland. A poster for the Paris premiere of Jules_Massenet's 1910 opera Don_Quichotte.";
NSString *newStr = [str stringByReplacingOccurrencesOfString:#"_" withString:#" "];
NSLog(#"NEW STR ::::: %#",newStr);
NSString *str = #"This_is_a_string";
str = [str stringByReplacingOccurrencesOfString:#"_"
withString:#" "];
// str = "This is a string"
More on NSString
Found the workaround :)
NSString *strTemp = textView.text;
NSMutableAttributedString *mutString = [[NSMutableAttributedString alloc]initWithString:strTemp];
NSString *substring = #"_";
NSRange searchRange = NSMakeRange(0,mutString.length);
NSRange wholeTextRange = [strTemp rangeOfString:strTemp];
[mutString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:wholeTextRange];
NSRange foundRange;
while (searchRange.location < mutString.length)
{
searchRange.length = mutString.length-searchRange.location;
foundRange = [strTemp rangeOfString:substring options:nil range:searchRange];
if (foundRange.location != NSNotFound) {
// found an occurrence of the substring! do stuff here
[mutString addAttribute:NSForegroundColorAttributeName value:[UIColor clearColor] range:foundRange];
searchRange.location = foundRange.location+foundRange.length;
} else {
// no more substring to find
[txtComment setAttributedText:mutString];
break;
}
}
If you want to selectively remove underscores you need to know the range of text from which you want to remove them.
Then you can use a regular expression to get an array of ranges which match the underscore in that known range.
NSError *error;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"_" options:0 error:&error];
NSArray *matchingRangesInSelectedRange = [regex matchesInString:yourString options:0 range:knownRangeToCheckForMatches];
You can then iterate over the ranges in the array and do whatever changes you wish.
If you know which strings should not be cleaned or which that should not be cleaned you could do something like this:
NSString *stringWithUnderScore = #"A rather long text with some_odd_underscores but also names like André_Franquin & Albert_Uderzo";
NSSet *stringsThatShouldKeepUnderscore = [NSSet setWithObjects:#"some_odd_underscores", nil];
NSArray *singleStrings = [stringWithUnderScore componentsSeparatedByString:#" "];
NSMutableString *newCompleteString = [[NSMutableString alloc] init];
for (NSString *singleString in singleStrings) {
if (![stringsThatShouldKeepUnderscore containsObject:singleString]){
NSString *fixedString = [singleString stringByReplacingOccurrencesOfString:#"_" withString:#" "];
[newCompleteString appendString: fixedString];
} else{
[newCompleteString appendString:singleString];
}
[newCompleteString appendString:#" "];
}
NSLog(#"new string:%#",newCompleteString);
Edit: If you do not have this information you could try to make some kind of parser checking if each of the strings separated by "_" have capital-letters, indicating a name. Not fail-safe, but without knowledge about the text I can't really see any better option.

UILabel Bold / Highlight All occurrences SubString

I have multiple UILabels within a Custom Table cell. These labels contain varied text or varied length.
As it stands i have UILabel Subclassed allowing me to implement these methods
- (void)boldRange:(NSRange)range {
if (![self respondsToSelector:#selector(setAttributedText:)]) {
return;
}
NSMutableAttributedString *attributedText;
if (!self.attributedText) {
attributedText = [[NSMutableAttributedString alloc] initWithString:self.text];
} else {
attributedText = [[NSMutableAttributedString alloc]initWithAttributedString:self.attributedText];
}
[attributedText setAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:self.font.pointSize]} range:range];
self.attributedText = attributedText;
NSLog(#"%#", NSStringFromRange(range));
}
- (void)boldSubstring:(NSString*)substring {
NSRange range = [self.text rangeOfString:substring];
[self boldRange:range];
}
This allows me to call [cell.StoryLabel boldSubstring:#"test"]; which will BOLD the first occurrence of the word 'test'.
What i am after is the ability to either create new subclass methods or extend the ones i already have, to allow me to replace ALL occurrences of a specified word within the label.
I have looked into a number of methods including 3rd party frameworks. The trouble i have is this is a learning process for me. I would be far more beneficial for me to try and complete this myself.
Thanks in advance!
rangeOfString returns the first occurrence, that's normal behavior.
From the Doc:
Finds and returns the range of the first occurrence of a given string
within the receiver.
You could use a NSRegularExpression, and use matchesInString:options:range to get a NSArray of NSTextCheckingResult (that have a NSRange property), an use a for loop to bold it.
This should do the trick:
- (void)boldSubstring:(NSString*)substring
{
if (![self respondsToSelector:#selector(setAttributedText:)])
{
return;
}
NSError *error;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern: substring options:NSRegularExpressionCaseInsensitive error:&error];
if (!error)
{
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:[self text]];
NSArray *allMatches = [regex matchesInString:[self text] options:0 range:NSMakeRange(0, [[self text] length])];
for (NSTextCheckingResult *aMatch in allMatches)
{
NSRange matchRange = [aMatch range];
[attributedString setAttributes:#{NSFontAttributeName:[UIFont boldSystemFontOfSize:self.font.pointSize]} range: matchRange];
}
[self setAttributedText:attributedString];
}
}

Formatting multi line text in UILabel?

I would like to have label that has 4 lines. If text is not long enough for 4 lines, nothing happens. But if text is 4 lines or longer I want it to have a little different color just for last line.
Is there easy way to do this. I know i can with attributed string change font of label, but how do i get text that is in forth line?
Use NSAttributedString to format paragraphs, lines, words or even single characters how ever you want. To get the text on the 4th line, separate your text on its \n characters.
If you don't have any \n, you can use getLineStart:end:contentsEnd:forRange:, adapted from here
NSString *string = /* assume this exists */;
unsigned length = [string length];
unsigned lineStart = 0, lineEnd = 0, contentsEnd = 0;
NSMutableArray *lines = [NSMutableArray array];
NSRange currentRange;
while (lineEnd < length) {
[string getLineStart:&lineStart end:&lineEnd
contentsEnd:&contentsEnd forRange:NSMakeRange(lineEnd, 0)];
currentRange = NSMakeRange(lineStart, contentsEnd - lineStart);
[lines addObject:[string substringWithRange:currentRange]];
}
EDIT
After rereading the question, this may not be exactly what you are after. Check out the full answer here:
In iOS 6+ you can render attributed strings using the attributedText property of UILabel.
Following a code example:
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:#"Hello. That is a test attributed string."];
[str addAttribute:NSBackgroundColorAttributeName value:[UIColor yellowColor] range:NSMakeRange(3,5)];
[str addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(10,7)];
[str addAttribute:NSFontAttributeName value:[UIFont fontWithName:#"HelveticaNeue-Bold" size:20.0] range:NSMakeRange(20, 10)];
label.attributedText = str;
As you can see in the code you can select a different color text for the different range of characters. In your case you can put the different font color for the chars in the string for the last line.
In order to check the range of chars for the last line you can use:
NSUInteger characterCount = [myString length];
And then characterCount divided for number of chars you can put in each line depending on the width of it.

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

How to get NSRange(s) for a substring in NSString? [duplicate]

This question already has answers here:
How to get all NSRange of a particular character in a NSString?
(4 answers)
Closed 9 years ago.
NSString *str = #" My name is Mike, I live in California and I work in Texas. Weather in California is nice but in Texas is too hot...";
How can I loop through this NSString and get NSRange for each occurrence of "California", I want the NSRange because I would like to change it's color in the NSAttributed string.
NSRange range = NSMakeRange(0, _stringLength);
while(range.location != NSNotFound)
{
range = [[attString string] rangeOfString: #"California" options:0 range:range];
if(range.location != NSNotFound)
{
range = NSMakeRange(range.location + range.length, _stringLength - (range.location + range.length));
[attString addAttribute:NSForegroundColorAttributeName value:_green range:range];
}
}
Lots of ways of solving this problem - NSScanner was mentioned; rangeOfString:options:range etc. For completeness' sake, I'll mention NSRegularExpression. This also works:
NSMutableAttributedString *mutableString = nil;
NSString *sampleText = #"I live in California, blah blah blah California.";
mutableString = [[NSMutableAttributedString alloc] initWithString:sampleText];
NSString *pattern = #"(California)";
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:nil];
// enumerate matches
NSRange range = NSMakeRange(0,[sampleText length]);
[expression enumerateMatchesInString:sampleText options:0 range:range usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSRange californiaRange = [result rangeAtIndex:0];
[mutableString addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:californiaRange];
}];
with
[str rangeOfString:#"California"]
and
[str rangeOfString:#"California" options:YOUR_OPTIONS range:rangeToSearch]
You may use rangeOfString:options:range: or NSScanner (there are other possibilities like regexps but anyway). It's easier to use first approach updating range, i.e. search for first occurrence and then depending on the result update the search range. When the search range is empty, you've found everything;

Resources