John Baum
John Baum

Reputation: 3331

Hyperlinks in a UITextView

I am trying to create a UITextView with a hyperlink so that when the user clicks on the link, they are taken to safari to open the webpage. I have read on link detectors for a textview but those samples always show link detection working if an actual url is present in the text (ie. www.google.com). I want it to be regular text that, when clicked, opens an associated URL. (ie. Google is the text and when clicked, opens up a url www.google.com). How can I accomplish this in iOS7/8?

Upvotes: 26

Views: 35376

Answers (6)

Amr Angry
Amr Angry

Reputation: 3831

Swift 3, iOS10 , Xcode 9

@sikhapol's answer is really nice if you knew exactly the words you want to parse like "word dictionary" somehow

it's all about the string itself that displayed in UITextView

My solution is based on the text rendering if you make the UITextView render HTML tags you may use href tag

Here is some Code references you may use

first, you need to configure UITextView from interface builder or form code to

  1. Selectable
  2. Data detectores

Note: do not make the textview editable

Interface builder

enter image description here

programmatically

         let htmlData = NSString(string: "go to <a href=\"http://www.google.com\">google</a> and search for it").data(using: String.Encoding.unicode.rawValue)


        let attributedString = try! NSAttributedString(data: htmlData!, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
        yourUIViewView.isSelectable = true
        yourUIViewView.dataDetectorTypes = .link
        yourUIViewView.attributedText = attributedString
        yourUIViewView.delegate = self

for the UITextViewDelegate

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
    
       // check for the url string for performing your own custom actions here
      let urlString = URL.absoluteString
    
       // Return NO if you don't want iOS to open the link
        return true
     }

Upvotes: 20

Linus Karlsson
Linus Karlsson

Reputation: 546

Since the edit queue was full, I'll post my version of an answer here. It's based on Amr Angry's answer.

I'm using DispatchQueue.main.async { ... } since the app will crash if the NSAttributedString is running on a background thread.

guard let htmlData = NSString(string: "go to <a href=\"http://www.google.com\">google</a> and search for it").data(using: String.Encoding.unicode.rawValue) else { return }
    
DispatchQueue.main.async {
    do {

    let attributedString = try NSAttributedString(data: htmlData, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil)
    yourUIViewView.isSelectable = true
    yourUIViewView.dataDetectorTypes = .link
    yourUIViewView.attributedText = attributedString
    yourUIViewView.delegate = self

    } catch {
        print("Cannot setup link with \(htmlData)!")
    }
}

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

    // Return NO if you don't want iOS to open the link
    return true
}

Upvotes: 0

splangi
splangi

Reputation: 683

A nifty little extension that I wrote and use (Swift 4.2, tested on iOS 12.1)

extension NSAttributedString {
    func replace(placeholder: String, with hyperlink: String, url: String) -> NSAttributedString {
        let mutableAttr = NSMutableAttributedString(attributedString: self)

        let hyperlinkAttr = NSAttributedString(string: hyperlink, attributes: [NSAttributedString.Key.link: URL(string: url)!])

        let placeholderRange = (self.string as NSString).range(of: placeholder)

        mutableAttr.replaceCharacters(in: placeholderRange, with: hyperlinkAttr)
        return mutableAttr
    }
}

Usage:

//Set through code or through interface builder
footerText.isSelectable = true
footerText.dataDetectorTypes = .link

//Keeps the original formatting from xib or storyboard
footerText.text = "By continuing, you are indicating that you accept our @Terms@ and @Privacy@."
footerText.attributedText = footerText.attributedText?
        .replace(placeholder: "@Terms@", with: "Terms and Conditions", url: AppUrl.terms)
        .replace(placeholder: "@Privacy@", with: "Privacy Policy", url: AppUrl.privacy)

Upvotes: 13

Tiago Mendes
Tiago Mendes

Reputation: 5156

This code sample has two different links in the same label and URL color is set to avoid the default blue.

UITextView * textTerm = [UITextView new];
NSMutableAttributedString *attrRight = [[NSMutableAttributedString alloc] initWithString:@"Terms of Service"
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
NSMutableAttributedString *attrLeft = [[NSMutableAttributedString alloc] initWithString:@"Privacy Policy"
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
[attrRight appendAttributedString:attrLeft];
textTerm.attributedText = attrRight;
textTerm.editable = NO;
textTerm.dataDetectorTypes = UIDataDetectorTypeAll;
textTerm.linkTextAttributes = [UIColor whiteColor];
textTerm.backgroundColor = [UIColor clearColor];

Upvotes: 1

Marek Manduch
Marek Manduch

Reputation: 2481

if you want active substring in your UITextView then you can use my extended TextView... its short and simple. You can edit it as you want.

how to use (range = substring location):

[self.textView addTapActionWithRange:range withActionBlock:^{
      // anything you want to do - show something
}];

result: enter image description here

source code: https://github.com/marekmand/ActiveSubstringTextView

Upvotes: 0

yusuke024
yusuke024

Reputation: 2219

Use NSAttributedString

NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:@"Google" 
                                                                       attributes:@{ NSLinkAttributeName: [NSURL URLWithString:@"http://www.google.com"] }];
self.textView.attributedText = attributedString;

Sure, you can set just a portion of the text to be the link. Please read more about the NSAttributedString here.

If you want to have more control and do something before opening the link. You can set the delegate to the UITextView.

- (void)viewDidLoad {
    ...
    self.textView.delegate = self; // self must conform to UITextViewDelegate protocol
}

...

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    // Do whatever you want here
    NSLog(@"%@", URL); // URL is an instance of NSURL of the tapped link
    return YES; // Return NO if you don't want iOS to open the link
}

Upvotes: 33

Related Questions