1

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?

koen
  • 5,383
  • 7
  • 50
  • 89

2 Answers2

0

If these urls are fixed. For example: I have the following urls:

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!

  • Not sure how this allows me to change the color of an url that was selected. Furthermore, the urls are already in an `NSString` which is part of my CoreData model, so I don't think I can use `appendAttributedString:`, etc. to construct an `NSMutableAttributedString`. – koen Apr 06 '15 at 16:24
  • Basically there is no link attribute for changing the visited link on UITextView. ([check here](https://developer.apple.com/library/ios/documentation/UIKit/Reference/NSAttributedString_UIKit_Additions/index.html#//apple_ref/doc/constant_group/Character_Attributes)) But there is a delegate of the UITextView that catches the clicked URL. The idea is get those strings reassign it to the UITextView. Otherwise just use a UIWebView then format the HTML String, which I think is a harder solution. – Mark Dominick Flores Apr 06 '15 at 16:32
  • Yeah, I looked at `shouldInteractWithURL:` before, but it didn't work well and I switched to gesture recognizers. But I will revisited it since it sounds like a good place to change the colors. – koen Apr 06 '15 at 17:02
0

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.

koen
  • 5,383
  • 7
  • 50
  • 89