Как перехватить клик по ссылке в UITextView? - PullRequest
92 голосов
/ 30 марта 2010

Можно ли выполнить пользовательское действие, когда пользователь коснется автоматически определенной телефонной ссылки в UITextView. Пожалуйста, не советуйте использовать UIWebView вместо этого.

И, пожалуйста, не просто повторяйте текст из справочника классов Apple - конечно, я уже прочитал это.

Спасибо.

Ответы [ 9 ]

135 голосов
/ 02 декабря 2010

Обновление: с ,

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

Это помогло мне.

50 голосов
/ 11 ноября 2013

В iOS 7 или новее

Вы можете использовать следующий метод делегата UITextView:

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

Текстовое представление вызывает этот метод, если пользователь нажимает или долго нажимает ссылку URL. Реализация этого метода не является обязательной. По умолчанию текстовое представление открывает приложение, ответственное за обработку типа URL, и передает ему URL. Этот метод можно использовать для запуска альтернативного действия, например, отображения веб-содержимого по URL-адресу в веб-представлении в текущем приложении.

Важно:

Ссылки в текстовых представлениях являются интерактивными, только если текстовое представление выбирается, но не редактируется. То есть, если значение UITextView выбираемое свойство - YES, а свойство isEditable - NO.

6 голосов
/ 03 января 2017

для 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
5 голосов
/ 24 июня 2015

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
    }
}
2 голосов
/ 30 июня 2016

В Swift 3 и i0S 10 самый простой способ взаимодействия с телефонными номерами, URL-адресами или адресами в UITextView - это использовать UIDataDetectorTypes. Приведенный ниже код показывает, как отобразить номер телефона в UITextView, чтобы пользователь мог взаимодействовать с ним.

import UIKit

class ViewController: UIViewController {

    // Link this outlet to a UITextView in your Storyboard
    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.text = "+33687654321"
        textView.isUserInteractionEnabled = true // default: true
        textView.isEditable = false // default: true
        textView.isSelectable = true // default: true
        textView.dataDetectorTypes = [.phoneNumber]
    }

}

С этим кодом при нажатии на номер телефона всплывает UIAlertController.


В качестве альтернативы вы можете использовать NSAttributedString:

import UIKit

class ViewController: UIViewController {

    // Link this outlet to a UITextView in your Storyboard
    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let phoneUrl = NSURL(string: "tel:+33687654321")! // "telprompt://+33687654321" also works
        let attributes = [NSLinkAttributeName: phoneUrl]
        let attributedString = NSAttributedString(string: "phone number", attributes: attributes)

        textView.attributedText = attributedString
        textView.isUserInteractionEnabled = true // default: true
        textView.isEditable = false // default: true
        textView.isSelectable = true // default: true
    }

}

С этим кодом, при нажатии на приписанную строку, появится UIAlertController.


Однако вы можете захотеть выполнить какое-то пользовательское действие вместо всплывающего UIAlertController при нажатии на номер телефона. Или вы можете оставить UIAlertController для всплывающего окна и одновременно выполнять свои собственные действия.

В обоих случаях вам придется настроить UIViewController на соответствие протоколу UITextViewDelegate и внедрить textView(_:shouldInteractWith:in:interaction:). Код ниже показывает, как это сделать.

import UIKit

class ViewController: UIViewController, UITextViewDelegate {

    // Link this outlet to a UITextView in your Storyboard
    @IBOutlet weak var textView: UITextView!

    override func viewDidLoad() {
        super.viewDidLoad()

        textView.delegate = self

        textView.text = "+33687654321"
        textView.isUserInteractionEnabled = true
        textView.isEditable = false
        textView.isSelectable = true
        textView.dataDetectorTypes = [.phoneNumber]
    }

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

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

}

С помощью этого кода при нажатии на номер телефона не будет отображаться UIAlertController, и вы получите вместо этого следующую консоль в консоли:

тел: + 33687654321

0 голосов
/ 29 ноября 2017

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


Если вы используете раскадровку, убедитесь, что ваш текстовый вид выглядит в правом окне инспектора идентификации:
enter image description here



Вуаля! Теперь вы получаете немедленное нажатие ссылки вместо того, когда URL должен взаимодействовать с методом URL

0 голосов
/ 08 июня 2010

application:handleOpenURL: вызывается, когда другое приложение открывает ваше приложение, открывая URL со схемой, поддерживаемой вашим приложением. Он не вызывается, когда ваше приложение начинает открывать URL.

Я думаю, что единственный способ сделать то, что хочет Владимир, - это использовать UIWebView вместо UITextView. Сделайте так, чтобы ваш контроллер представления реализовал UIWebViewDelegate, установите делегат UIWebView на контроллер представления, а в контроллере представления реализуйте webView:shouldStartLoadWithRequest:navigationType:, чтобы открыть [request URL] в представлении вместо выхода из приложения и открытия его в Mobile Safari.

0 голосов
/ 05 апреля 2010

Не уверен, как бы вы перехватили обнаруженный канал передачи данных или какой тип функции вам нужно запустить. Но вы можете использовать метод didBeginEditing TextField для запуска теста / сканирования через текстовое поле, если вы знаете, что ищете .. например, сравнивая текстовые строки, которые соответствуют формату ### - ### - ####, или начните с "www." чтобы захватить эти поля, но вам нужно написать небольшой код, чтобы прослушать строку textfields, восстановить то, что вам нужно, а затем извлечь его для использования вашей функцией. Я не думаю, что это будет так сложно, как только вы сузите в точности то, что вы хотели, а затем сфокусируете свои выражения if () на очень конкретном шаблоне соответствия того, что вам нужно.

Конечно, это означает, что пользователь собирается коснуться текстового поля, чтобы активировать didBeginEditing (). Если это не тот тип взаимодействия с пользователем, который вы искали, вы можете просто использовать таймер триггера, который запускается в ViewDidAppear () или другом в зависимости от необходимости и проходит через строку текстовых полей, а затем в конце вы пробегаете строку текстового поля. методы, которые вы создали, вы просто выключаете таймер.

0 голосов
/ 31 марта 2010

Я сам не пробовал, но вы можете попробовать реализовать метод application:handleOpenURL: в делегате приложения - похоже, все запросы openURL проходят через этот обратный вызов.

...