如何拦截UITextView中的单击链接?


101

当用户触摸UITextView中的自动检测到的电话链接时,是否可以执行自定义操作。请不要建议改用UIWebView。

而且,请不要只是重复苹果类参考中的文字-当然,我已经读过了。

谢谢。

Answers:


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);

第三个参数是您的应用程序的类名。因此,将以下行替换为:

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

这帮了我大忙。


最后真正的答案!简单而酷的想法。谢谢,它有效。很久以前,所以我已经设法不用它了。将来还是有帮助的。尽管进行了较小的修正,但应该返回else分支中super的结果:return [super openURL:url];
弗拉基米尔·

2
您还可以分类UIApplication并替换openURL实现。虽然通过这种方式引用原始实现是棘手的(但并非不可能)。
mxcl 2010年

1
仅供参考-我在GitHub上发布了一个完全实现此功能的BrowserViewController,并支持从UIWebView中单击的链接:github.com/nbuggia/Browser-View-Controller--iPhone-
内森·布贾

3
这似乎仅适用于Web链接,不适用于自动格式化的电话号码。
阿兹德夫2012年

我可以通过哪种方法设置应用程序的委托?
ratsimihah 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>标签仍然是在这种情况下,以最好的一段路要走。
MLQ 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

7

使用Swift 5和iOS 12,您可以使用以下三种模式之一来与中的链接进行交互UITextView


#1。使用UITextViewdataDetectorTypes属性。

与中的电话号码,网址或地址进行交互的最简单方法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。使用UITextViewDelegatetextView(_:shouldInteractWith:in:interaction:)方法

如果要执行一些自定义操作,而不是UIAlertController在使用时点击电话号码时弹出窗口dataDetectorTypes,则必须使自己UIViewController遵守UITextViewDelegate协议并实施textView(_: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。使用NSAttributedStringNSAttributedString.Key.link

另外,您可以使用NSAttributedStringURL为其NSAttributedString.Key.link属性设置。下面的示例代码显示了它的可能实现。使用此代码,当用户点击属性字符串时,将UIAlertController弹出一个窗口。

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
    }

}

6

迅捷版:

您的标准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
    }
}

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)无论您在何处设置textview,都应执行以下操作:

//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")
    }
}


如果您使用情节提要,请确保右窗格身份检查器中的textview如下所示:
在此处输入图片说明



瞧!现在您将立即获得链接点击,而不是当URL shouldInteractWith URL方法时


也:如果您不希望处理该网址,只需将shouldInteractWith方法设置为返回false
Josh O'Connor

认为这有很多问题,例如当您不点击链接时会发生什么。也就是说,由于返回nil,因此我认为文本视图不再能正常工作。当您点击链接时,选择也会更改,因为在这种情况下,将返回自身。
德鲁·麦考马克

对我来说很棒,您需要根据需要处理此类情况
Josh O'Connor

变量linkDetectDelegate:QuickDetectLinkTextViewDelegate?
maslovsa

@vyachaslav不适合我,您必须做错了
Josh O'Connor

0

我自己还没有尝试过,但是您可以尝试application:handleOpenURL:在应用程序委托中实现方法-看起来所有openURL请求都通过此回调传递。


0

不确定如何截获检测到的数据链接,或者需要运行哪种类型的函数。但是,如果您知道要查找的内容,则可以使用didBeginEditing TextField方法对文本字段进行测试/扫描。例如,比较符合###-###-####格式的文本字符串,或以“ www。”开头 捕获这些字段,但是您需要编写一些代码来嗅探textfields字符串,重新整理所需的内容,然后将其提取出来以供函数使用。我不认为这会那么困难,一旦您精确地缩小了想要的范围,然后将if()语句集中到所需的非常特定的匹配模式上。

当然,这意味着用户将要触摸文本框以激活didBeginEditing()。如果这不是您要查找的用户交互类型,则可以只使用触发计时器,该触发计时器根据需要从ViewDidAppear()或其他开始,并贯穿文本字段字符串,然后结束时贯穿文本字段字符串您建立的方法,只需关闭计时器。


0

application:handleOpenURL:当另一个应用通过使用您的应用支持的方案打开URL来打开您的应用时调用。当您的应用开始打开URL时,不会调用它。

我认为做弗拉基米尔想要的唯一方法是使用UIWebView而不是UITextView。使您的视图控制器实现UIWebViewDelegate,将UIWebView的委托设置为视图控制器,并在视图控制器实现中在视图webView:shouldStartLoadWithRequest:navigationType:中打开[request URL],而不是退出应用程序并在Mobile Safari中打开它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.