UITextViewのリンクのクリックをインターセプトする方法?


101

ユーザーがUITextViewで自動検出された電話リンクをタッチしたときにカスタムアクションを実行することは可能ですか?代わりにUIWebViewを使用するようにアドバイスしないでください。

そして、Appleクラスリファレンスからのテキストを繰り返すだけではなく、確かにすでに読んでいます。

ありがとう。

回答:


144

更新:から

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction;

から 以降UITextViewはデリゲートメソッドがあります:

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange *NS_DEPRECATED_IOS(7_0, 10_0, "Use textView:shouldInteractWithURL:inRange:forInteractionType: instead");*

リンクへのクリックを遮断します。そして、これはそれを行うための最良の方法です。

ために 以前は、これを行うための良い方法は、サブクラス化UIApplicationして上書きすることです-(BOOL)openURL:(NSURL *)url

@interface MyApplication : UIApplication {

}

@end

@implementation MyApplication


-(BOOL)openURL:(NSURL *)url{
    if  ([self.delegate openURL:url])
         return YES;
    else
         return [super openURL:url];
}
@end

openURL:デリゲートに実装する必要があります。

ここで、アプリケーションをの新しいサブクラスで開始するにUIApplicationは、プロジェクトでファイルmain.mを見つけます。アプリをブートストラップするこの小さなファイルには、通常、次の行があります。

int retVal = UIApplicationMain(argc, argv, nil, nil);

3番目のパラメーターは、アプリケーションのクラス名です。したがって、この行を次のように置き換えます。

int retVal = UIApplicationMain(argc, argv, @"MyApplication", nil);

これは私にとってはトリックでした。


やっと本当の答え!シンプルでクールなアイデア。ありがとう、うまくいきました。ずいぶん前だったので、それなしでなんとかやりました。それでも将来は役立つかもしれません。小さな修正ですが、elseブランチのsuperからの結果が返されます:return [super openURL:url];
ウラジミール

2
またUIApplication、openURL実装を分類して置き換えることもできます。この方法でも、元の実装を参照するのは難しい(ただし不可能ではない)。
mxcl

1
参考までに、これを完全に実装するBrowserViewControllerを投稿し、UIWebView内からクリックしたサポートリンクをここに表示します:github.com/nbuggia/Browser-View-Controller--iPhone-
Nathan Buggia

3
これは、自動フォーマットされた電話番号ではなく、Webリンクでのみ機能するようです。
azdev 2012年

アプリケーションのデリゲートはどの方法で設定できますか?
ratimihah 2012

50

iOS 7以降

次のUITextViewデリゲートメソッドを使用できます。

- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange

ユーザーがURLリンクをタップまたは長押しすると、テキストビューはこのメソッドを呼び出します。このメソッドの実装はオプションです。デフォルトでは、テキストビューはURLタイプを処理するアプリケーションを開き、URLを渡します。このメソッドを使用して、現在のアプリケーション内のWebビューのURLにWebコンテンツを表示するなど、代替アクションをトリガーできます。

重要:

テキストビューのリンクは、テキストビューが選択可能で編集不可の場合にのみインタラクティブです。つまり、UITextViewの値の場合、selectableプロパティはYESで、isEditableプロパティはNOです。


これをUITextViewDelegateに追加してよかったです。
fsaint 2014年

残念ながら、あなたはまだ使用して終わるだろうUIWebViewあなたには、いくつかの他のテキストにしたい場合は、リンクではなくURL自体を。タグはまだその場合に行くための最良の方法です。<a>
マシュー・キロス2014

他の人がこれを見た場合に備えて、他のテキストをリンクにすることができます。
体系的な

10

Swift 3の場合

textView.delegate = self

extension MyTextView: UITextViewDelegate {

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

        GCITracking.sharedInstance.track(externalLink: URL)
        return true
    }
}

またはターゲットがIOS 10以上の場合

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

6

Swiftバージョン:

標準のUITextViewセットアップは次のようになります。デリゲートとdataDetectorTypesを忘れないでください。

var textView = UITextView(x: 10, y: 10, width: CardWidth - 20, height: placeholderHeight) //This is my custom initializer
textView.text = "dsfadsaf www.google.com"
textView.selectable = true
textView.dataDetectorTypes = UIDataDetectorTypes.Link
textView.delegate = self
addSubview(textView)

クラスが終了したら、この部分を追加します。

class myVC: UIViewController {
    //viewdidload and other stuff here
}

extension MainCard: UITextViewDelegate {
    func textView(textView: UITextView, shouldInteractWithURL URL: NSURL, inRange characterRange: NSRange) -> Bool {
        //Do your stuff over here
        var webViewController = SVModalWebViewController(URL: URL)
        view.presentViewController(webViewController, animated: true, completion: nil)
        return false
    }
}

6

Swift 5とiOS 12では、次の3つのパターンのいずれかを使用して、のリンクを操作できますUITextView


#1。UITextViewdataDetectorTypesプロパティを使用します。

で電話番号、URL、またはアドレスを操作する最も簡単な方法UITextViewは、dataDetectorTypesプロパティを使用することです。以下のサンプルコードは、それを実装する方法を示しています。このコードでは、ユーザーが電話番号をタップすると、UIAlertControllerポップアップが表示されます。

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        textView.text = "Phone number: +33687654321"
        textView.isUserInteractionEnabled = true
        textView.isEditable = false
        textView.isSelectable = true
        textView.dataDetectorTypes = [.phoneNumber]
        textView.isScrollEnabled = false

        textView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textView)
        textView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        textView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
    }

}

#2。使い方UITextViewDelegateさんtextView(_:shouldInteractWith:in:interaction:)方法する

UIAlertController使用中に電話番号をタップしたときにポップアップを表示するのではなく、カスタムアクションを実行する場合は、プロトコルに準拠させて実装dataDetectorTypesする必要があります。以下のコードは、それを実装する方法を示しています。UIViewControllerUITextViewDelegatetextView(_:shouldInteractWith:in:interaction:)

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    override func viewDidLoad() {
        super.viewDidLoad()

        let textView = UITextView()
        textView.delegate = self
        textView.text = "Phone number: +33687654321"
        textView.isUserInteractionEnabled = true
        textView.isEditable = false
        textView.isSelectable = true
        textView.dataDetectorTypes = [.phoneNumber]
        textView.isScrollEnabled = false

        textView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textView)
        textView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        textView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
    }

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
        /* perform your own custom actions here */
        print(URL) // prints: "tel:+33687654321"

        return false // return true if you also want UIAlertController to pop up
    }

}

#3。使用するNSAttributedStringと、NSAttributedString.Key.link

別の方法として、その属性にを使用NSAttributedStringして設定することができます。以下のサンプルコードは、可能な実装を示しています。このコードでは、ユーザーが属性付き文字列をタップすると、ポップアップが表示されます。URLNSAttributedString.Key.linkUIAlertController

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let attributedString = NSMutableAttributedString(string: "Contact: ")
        let phoneUrl = NSURL(string: "tel:+33687654321")! // "telprompt://+33687654321" also works
        let attributes = [NSAttributedString.Key.link: phoneUrl]
        let phoneAttributedString = NSAttributedString(string: "phone number", attributes: attributes)
        attributedString.append(phoneAttributedString)

        let textView = UITextView()
        textView.attributedText = attributedString
        textView.isUserInteractionEnabled = true
        textView.isEditable = false
        textView.isSelectable = true
        textView.isScrollEnabled = false

        textView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(textView)
        textView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        textView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        textView.leadingAnchor.constraint(equalTo: view.layoutMarginsGuide.leadingAnchor).isActive = true
    }

}

1

スウィフト4:

1)次のクラス(サブクラス化されたUITextView)を作成します。

import Foundation

protocol QuickDetectLinkTextViewDelegate: class {
    func tappedLink()
}

class QuickDetectLinkTextView: UITextView {

    var linkDetectDelegate: QuickDetectLinkTextViewDelegate?

    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)

    }

    required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
    }

    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let glyphIndex: Int? = layoutManager.glyphIndex(for: point, in: textContainer, fractionOfDistanceThroughGlyph: nil)
        let index: Int? = layoutManager.characterIndexForGlyph(at: glyphIndex ?? 0)
        if let characterIndex = index {
            if characterIndex < textStorage.length {
                if textStorage.attribute(NSLinkAttributeName, at: characterIndex, effectiveRange: nil) != nil {
                    linkDetectDelegate?.tappedLink()
                    return self
                }
            }
        }

        return nil
    }
}


2)テキストビューを設定する場合は常に、次のようにします。

//init, viewDidLoad, etc
textView.linkDetectDelegate = self

//outlet
@IBOutlet weak var textView: QuickDetectLinkTextView!

//change ClassName to your class
extension ClassName: QuickDetectLinkTextViewDelegate {
    func tappedLink() {
        print("Tapped link, do something")
    }
}


ストーリーボードを使用している場合は、右側のペインのIDインスペクターでテキストビューが次のようになっていることを確認してください。
ここに画像の説明を入力してください



出来上がり!これで、URL shouldInteractWith URLメソッドの代わりに、すぐにリンクタップを取得できます


また、URLを処理しない場合は、shouldInteractWithメソッドをfalseを返すように設定するだけです
Josh O'Connor

これには、リンクをタップしないとどうなるかなど、たくさんの問題があると思います。つまり、nilが返されているため、テキストビューは正常に機能しなくなります。リンクをタップすると選択が変わります。その場合、自分が返されるからです。
Drew McCormack

私にとってはうまく機能し、あなたはあなたのニーズのためにそのようなケースを処理する必要があります
ジョシュ・オコナー

弱い変数linkDetectDelegate:QuickDetectLinkTextViewDelegate?
マスロフサ2018

@vyachaslavは私には適していません。何か間違っているのでしょう
Josh O'Connor

0

私自身は試していませんapplication:handleOpenURL:が、アプリケーションデリゲートにメソッドを実装することはできopenURLます。すべてのリクエストがこのコールバックを通過するようです。


0

検出されたデータリンクをどのようにインターセプトするか、または実行する必要がある関数のタイプが不明です。しかし、didBeginEditing TextFieldメソッドを利用して、探しているものがわかっている場合は、テキストフィールド全体をテスト/スキャンできます。たとえば、###-###-####形式を満たすテキスト文字列を比較したり、または「www」で始まる。これらのフィールドを取得するには、textfields文字列をスニッフィングし、必要なものを再調整して、関数で使用するために抽出するための小さなコードを書く必要があります。私はこれがそれほど難しいとは思わない、一度あなたがそれが望むものを正確に絞り込み、そしてあなたのif()ステートメントがあなたが必要とするものの非常に特定のマッチングパターンにフィルターを絞ったならば。

これは、ユーザーがdidBeginEditing()をアクティブにするためにテキストボックスに触れることを意味します。それがあなたが探していたユーザーインタラクションのタイプではない場合、ViewDidAppear()または必要に応じて開始されるトリガーTimerを使用して、テキストフィールド文字列を実行し、最後にテキストフィールド文字列を実行することができます作成したメソッドでは、単にタイマーをオフに戻します。


0

application:handleOpenURL:アプリがサポートするスキームを含むURLを開い別のアプリがアプリを開くと呼び出されます。アプリがURLを開き始めるときに呼び出されません。

私はウラジミールが望むことをする唯一の方法はUITextViewの代わりにUIWebViewを使うことだと思います。ビューコントローラーにUIWebViewDelegateを実装させ、UIWebViewのデリゲートをビューコントローラーに設定し、ビューコントローラーの実装で、アプリを終了してMobile SafariでwebView:shouldStartLoadWithRequest:navigationType:開くの[request URL]ではなく、ビューで開くようにします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.