I have a UITextView with multiple URLs that I activate by setting the dataDetectorTypes property to UIDataDetectorTypeLink. I then use the linkTextAttributes property to set the color of the links. Now when the user taps on one of the links (using a UITapGestureRecognizer), I'd like to change the color of that link only. If I change linkTextAttributes, all the links will change color.
How can I change just the color of the link that was tapped on?
If these urls are fixed.
For example:
I have the following urls:
http://www.123.com
http://www.456.com
http://www.789.com
I would put them to an NSAttributedString
Use NSMutableAttributedString to combine them all
NSMutableAttributedString *urlsAttributedText = [[NSMutableAttributedString alloc]init];
NSAttributedString *url1 = [[NSAttributedString alloc]initWithString:NSLocalizedString(#"http://www.123.com\n", nil) attributes:#{NSForegroundColorAttributeName : [UIColor whiteColor], NSFontAttributeName : [UIFont systemFontOfSize:15.0f]}];
NSAttributedString *url2 = [[NSAttributedString alloc]initWithString:NSLocalizedString(#"http://www.456.com\n", nil) attributes:#{NSForegroundColorAttributeName : [UIColor greenColor], NSFontAttributeName : [UIFont systemFontOfSize:15.0f]}];
NSAttributedString *url3 = [[NSAttributedString alloc]initWithString:NSLocalizedString(#"http://www.789.com\n", nil) attributes:#{NSForegroundColorAttributeName : [UIColor redColor], NSFontAttributeName : [UIFont systemFontOfSize:15.0f]}];
[urlsAttributedText url1];
[urlsAttributedText appendAttributedString:url2];
[urlsAttributedText appendAttributedString:url3];
self.texView.attributedText = urlsAttributedText;
Cheers!
I think I solved it, using a subclass of UITextView called which has a rangeOfLink property.
First, in my UIViewController viewDidLoad:, I add
self.textView.dataDetectorTypes = UIDataDetectorTypeLink; // change for other link types
self.textView.selectable = YES;
self.textView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget: self action: #selector(handleTap:)];
tapGesture.cancelsTouchesInView = YES;
[self.textView addGestureRecognizer: tapGesture];
[self.textView setNeedsDisplay]; // force a redraw so that drawRect is called
Then in handleTap, I do this:
MyTextViewWithLink *aTextView = (IDTextViewWithLink *) recognizer.view;
if (aTextView != self.textView)
return;
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint location = [recognizer locationInView: aTextView];
// this returns an NSTextCheckingResult if location is inside a link
NSTextCheckingResult *result = [self textCheckingResultAtPoint: location inTextView: aTextView];
if (result)
{
aTextView.rangeOfLink = result.range;
[aTextView setNeedsDisplay]; // this will force the color change
// open url
}
}
Finally I override drawRect in my UITextView subclass:
self.linkTextAttributes = [NSDictionary dictionary];
NSError *error = nil;
NSDataDetector *dataDetector = [NSDataDetector dataDetectorWithTypes: NSTextCheckingTypeLink error: &error]; // change for other link types
if (!error && dataDetector)
{
NSArray* resultString = [dataDetector matchesInString: self.text
options: NSMatchingReportProgress
range: NSMakeRange(0, [self.text length])];
if (resultString.count > 0)
{
NSMutableAttributedString *mas = [self.attributedText mutableCopy];
for (NSTextCheckingResult* result in resultString)
{
if (result.resultType == NSTextCheckingTypeLink)
{
NSRange intersection = NSIntersectionRange(result.range, self.rangeOfLink);
if (intersection.length <= 0) // no match
[mas addAttribute: NSForegroundColorAttributeName
value: [UIColor blueColor]
range: self.rangeOfLink];
else
[mas addAttribute: NSForegroundColorAttributeName
value: [UIColor redColor]
range: self.rangeOfLink];
}
}
self.attributedText = mas;
}
}
[super drawRect: rect];
Now if the textView has more than one link, only the selected one will change color.
Related
I want to display attributed text label inside a scrollview. I use the following code the label displays the text in centre and some extra white space is added in top and bottom.
NSString *STR_titl_iten_des = #"Item Description";
NSString *STR_descrip_detail = #"Long text to be displayed";
self.lbl_item_descrip.numberOfLines = 0;
NSString *text2 = [NSString stringWithFormat:#"%#\n%#",STR_titl_iten_des,STR_descrip_detail];
if ([self.lbl_item_descrip respondsToSelector:#selector(setAttributedText:)]) {
NSDictionary *attribs = #{
NSForegroundColorAttributeName: self.lbl_item_descrip.textColor,
NSFontAttributeName: self.lbl_item_descrip.font
};
NSMutableAttributedString *attributedText =
[[NSMutableAttributedString alloc] initWithString:text2
attributes:attribs];
NSRange cmp = [text2 rangeOfString:STR_titl_iten_des];
if ( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad )
{
[attributedText setAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue-Bold" size:21.0]}
range:cmp];
}
else
{
[attributedText setAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"HelveticaNeue-Bold" size:17.0]}
range:cmp];
}
self.lbl_item_descrip.attributedText = attributedText;
}
else
{
self.lbl_item_descrip.text = text;
}
[_lbl_item_descrip sizeToFit];
The below is my output
Can you remove [_lbl_item_descrip sizeToFit]; after that try once.
hi i want a textview or label with see more and less functionality. i tried with label and successfully got result as:
but if text will increase it button more will look like :
and in some conditions it will look :
i am trying to set button will automatically set according to text.can anyone suggest me better way to got this.
If you are using UILabel than you can use TTTAttributedLabel in that you can set clickable text.
Append your string "see more" or "less functionality" with label text and make it clickable.
So there is my simple code that create label with TTTAttributedLabel to implement 'more...' and 'less...' functionality for label:
- (void) setupStoreTitleLabel {
self.titleLabel.delegate = self;
[self.titleLabel setText:self.card.name];
self.titleLabel.numberOfLines = 2;
NSAttributedString *showMore = [[NSAttributedString alloc] initWithString:#" more..." attributes:#{
NSForegroundColorAttributeName:[UIColor blueColor],
NSFontAttributeName : [UIFont boldSystemFontOfSize:12],
NSLinkAttributeName : [NSURL URLWithString:#"more..."]
}];
[self.titleLabel setAttributedTruncationToken:showMore];
}
#pragma mark - TTTAttributedLabelDelegate
- (void)attributedLabel:(__unused TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url
{
NSLog(#"%# pressed", url);
if ([[url absoluteString] isEqualToString:#"more..."]) {
self.titleLabel.numberOfLines = 99;
NSString *cardNameWithLess = [NSString stringWithFormat:#"%# %#",self.card.name, #" less..."];
[self.titleLabel setText:cardNameWithLess afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
//code
NSRange linkRange = [[mutableAttributedString string] rangeOfString:#" less..." options:NSCaseInsensitiveSearch];
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:linkRange];
[mutableAttributedString addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:12] range:linkRange];
[mutableAttributedString addAttribute:NSLinkAttributeName value:[NSURL URLWithString:#"less..."] range:linkRange];
return mutableAttributedString;
}];
} else {
self.titleLabel.numberOfLines = 2;
NSAttributedString *showMore = [[NSAttributedString alloc] initWithString:#" more..." attributes:#{
NSForegroundColorAttributeName:[UIColor blueColor],
NSFontAttributeName : [UIFont boldSystemFontOfSize:12],
NSLinkAttributeName : [NSURL URLWithString:#"more..."]
}];
[self.titleLabel setAttributedTruncationToken:showMore];
[self.titleLabel setText:self.card.name];
}
}
I want my UITextView includes plain text, links, phone numbers and other UIDataDetectorTypes. For instance, plain text - black color, links - blue color with underline, hashtags - green color. So, I parse text and add links to hashtags:
[attributedString addAttribute:NSLinkAttributeName value:[NSString stringWithFormat:#"hashtag://%#", hashtag] range:range];
Other links are detected by default, if dataDetectorTypes set to UIDataDetectorTypeAll. But how to change link style for hashtags?
You can use the official Twitter library for tracking hashtags called twitter text along with TTTAttributedLabel.
Hashtags have special conditions, these could be more than alphanumeric characters preceded by #.
The twitter-text library will automatically find the ranges of hashtags for you then you should be able to format it with TTTAttributedLabel.
Here is a sample implementation of both libraries together.
- (void)scanForHashtag:(NSArray *)hashtags fromLabel:(id)sender
{
TTTAttributedLabel *label = (TTTAttributedLabel *)sender;
for (TwitterTextEntity *entity in hashtags) {
UIFont *boldSystemFont = [UIFont fontWithName:#"Helvetica-Bold" size:12];
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
NSArray *keys = [[NSArray alloc] initWithObjects:(id)kCTForegroundColorAttributeName,(id)kCTUnderlineStyleAttributeName,(NSString *)kCTFontAttributeName, nil];
UIColor *hashtagColor;
hashtagColor = [UIColor greenColor];
NSArray *objects = [[NSArray alloc] initWithObjects:hashtagColor,[NSNumber numberWithInt:kCTUnderlineStyleNone],(__bridge id)font, nil];
NSDictionary *linkAttributes = [[NSDictionary alloc] initWithObjects:objects forKeys:keys];
label.linkAttributes = linkAttributes;
NSString *hashtag = [label.text substringWithRange:entity.range];
if (entity.type == 2) { //Hashtag
[label addLinkToPhoneNumber:hashtag withRange:entity.range];
} else if (entity.type == 0) { //URL
NSURL *url = [NSURL URLWithString:[label.text substringWithRange:entity.range]];
[label addLinkToURL:url withRange:entity.range];
}
}
}
- (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber
{
//Do whatever you need when the hashtag is tapped.
}
[self scanForHashtag:[TwitterText hashtagsInText:#"The text that contains the tweet with hashtags." checkingURLOverlap:NO] fromLabel:yourLabel]; //yourLabel is the label that contains the text so it would be formatted.
EDIT FOR UITextView
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:str];
[attributedString addAttribute:NSBackgroundColorAttributeName
value:[UIColor yellowColor]
range:entity.range];
You need to find hashtags ranges and than add special attributes for them.
For example.
NSError *error = nil;
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"YOUR HASHTAG PATTERN" options:0 error:&error];
if (!error) {
[regex enumerateMatchesInString:messageText options:0 range:NSMakeRange(0, messageText.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) {
[mutableAttributedString addAttributes:#{
NSFontAttributeName: [UIFont fontLinkMessageCell],
(__bridge NSString*) kCTForegroundColorAttributeName: [UIColor colorLinkOutCell],
NSUnderlineStyleAttributeName: #(NSUnderlineStyleNone),
} range:result.range];
}];
}
self.textView.linkTextAttributes = #{};
self.textView.textStorage.delegate = self;
- (void) textStorage: (NSTextStorage*) textStorage didProcessEditing: (NSTextStorageEditActions) editedMask range: (NSRange) editedRange changeInLength: (NSInteger) delta
{
[textStorage enumerateAttribute: NSLinkAttributeName
inRange: editedRange
options: 0
usingBlock: ^(id value, NSRange range, BOOL* stop)
{
if (value)
{
[textStorage addAttribute: NSForegroundColorAttributeName
value: [UIColor redColor]
range: range];
}
}];
}
I need to change the text color on tap in UITextView. I got NSRange on selected text of text view but unable to change its color using this code.
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:10.0/255.0 green:15.0/255.0 blue:5.0/255.0 alpha:1.0] range:range1];
Is there any way to change tap color change in text view?
I hope, This code may help you :)
In my scenario i am changing selected textColor by Tapping uiButton, You can try this way.
// When text is tapped or selected by user we can change the
- (IBAction)applyBlueColor:(id)sender {
// getting textRange
NSRange textRange = [_textView selectedRange];
NSDictionary *attributeDictionary = [_textView.textStorage attributesAtIndex:textRange.location
effectiveRange:nil];
if ([attributeDictionary objectForKey:NSForegroundColorAttributeName] == nil ||
[attributeDictionary objectForKey:NSForegroundColorAttributeName] != [UIColor blackColor]) {
// Setting blue color to my selected text.
NSDictionary *colorDictionary = #{NSForegroundColorAttributeName: [UIColor blueColor]};
[_textView.textStorage beginEditing];
[_textView.textStorage setAttributes:colorDictionary range:textRange];
[_textView.textStorage endEditing];
}
}
hello every one i have post my answer. i hope it will help to other,enter code here
-(void)textTapped:(UITapGestureRecognizer *)recognizer
{
NSMutableAttributedString *attributedStringText = [[NSMutableAttributedString alloc]initWithString:txtView.text];
UITextView *textView = (UITextView *)recognizer.view;
NSLayoutManager *layoutManager = textView.layoutManager;
CGPoint location = [recognizer locationInView:textView];
location.x -= textView.textContainerInset.left;
location.y -= textView.textContainerInset.top;
// Get character Index.
NSInteger characterIndex = [layoutManager characterIndexForPoint:location inTextContainer:textView.textContainer fractionOfDistanceBetweenInsertionPoints:NULL];
if (characterIndex < textView.textStorage.length)
{
// Enumerate string and get word from character index.
[txtView.text enumerateSubstringsInRange:NSMakeRange(0, textView.textStorage.length)options:NSStringEnumerationByWords usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
if (NSLocationInRange(characterIndex, enclosingRange)) {
// Do your thing with the word, at range 'enclosingRange'
//[mutableAttributedString setTextColor:[UIColor redColor] range:NSMakeRange(range.location, [word1 length])];
// Change color of text.
//[attributedStringText addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:255.0/255.0 green:0.0/255.0 blue:0.0/255.0 alpha:1.0] range:NSMakeRange(enclosingRange.location,[word1 length])];
NSLog(#"%#",textView.tintColor);
NSLog(#"%#",);
[attributedStringText addAttribute:NSForegroundColorAttributeName value:[UIColor greenColor] range:NSMakeRange(enclosingRange.location,[word1 length])];
[textView setAttributedText:attributedStringText];
*stop = YES;
}
}];
}
}
I am using this code to change color of label and set text as strike through:
sliderlabel = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(10, 260, 310, 30)];
sliderlabel.font = [UIFont fontWithName:#"Optima-Bold" size:14];
[sliderlabel setTag:112];
sliderlabel.lineBreakMode = UILineBreakModeWordWrap;
[sliderlabel setBackgroundColor:[UIColor clearColor]];
NSString *sliderlabeltext = [NSString stringWithFormat:#"Change To: In-Progress (%d %%)",(int)slider.value];
[sliderlabel setText:sliderlabeltext afterInheritingLabelAttributesAndConfiguringWithBlock:^ NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
NSRange boldRange = [[mutableAttributedString string] rangeOfString:[NSString stringWithFormat:#"In-Progress (%d %%)",(int)slider.value] options:NSCaseInsensitiveSearch];
NSRange strikeRange = [[mutableAttributedString string] rangeOfString:sliderlabeltext options:NSCaseInsensitiveSearch];
UIFont *boldSystemFont = [UIFont fontWithName:#"Optima-Bold" size:14];
CTFontRef font = CTFontCreateWithName((CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
if (font) {
[mutableAttributedString addAttribute:(NSString *)kCTForegroundColorAttributeName value:(id)[UIColor colorWithRed:8/255.0 green:156/255.0 blue:94/255.0 alpha:1.0].CGColor range:boldRange];//34-139-34
[mutableAttributedString addAttribute:kTTTStrikeOutAttributeName value:[NSNumber numberWithBool:YES] range:strikeRange];
CFRelease(font);
}
return mutableAttributedString;
}];
[self.view addSubview:sliderlabel];
[sliderlabel release];
Now I want it to be without strike through when I perform some operation like click on a button, passing [NSNumber numberWithBool:NO] in addAttribute:value:range doesnt work. Any suggestions?
Try this additions.
#implementation TTTAttributedLabel (Additions)
- (void)setStrikeThroughOn:(BOOL)isStrikeThrough {
NSString* text = self.text;
[self setText:text afterInheritingLabelAttributesAndConfiguringWithBlock:^ NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
NSRange strikeRange = [[mutableAttributedString string] rangeOfString:text options:NSCaseInsensitiveSearch];
[mutableAttributedString addAttribute:kTTTStrikeOutAttributeName value:[NSNumber numberWithBool:isStrikeThrough] range:strikeRange];
return mutableAttributedString;
}];
// must trigger redraw
[self setNeedsDisplay];
}
#end