- Release 0.1.0
This commit is contained in:
parent
78776f796f
commit
17cea1e099
@ -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:®exError];
|
self.urlRegex = [NSRegularExpression regularExpressionWithPattern:STURLRegex options:0 error:®exError];
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user