【问题标题】:How to intercept click on link in UITextView?如何拦截点击 UITextView 中的链接?
【发布时间】:2011-02-02 09:24:00
【问题描述】:

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

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

谢谢。

【问题讨论】:

    标签: iphone objective-c uitextview datadetectortypes


    【解决方案1】:

    更新:来自

    - (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];
    • 您还可以对UIApplication 进行分类并替换openURL 实现。虽然这种方式引用原始实现很棘手(但并非不可能)。
    • 仅供参考 - 我在 GitHub 上发布了一个完全实现此功能的 BrowserViewController,并支持从 UIWebView 中单击的链接:github.com/nbuggia/Browser-View-Controller--iPhone-
    • 这似乎只适用于网络链接,不适用于自动格式化的电话号码。
    • 我可以通过什么方法设置我的应用程序的委托?
    【解决方案2】:

    在 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。
    • 不幸的是,如果您想创建一些其他文本 the 链接而不是 URL 本身,您最终仍会使用 UIWebView。在这种情况下,<a> 标签仍然是最好的选择。
    • 如果其他人看到这个,您现在可以将一些其他文本作为链接。
    【解决方案3】:

    对于 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
    

    【讨论】:

      【解决方案4】:

      在 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:)方法

      如果您想执行一些自定义操作而不是在使用dataDetectorTypes 时点击电话号码时弹出UIAlertController,您必须使您的UIViewController 符合UITextViewDelegate 协议并实现@ 987654337@。下面的代码展示了如何实现它:

      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

      作为替代方案,您可以使用NSAttributedString 并为其NSAttributedString.Key.link 属性设置URL。下面的示例代码显示了它的可能实现。使用此代码,当用户点击属性字符串时,会弹出一个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
          }
      
      }
      

      【讨论】:

        【解决方案5】:

        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】:

          斯威夫特 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")
              }
          }
          


          如果您使用故事板,请确保您的文本视图在右窗格身份检查器中如下所示:



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

          【讨论】:

          • 还有:如果你不想处理url,只要设置shouldInteractWith方法返回false
          • 认为这有很多问题,例如当您不点击链接时会发生什么。即我认为文本视图将不再正常工作,因为正在返回 nil。当您点击链接时,选择也会改变,因为在这种情况下,会返回 self。
          • 非常适合我,您需要根据自己的需要处理此类情况
          • var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
          • @vyachaslav 不适合我,你一定是做错了什么
          【解决方案7】:

          application:handleOpenURL: 在另一个应用程序打开 您的 应用程序时调用,方法是使用您的应用程序支持的方案打开 URL。当您的应用开始打开 ​​URL 时不会调用它。

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

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

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

              【讨论】:

                猜你喜欢
                • 2012-10-25
                • 1970-01-01
                • 1970-01-01
                • 2011-04-22
                • 2015-04-20
                • 2011-02-13
                • 2016-12-24
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多