Skip to content

Instantly share code, notes, and snippets.

@nickhsu
Forked from brentsimmons/gist:5810992
Created November 18, 2016 08:29
Show Gist options
  • Save nickhsu/73166d4e94179ce7ec97e8c8312c41d7 to your computer and use it in GitHub Desktop.
Save nickhsu/73166d4e94179ce7ec97e8c8312c41d7 to your computer and use it in GitHub Desktop.
Detect a tap on a URL inside a UITextView.Note: the rs_links method isn't included -- you'll need something that takes text and returns an array of detected links. This gist just demonstrates walking through the UITextView characters.
#import "UITextView+RSExtras.h"
@implementation UITextView (RSExtras)
static BOOL stringCharacterIsAllowedAsPartOfLink(NSString *s) {
/*[s length] is assumed to be 0 or 1. s may be nil.
Totally not a strict check.*/
if (s == nil || [s length] < 1) {
return NO;
}
unichar ch = [s characterAtIndex:0];
return ![[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:ch];
}
- (NSString *)rs_potentialLinkAtPoint:(CGPoint)point {
/*Grow a string around the tap until hitting a space, cr, lf, or beginning or end of document.*/
/*If we don't check for end of document, then you could tap way below end of text, and it would return a link if the last text was a link. This has the unfortunate side effect that you can't tap on the last character of a link if it appears at the end of a document. I can live with shipping that.*/
UITextRange *textRange = [self characterRangeAtPoint:point];
UITextPosition *endOfDocumentTextPosition = self.endOfDocument;
if ([textRange.end isEqual:endOfDocumentTextPosition])
return nil;
UITextPosition *tapPosition = [self closestPositionToPoint:point];
if (tapPosition == nil)
return nil;
NSMutableString *s = [NSMutableString stringWithString:@""];
/*Move right*/
UITextPosition *textPosition = tapPosition;
BOOL isFirstCharacter = YES;
while (true) {
UITextRange *rangeOfCharacter =
[self.tokenizer rangeEnclosingPosition:textPosition withGranularity:UITextGranularityCharacter inDirection:UITextWritingDirectionNatural];
NSString *oneCharacter = [self textInRange:rangeOfCharacter];
if (isFirstCharacter) {
/*If first character is cr or lf, then we're off the right hand side of the link. Maybe way outside.*/
if ([oneCharacter isEqualToString:@"\n"] || [oneCharacter isEqualToString:@"\r"])
return nil;
}
isFirstCharacter = NO;
if (!stringCharacterIsAllowedAsPartOfLink(oneCharacter))
break;
[s appendString:oneCharacter];
textPosition = [self positionFromPosition:textPosition offset:1];
if (textPosition == nil)
break;
}
/*Move left*/
textPosition = [self positionFromPosition:tapPosition offset:-1];
if (textPosition != nil) {
while (true) {
UITextRange *rangeOfCharacter =
[self.tokenizer rangeEnclosingPosition:textPosition withGranularity:UITextGranularityCharacter inDirection:UITextWritingDirectionNatural];
NSString *oneCharacter = [self textInRange:rangeOfCharacter];
if (!stringCharacterIsAllowedAsPartOfLink(oneCharacter))
break;
[s insertString:oneCharacter atIndex:0];
textPosition = [self positionFromPosition:textPosition offset:-1];
if (textPosition == nil)
break;
}
}
return s;
}
- (NSArray *)rs_links:(NSString *)string {
NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
error:nil];
return [detector matchesInString:string
options:0
range:NSMakeRange(0, [string length])];
}
- (NSString *)rs_linkAtPoint:(CGPoint)point {
NSString *potentialLink = [self rs_potentialLinkAtPoint:point];
if (potentialLink == nil || [potentialLink length] < 1) {
return nil;
}
NSArray *links = [self rs_links: potentialLink];
if (links == nil || [links count] < 1) {
return nil;
} else {
return links[0];
}
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment