Unable get range from NSString for NSURL absoluteString - ios

I'm developing the chat application, where the text entered has to be detected if its URL. If so, change its color and underline it. Please have a look at below screenshot :
To change the color of the url I've used following code:
NSString *urlString = [[detetctedURL absoluteString] stringByRemovingPercentEncoding];
/* *detetctedURL is detected url from entered text using NSDataDetector
/* *for messageText http://stackoverflow.com/somefolder/any-[thing]-etc, the detectedURL is http://stackoverflow.com/somefolder/any-%5Bthing%5B-etc */
NSRange r = [messageText rangeOfString:urlString];
if (r.location != NSNotFound)
{
//colorFromHex 4285f4
[atext addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:66.0/255.0 green:133.0/255.0 blue:244.0/255.0 alpha:1.0] range:r];
[atext addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:r];
//set attributed text (atext) to UILabel
}
How can I format detected URLs correctly if the messageText contains the URL with both special character and percent encoding both, or only percent encoding ?
Thanks!
Update :
With the help of following code, I was able to get the required range. However, its working almost fine for most of the links, but not all like in case if there is charcters such as ']-('.
NSString *urlString = [url absoluteString];
NSRange r = [messageText rangeOfString:urlString];
BOOL foundRange = YES;
if (r.location == NSNotFound)
{
foundRange = NO;
//for umlauts or special characters
urlString = [[url absoluteString] stringByRemovingPercentEncoding];
r = [messageText rangeOfString:urlString];
if (r.location == NSNotFound)
{
//for white space in url
urlString = [urlString stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
r = [messageText rangeOfString:urlString];
if (r.location == NSNotFound)
{
urlString = [url absoluteString];
NSString *prefix = url.scheme;
if(prefix)
{
prefix = [prefix stringByAppendingString:#"://"];
urlString = [urlString stringByReplacingOccurrencesOfString:prefix withString:#""];
r = [messageText rangeOfString:urlString];
if (r.location == NSNotFound)
{
urlString = [urlString stringByRemovingPercentEncoding];
r = [messageText rangeOfString:urlString];
if (r.location == NSNotFound)
{
urlString = [urlString stringByReplacingOccurrencesOfString:#" " withString:#"%20"];
r = [messageText rangeOfString:urlString];
if (r.location != NSNotFound){ foundRange = YES; }
}else{ foundRange = YES; }
}else{ foundRange = YES; }
}
}else{ foundRange = YES; }
}else{ foundRange = YES; }
}
if (foundRange)
{
//colorFromHex 4285f4
[atext addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:66.0/255.0 green:133.0/255.0 blue:244.0/255.0 alpha:1.0] range:r];
[atext addAttribute:NSUnderlineStyleAttributeName value:#(NSUnderlineStyleSingle) range:r];
myLabel.attributedText = atext;
}

Use NSDataDetector to find and highlight url.
NSDataDetector* detector = [NSDataDetector dataDetectorWithTypes: NSTextCheckingTypeLink
error: nil];
NSArray* matches = [detector matchesInString: source
options: 0
range: NSMakeRange(0, [source length])];
You can use this to find phone numbers, address etc.

Set .dataDetectorTypes on your text view instead of trying to change the color yourself. The UITextView will take care of highlighting itself.

Related

How to delete that rows, when i get text from URL?

i try to get some text from an URL and put it out in a UITextView.
Thats my code in the ViewController:
NSString *urlString = #"http:/...";
NSError *error = nil;
NSData *dataURL = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString] options:kNilOptions error:&error];
if (error)
NSLog(#"%s: dataWithContentsOfURL error: %#", __FUNCTION__, error);
else
{
NSString *result = [[NSString alloc] initWithData:dataURL encoding:NSUTF8StringEncoding];
NSLog(#"%s: result = %#", __FUNCTION__, result);
// if you were updating a label with an `IBOutlet` called `resultLabel`, you'd do something like:
self.mytextview.text = result;
}
Now the simulator shows that text in my view:
Sorry, the text is german :)
Now how can I hide or delete that html, head, body etc.?
It displays like this because you are downloading the content of that PHP file which is in HTML format.
You can create a method to find & delete each HTML tag between "<" and ">"
- (NSString*)stringByRemovingHTMLtags:(NSString*)string {
NSRange range;
while ((range = [string rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
string = [string stringByReplacingCharactersInRange:range withString:#""];
return string;
}
Then set your text in the UITextView like this:
self.mytextview.text = [self stringByRemovingHTMLtags:result];
EDIT: In order to get the content of the <p> tag use this method:
- (NSString*)getPtagContentFromString:(NSString*)htmlString {
NSRange startRange = [htmlString rangeOfString:#"<p>"];
if (startRange.location != NSNotFound) {
NSRange targetRange;
targetRange.location = startRange.location + startRange.length;
targetRange.length = [htmlString length] - targetRange.location;
NSRange endRange = [htmlString rangeOfString:#"</p>" options:0 range:targetRange];
if (endRange.location != NSNotFound) {
targetRange.length = endRange.location - targetRange.location;
return [htmlString substringWithRange:targetRange];
}
}
return nil;
}
Then set your text in the UITextView like this:
self.mytextview.text = [self getPtagContentFromString:result];
You should use UIWebView and load your html text like this:
[_webView loadHTMLString:result baseURL:nil];

how to remove HTML Tags from NSString in iphone?

I am working Calendar functionality for my app requirement.IF i click today date or tomorrow date or some other date need to display auspicious details in UITextview.I have been trying to format a string in my text view but I cant work it out. Im very new to xcode.I want to remove HTML Tags in my stringResonse and display in UITextview.
I am writing like this in my code:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[UIApplication sharedApplication].networkActivityIndicatorVisible=NO;
if(connection==urlConnection)
{
NSString *strResponse=[[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
NSLog(#"String Response is : %#",strResponse);
NSMutableString *mutString=[NSMutableString string];
NSString *s=nil;
NSString *s1=nil;
//NSArray *arr1=[strResponse componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<br>"]];
NSArray *arr2=[strResponse componentsSeparatedByString:#"\n"];
NSLog(#"array %#",arr2);
for(s in arr2)
{
s = [s stringByReplacingOccurrencesOfString: #"<br>" withString: #"\n"];
s1=[s stringByReplacingOccurrencesOfString:#"<font color>" withString:#" "];
[mutString appendString:#""];
[mutString appendString:s1];
}
text1.text=[text1.text stringByAppendingString:mutString];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
strZone=[[NSString alloc]init];
dict=[[NSDictionary alloc]init];
text1=[[UITextView alloc]initWithFrame:CGRectMake(10, 280, 300, 120)];
text1.font=[UIFont fontWithName:#"Helvetica" size:12];
text1.font=[UIFont boldSystemFontOfSize:12];
text1.backgroundColor=[UIColor whiteColor];
text1.editable=NO;
[self.view addSubview:text1];
}
This is my string Response.
S.Panchami 01.38<br>Arudra 02.01<br>V.08.54-10.39<br>D.05.02-06.52<br> <font color=red><u>Festival</u></font><br><font color=blue>Shankara Jayanthi<br></font>
But i want to display like this if user clicks date in calendar
S.Panchami 01.38
Arudra 02.01
V.08.54-10.39
D.05.02-06.52
Festival
Shankara Jayanthi
A simple solution with iOS 7:
NSString *html = #"S.Panchami 01.38<br>Arudra 02.01<br>V.08.54-10.39<br>D.05.02-06.52<br> <font color=red><u>Festival</u></font><br><font color=blue>Shankara Jayanthi<br></font>";
NSAttributedString *attr = [[NSAttributedString alloc] initWithData:[html dataUsingEncoding:NSUTF8StringEncoding]
options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute:#(NSUTF8StringEncoding)}
documentAttributes:nil
error:nil];
NSLog(#"html: %#", html);
NSLog(#"attr: %#", attr);
NSLog(#"string: %#", [attr string]);
NSString *finalString = [attr string];
If your working with ios 7 then you can apply this code
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:#{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]} documentAttributes:nil error:nil];
for ios lower than ios 7 use this code,
(NSString *) stringByStrippingHTML {
NSRange r;
NSString *s = [[self copy] autorelease];
while ((r = [s rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
s = [s stringByReplacingCharactersInRange:r withString:#""];
return s;
}
This should remove all content between < >
NSString * yourString = #"<br>Arudra 02.01<br>V.08.54-10.39<br>D.05.02-06.52<br> <font color=red><u>Festival</u></font><br><font color=blue>Shankara Jayanthi<br></font>";
NSRange r;
NSMutableString * cleanString = [NSMutableString stringWithString:yourString];
while ((r = [cleanString rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
cleanString = [cleanString stringByReplacingCharactersInRange:r withString:#""];
An option to regular expression is to use NSScanner:
- (NSString *)removeHTML:(NSString *)str
{
NSMutableString *pureStr = [NSMutableString stringWithCapacity:[str length]];
NSScanner *scanner = [NSScanner scannerWithString:str];
scanner.charactersToBeSkipped = NULL;
NSString *tmp = nil;
while (![scanner isAtEnd])
{
[scanner scanUpToString:#"<" intoString:&tmp];
if (tmp != nil)
[html appendString:tmp];
[scanner scanUpToString:#">" intoString:NULL];
if (![scanner isAtEnd])
[scanner setScanLocation:[scanner scanLocation] + 1];
tmp = nil;
}
return pureStr;
}
I'd suggest you use a UIWebView. If you use a UITextView, first you will have to strip the HTML tags from it and then have to insert newline character \n for formatting purposes. It would be much easier to wrap your string response with simple HTML format such as <html><head/><body><your string response><body/></html> and display it in a UIWebView.

Highlight particular characters in the string for all the characters of given string

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;
}

Highlight or Underline Specific Word in a String

I would like to highlight or underline a specific set of words in a NSString. I am able to detect if the words exist, I'm just not able to get them highlighted.
NSString * wordString = [NSString stringWithFormat:#"%#", [self.myArray componentsJoinedByString:#"\n"]];
self.myLabel.text = wordString;
if ([wordString rangeOfString:#"Base Mix"].location == NSNotFound)
{
NSLog(#"string does not contain base mix");
}
else
{
NSLog(#"string contains base mix!");
NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:wordString];
NSString * editedString = [NSString stringWithFormat:#"%lu", (unsigned long)[wordString rangeOfString:#"Base Mix"].location];
NSRange theRange = NSMakeRange(0, [editedString length]);
[string beginEditing];
[string removeAttribute:NSForegroundColorAttributeName range:theRange];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:theRange];
[string endEditing];
[self.myLabel setAttributedText:string];
}
This code is closer to the right path. I do see a highlighted character, but it's the very first character in the string and not the words that I have searched for.
You can use the NSUnderlineStyleAttributeName and NSUnderlineColorAttributeName attributes. I think you can use it like this:
NSRange foundRange = [wordString rangeOfString:#"Base Mix"];
if (foundRange.location != NSNotFound)
{
[wordString beginEditing];
[wordString addAttribute:NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:1] range:foundRange];
[wordString addAttribute:NSUnderlineColorAttributeName value:[NSColor redColor] range:foundRange];
[wordString endEditing];
}
You can use below code which relate to NSAttributed string. works only ios6+
NSString *tem = #"String with base Mix dfsdfsd ";
NSString *substring = #"base Mix";
NSRange range;
if ((range =[tem rangeOfString:substring]).location == NSNotFound)
{
NSLog(#"string does not contain base mix");
}
else
{
NSMutableAttributedString *temString=[[NSMutableAttributedString alloc]initWithString:tem];
[temString addAttribute:NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:1]
range:(NSRange){range.location,substring.length}];
NSLog(#"%#",temString);
self.yourLabel.attributedText = temString;
}
I think the part is missing from NSAttributedString. You can try with Three20
You could create an category method of NSString class like this
-(NSMutableAttributedString*)addAttributesToWords:(NSString*)string attributes:(NSArray*)attributes {
NSRange range;
if ((range = [self rangeOfString:string]).location == NSNotFound){
return [[NSMutableAttributedString alloc] initWithString:self];
}
else{
NSMutableAttributedString *result = [[NSMutableAttributedString alloc] initWithString:self];
for (NSDictionary *attribute in attributes) {
[result addAttributes:attribute range:range];
}
return result;
}
}
How to use it::
NSArray *attributes = #[#{NSFontAttributeName : kFont_OpenSansSemiBold14},
#{NSForegroundColorAttributeName : [UIColor darkGrayColor]}];
self.label.attributedText = [#"Hello There, this is a Test" addAttributesToWords:#"Hello There" attributes:attributes];
With the help of #user1118321's answer, I wrote this function and I would hope it saves someone's time.
func highlightedString(allText:String,toBeHighlighted word:String) -> NSAttributedString{
var putInString = NSMutableAttributedString.init()
var giveFrom = NSMutableString.init(string: allText)
while true {
let range = giveFrom.range(of: word)
let index = range.location + range.length
if range.location == NSNotFound{
putInString.append(NSAttributedString.init(string: giveFrom as String))
break
}
else{
let slite = giveFrom.substring(to: index)
let highlightedSlite = NSMutableAttributedString.init(string: slite)
highlightedSlite.addAttributes([NSAttributedStringKey.backgroundColor : UIColor.yellow], range: range)
giveFrom = giveFrom.substring(from: index) as! NSMutableString
putInString.append(highlightedSlite)
}
}
return putInString
}
Maybe it is not very clear code. I welcome any suggested edits that would help.
highlight Many words
func higlighted(allText:String,words:[String]) ->NSAttributedString{
let allAttributedText = NSMutableAttributedString.init(string: allText)
var ranges = [NSRange]()
for word in words{
var string = allAttributedText.string as NSString
var i = 0
while true {
var range = string.range(of: word)
if range.location == NSNotFound {
break
}
i = i + range.location + word.count
string = string.substring(from: range.location + range.length) as NSString
range.location = i - word.count
print("\(range) XX \(word)" )
ranges.append(range)
}
}
for range in ranges{
allAttributedText.addAttributes([NSAttributedStringKey.backgroundColor : UIColor.yellow], range: range)
}
return allAttributedText
}

How to get all NSRange of a particular character in a NSString?

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"]];

Resources