9

I have a UIViewController with a UITextView that auto-detects hyperlinks in its text. It works properly, but I'd to use SFSafariViewController to open the links so I stay "inside" my app, rather than opening a separate browser, which is the "out of the box" behavior.

I've taken the steps outline below, but websites still open a separate Safari browser, rather than inside my app. I get no errors or warnings, but the websites detected still open in a separate browser, not within my app. The UITextViewDelegate method doesn't appear to be getting called (I threw a log statement in to check).

I looked at UITextViewDelegate and I think I want to use this method to open the website that's detected by the UITextView:

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    // Fill code in here to use SFSafariViewController
    return YES;
}

What I've done so far:

1) Imported SafariServices, made delegate declaration, and declared a delegate property in MyViewController.h

@import SafariServices;

@interface MyViewController : UIViewController <UITextViewDelegate, SFSafariViewControllerDelegate>

@property(nonatomic, weak, nullable) id< SFSafariViewControllerDelegate, SFSafariViewControllerDelegate > delegate;

2) Added a delegate section to my .m file and tried to fill in this method from the stub above:

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
    SFSafariViewController *websiteToOpen = [[SFSafariViewController alloc]initWithURL:URL entersReaderIfAvailable:YES];
    websiteToOpen.delegate = self;
    [self presentViewController:websiteToOpen animated:YES completion:nil];
    return YES;
}

I'm certain it's 100% me mucking this up, but I'm unable to get over the finish line. What am I missing?

Adrian
  • 16,233
  • 18
  • 112
  • 180

3 Answers3

17

in case someone is looking for Swift 3 implementation.

Solution that works:

1.Importing Safari Services

import SafariServices

2.Creating attributed text, adding link to it, assigning string to textView

let myLink = "google.com"
let myText = "here is my link: \(myLink)"
let myAttributedText = NSMutableAttributedString(string: myText)
let rangeOfMyLink = myText.range(of: myLink)
if rangeOfMyLink.location != NSNotFound {
    myAttributedText.addAttributes([NSLinkAttributeName: myLink], range: rangeOfMyLink)
}
myTextView.attributedText = myAttributedText
myTextView.delegate = self

3.Add Delegate to VC:

class MyViewController: UIViewController, UITextViewDelegate

4.Adding Delegate method:

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
    let safariVC = SFSafariViewController(url: URL)
    present(safariVC, animated: true, completion: nil)
    return false
}

Hope it helps!

Tung Fam
  • 7,899
  • 4
  • 56
  • 63
3

I am trying to do the same thing, but having different problems.

Firstly, you'll need to set your text view's delegate for the delegate method to fire. I do this in viewDidLoad:

myTextView.delegate = self;

Then, tell your UITextView not to interact with the URL:

- (BOOL)textView:(UITextView *)textView 
        shouldInteractWithURL:(NSURL *)url 
        inRange:(NSRange)characterRange
{
    [self presentViewController:websiteToOpen animated:YES completion:nil];
    return NO;
}

With this, I can present the safari view controller, although it is empty, i.e. no content and no Done button. So I'm doing something else wrong...

Alex Cio
  • 6,014
  • 5
  • 44
  • 74
time_trial
  • 31
  • 1
3

You are returning YES at the end of your implementation, which tells the system, go ahead and do what you were going to do with this URL (in this case, open Safari). If you decide to handle this yourself, return NO instead.

That being said, a few caveats:

  • Safari view controller only supports http / https URLs. Make sure you check URL.scheme before using Safari view controller.
  • Although the documentation for textView:shouldInteractWithURL:inRange: suggests that you should use this method to intercept taps on URLs...

    You can use this method to trigger an alternative action, such as displaying the web content at the URL in a web view within the current application.

    ...you want to be careful. "Should interact" includes more than just a regular tap - this method will also be called for long presses and so on, which usually have a different behaviour, and there's no way that I know of to determine why this delegate method is being called.

For these reasons, you may want to consider attaching your own UITapGesturRecognizer to the UITextView, as detailed in this answer.

Community
  • 1
  • 1
jsadler
  • 589
  • 4
  • 18