- Release 0.1.0

This commit is contained in:
Giuseppe Nucifora 2016-01-19 14:07:54 +01:00
parent 78776f796f
commit 17cea1e099

View File

@ -55,27 +55,27 @@
} }
- (id)initWithCoder:(NSCoder *)coder { - (id)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder]; self = [super initWithCoder:coder];
if (self) { if (self) {
[self setupLabel]; [self setupLabel];
[self setupTextView]; [self setupTextView];
[self setupURLRegularExpression]; [self setupURLRegularExpression];
} }
return self; return self;
} }
- (void)setupTextView { - (void)setupTextView {
_textStorage = [NSTextStorage new]; _textStorage = [NSTextStorage new];
_layoutManager = [NSLayoutManager new]; _layoutManager = [NSLayoutManager new];
_textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)]; _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)];
[_layoutManager addTextContainer:_textContainer]; [_layoutManager addTextContainer:_textContainer];
[_textStorage addLayoutManager:_layoutManager]; [_textStorage addLayoutManager:_layoutManager];
_textView = [[UITextView alloc] initWithFrame:self.bounds textContainer:_textContainer]; _textView = [[UITextView alloc] initWithFrame:self.bounds textContainer:_textContainer];
_textView.delegate = self; _textView.delegate = self;
_textView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; _textView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
@ -95,7 +95,7 @@
} }
- (void)setupURLRegularExpression { - (void)setupURLRegularExpression {
NSError *regexError = nil; NSError *regexError = nil;
self.urlRegex = [NSRegularExpression regularExpressionWithPattern:STURLRegex options:0 error:&regexError]; self.urlRegex = [NSRegularExpression regularExpressionWithPattern:STURLRegex options:0 error:&regexError];
} }
@ -125,14 +125,14 @@
@try { @try {
[_textStorage removeAttribute:NSBackgroundColorAttributeName range:_selectableRange]; [_textStorage removeAttribute:NSBackgroundColorAttributeName range:_selectableRange];
} @catch (NSException *exception) { } @catch (NSException *exception) {
NSLogDebug(@"%@", exception); NSLog(@"%@", exception);
} }
} }
#pragma mark - Setup #pragma mark - Setup
- (void)setupLabel { - (void)setupLabel {
// Set the basic properties // Set the basic properties
[self setBackgroundColor:[UIColor clearColor]]; [self setBackgroundColor:[UIColor clearColor]];
[self setClipsToBounds:NO]; [self setClipsToBounds:NO];
@ -157,32 +157,32 @@
// Need a text // Need a text
if (_cleanText == nil) if (_cleanText == nil)
return; return;
NSMutableString *tmpText = [[NSMutableString alloc] initWithString:_cleanText]; NSMutableString *tmpText = [[NSMutableString alloc] initWithString:_cleanText];
// Support RTL // Support RTL
if (!_leftToRight) { if (!_leftToRight) {
tmpText = [[NSMutableString alloc] init]; tmpText = [[NSMutableString alloc] init];
[tmpText appendString:@"\u200F"]; [tmpText appendString:@"\u200F"];
[tmpText appendString:_cleanText]; [tmpText appendString:_cleanText];
} }
// Define a character set for hot characters (@ handle, # hashtag) // Define a character set for hot characters (@ handle, # hashtag)
NSString *hotCharacters = @"@#"; NSString *hotCharacters = @"@#";
NSCharacterSet *hotCharactersSet = [NSCharacterSet characterSetWithCharactersInString:hotCharacters]; NSCharacterSet *hotCharactersSet = [NSCharacterSet characterSetWithCharactersInString:hotCharacters];
// Define a character set for the complete world (determine the end of the hot word) // Define a character set for the complete world (determine the end of the hot word)
NSMutableCharacterSet *validCharactersSet = [NSMutableCharacterSet alphanumericCharacterSet]; NSMutableCharacterSet *validCharactersSet = [NSMutableCharacterSet alphanumericCharacterSet];
[validCharactersSet removeCharactersInString:@"!@#$%^&*()-={[]}|;:',<>.?/"]; [validCharactersSet removeCharactersInString:@"!@#$%^&*()-={[]}|;:',<>.?/"];
[validCharactersSet addCharactersInString:@"_"]; [validCharactersSet addCharactersInString:@"_"];
_rangesOfHotWords = [[NSMutableArray alloc] init]; _rangesOfHotWords = [[NSMutableArray alloc] init];
while ([tmpText rangeOfCharacterFromSet:hotCharactersSet].location < tmpText.length) { while ([tmpText rangeOfCharacterFromSet:hotCharactersSet].location < tmpText.length) {
NSRange range = [tmpText rangeOfCharacterFromSet:hotCharactersSet]; NSRange range = [tmpText rangeOfCharacterFromSet:hotCharactersSet];
STTweetHotWord hotWord; STTweetHotWord hotWord;
switch ([tmpText characterAtIndex:range.location]) { switch ([tmpText characterAtIndex:range.location]) {
case '@': case '@':
hotWord = STTweetHandle; hotWord = STTweetHandle;
@ -194,36 +194,36 @@
hotWord = STTweetText; hotWord = STTweetText;
break; break;
} }
[tmpText replaceCharactersInRange:range withString:@"%"]; [tmpText replaceCharactersInRange:range withString:@"%"];
// If the hot character is not preceded by a alphanumeric characater, ie email (sebastien@world.com) // If the hot character is not preceded by a alphanumeric characater, ie email (sebastien@world.com)
if (range.location > 0 && [validCharactersSet characterIsMember:[tmpText characterAtIndex:range.location - 1]]) if (range.location > 0 && [validCharactersSet characterIsMember:[tmpText characterAtIndex:range.location - 1]])
continue; continue;
// Determine the length of the hot word // Determine the length of the hot word
int length = (int)range.length; int length = (int)range.length;
while (range.location + length < tmpText.length) { while (range.location + length < tmpText.length) {
BOOL charIsMember = [validCharactersSet characterIsMember:[tmpText characterAtIndex:range.location + length]]; BOOL charIsMember = [validCharactersSet characterIsMember:[tmpText characterAtIndex:range.location + length]];
if (charIsMember) if (charIsMember)
length++; length++;
else else
break; break;
} }
// Register the hot word and its range // Register the hot word and its range
if (length > 1) if (length > 1)
[_rangesOfHotWords addObject:@{@"hotWord": @(hotWord), @"range": [NSValue valueWithRange:NSMakeRange(range.location, length)]}]; [_rangesOfHotWords addObject:@{@"hotWord": @(hotWord), @"range": [NSValue valueWithRange:NSMakeRange(range.location, length)]}];
} }
[self determineLinks]; [self determineLinks];
[self updateText]; [self updateText];
} }
- (void)determineLinks { - (void)determineLinks {
NSMutableString *tmpText = [[NSMutableString alloc] initWithString:_cleanText]; NSMutableString *tmpText = [[NSMutableString alloc] initWithString:_cleanText];
[self.urlRegex enumerateMatchesInString:tmpText options:0 range:NSMakeRange(0, tmpText.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { [self.urlRegex enumerateMatchesInString:tmpText options:0 range:NSMakeRange(0, tmpText.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
NSString *protocol = @"http"; NSString *protocol = @"http";
NSString *link = [tmpText substringWithRange:result.range]; NSString *link = [tmpText substringWithRange:result.range];
@ -231,29 +231,29 @@
if (protocolRange.location != NSNotFound) { if (protocolRange.location != NSNotFound) {
protocol = [link substringToIndex:protocolRange.location]; protocol = [link substringToIndex:protocolRange.location];
} }
if ([_validProtocols containsObject:protocol.lowercaseString]) { if ([_validProtocols containsObject:protocol.lowercaseString]) {
[_rangesOfHotWords addObject:@{ @"hotWord" : @(STTweetLink), [_rangesOfHotWords addObject:@{ @"hotWord" : @(STTweetLink),
@"protocol" : protocol, @"protocol" : protocol,
@"range" : [NSValue valueWithRange:result.range] @"range" : [NSValue valueWithRange:result.range]
}]; }];
} }
}]; }];
} }
- (void)updateText { - (void)updateText {
[_textStorage beginEditing]; [_textStorage beginEditing];
NSAttributedString *attributedString = _cleanAttributedText ?: [[NSMutableAttributedString alloc] initWithString:_cleanText]; NSAttributedString *attributedString = _cleanAttributedText ?: [[NSMutableAttributedString alloc] initWithString:_cleanText];
[_textStorage setAttributedString:attributedString]; [_textStorage setAttributedString:attributedString];
[_textStorage setAttributes:_attributesText range:NSMakeRange(0, attributedString.length)]; [_textStorage setAttributes:_attributesText range:NSMakeRange(0, attributedString.length)];
for (NSDictionary *dictionary in _rangesOfHotWords) { for (NSDictionary *dictionary in _rangesOfHotWords) {
NSRange range = [dictionary[@"range"] rangeValue]; NSRange range = [dictionary[@"range"] rangeValue];
STTweetHotWord hotWord = (STTweetHotWord)[dictionary[@"hotWord"] intValue]; STTweetHotWord hotWord = (STTweetHotWord)[dictionary[@"hotWord"] intValue];
[_textStorage setAttributes:[self attributesForHotWord:hotWord] range:range]; [_textStorage setAttributes:[self attributesForHotWord:hotWord] range:range];
} }
[_textStorage endEditing]; [_textStorage endEditing];
} }
@ -262,7 +262,7 @@
- (CGSize)suggestedFrameSizeToFitEntireStringConstrainedToWidth:(CGFloat)width { - (CGSize)suggestedFrameSizeToFitEntireStringConstrainedToWidth:(CGFloat)width {
if (_cleanText == nil) if (_cleanText == nil)
return CGSizeZero; return CGSizeZero;
return [_textView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)]; return [_textView sizeThatFits:CGSizeMake(width, CGFLOAT_MAX)];
} }
@ -309,7 +309,7 @@
copy[NSForegroundColorAttributeName] = self.textColor; copy[NSForegroundColorAttributeName] = self.textColor;
attributes = [NSDictionary dictionaryWithDictionary:copy]; attributes = [NSDictionary dictionaryWithDictionary:copy];
} }
_attributesText = attributes; _attributesText = attributes;
[self determineHotWords]; [self determineHotWords];
@ -350,7 +350,7 @@
- (void)setLeftToRight:(BOOL)leftToRight { - (void)setLeftToRight:(BOOL)leftToRight {
_leftToRight = leftToRight; _leftToRight = leftToRight;
[self determineHotWords]; [self determineHotWords];
} }
@ -388,10 +388,10 @@
switch (hotWord) { switch (hotWord) {
case STTweetHandle: case STTweetHandle:
return _attributesHandle; return _attributesHandle;
case STTweetHashtag: case STTweetHashtag:
return _attributesHashtag; return _attributesHashtag;
case STTweetLink: case STTweetLink:
return _attributesLink; return _attributesLink;
case STTweetText: case STTweetText:
@ -439,7 +439,7 @@
if (![_textView isFirstResponder]) { if (![_textView isFirstResponder]) {
[_textView becomeFirstResponder]; [_textView becomeFirstResponder];
} }
} }
- (NSInteger)charIndexAtLocation:(CGPoint)touchLocation { - (NSInteger)charIndexAtLocation:(CGPoint)touchLocation {
@ -454,17 +454,17 @@
- (id)getTouchedHotword:(NSSet *)touches { - (id)getTouchedHotword:(NSSet *)touches {
NSInteger charIndex = [self charIndexAtLocation:[[touches anyObject] locationInView:_textView]]; NSInteger charIndex = [self charIndexAtLocation:[[touches anyObject] locationInView:_textView]];
if (charIndex != NSNotFound) { if (charIndex != NSNotFound) {
for (id obj in _rangesOfHotWords) { for (id obj in _rangesOfHotWords) {
NSRange range = [[obj objectForKey:@"range"] rangeValue]; NSRange range = [[obj objectForKey:@"range"] rangeValue];
if (charIndex >= range.location && charIndex < range.location + range.length) { if (charIndex >= range.location && charIndex < range.location + range.length) {
return obj; return obj;
} }
} }
} }
return nil; return nil;
} }