Як перехопити клік на посилання в UITextView?


101

Чи можливо виконати власну дію, коли користувач торкнеться автоматично виявленої телефонної посилання в UITextView. Будь ласка, не радимо використовувати UIWebView замість цього.

І будь ласка, не просто повторюйте текст із посилань на класи з яблук - звичайно, я вже його читав.

Дякую.

Відповіді:


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

Це зробило для мене трюк.


Нарешті реальна відповідь! Проста і крута ідея. Спасибі, працює. Це було давно, тому я вже без цього обійшлася. Ще може допомогти в майбутньому. Невелике виправлення, однак, слід отримати результат із супер іншої гілки: return [super openURL: url];
Володимир

2
Ви також можете класифікувати UIApplicationта замінити реалізацію openURL. Хоча таким чином складно (але не неможливо) посилатися на оригінальну реалізацію.
mxcl

1
FYI - Я опублікував на GitHub браузер BrowserViewController, який повністю реалізує це, а також підтримуючі посилання, натиснуті в межах UIWebView тут: github.com/nbuggia/Browser-View-Controller--iPhone- .
Nathan Buggia

3
Це, здається, працює лише для веб-посилань, а не для автоматизованих номерів телефонів.
azdev

Яким методом можна встановити делегата програми?
ratsimihah

50

В iOS 7 або новіших версіях

Ви можете використовувати такий метод делегування UITextView:

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

Текстовий вигляд викликає цей метод, якщо користувач натискає або довго натискає посилання URL. Реалізація цього методу необов’язкова. За замовчуванням текстове подання відкриває програму, відповідальну за обробку типу URL-адреси, і передає їй URL-адресу. Ви можете використовувати цей метод для запуску альтернативної дії, наприклад відображення веб-вмісту за URL-адресою у веб-перегляді в поточній програмі.

Важливо:

Посилання в текстових поданнях є інтерактивними лише в тому випадку, якщо вигляд тексту можна вибрати, але не можна редагувати. Тобто, якщо значенням UITextView властивість вибору є ТАК, а властивість isEditable НІ.


Я радий, що вони додали це до UITextViewDelegate.
вигадка

На жаль , ви все ще будете в кінцевому підсумку з допомогою , UIWebViewякщо ви хочете зробити якийсь -небудь інший текст посилання , а не сам URL. Тег по - , як і раніше кращий спосіб йти в цьому випадку. <a>
MLQ

Якщо це бачать інші, тепер ви можете зробити ще якийсь текст посилання.
Схематично

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 ви можете використовувати один із трьох наступних шаблонів для взаємодії із посиланнями в a UITextView.


№1. Використання властивості UITextView's dataDetectorTypes.

Найпростіший спосіб взаємодії з номерами телефонів, 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, ви повинні зробити свою 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. Використання NSAttributedStringтаNSAttributedString.Key.link

Як альтернативу, ви можете використовувати NSAttributedStringта встановити URLдля його 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) Де б ви не налаштували перегляд тексту, зробіть це:

//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-адреса має взаємодіяти із методом URL-адреси


також: якщо ви хочете, щоб URL не оброблявся, просто встановіть метод ShouldInteractWith, щоб повернути помилкове
Josh O'Connor

Подумайте, у цьому є маса проблем, наприклад, що відбувається, коли ви не натискаєте посилання. Тобто, я не думаю, що перегляд тексту більше не працюватиме нормально, оскільки нуль повертається. Вибір також зміниться, коли ви натиснете посилання, тому що в такому випадку самовіддача повертається.
Дрю Маккормак

працює для мене чудово, вам потрібно обробляти такі випадки для ваших потреб
Josh O'Connor

слабкий var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa

@vyachaslav не для мене, ти повинен робити щось не так
Josh O'Connor

0

Я сам не пробував цього, але ви можете спробувати реалізувати application:handleOpenURL:метод у своєму делегаті програми - схоже, що всі openURLзапити проходять через цей зворотний виклик.


0

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

З іншого боку, це означає, що користувач збирається торкнутися текстового поля, щоб активувати didBeginEditing (). Якщо це не той тип взаємодії з користувачем, який ви шукали, ви можете просто скористатися тригерним таймером, який запускається на ViewDidAppear () або інший на основі потреби та проходить через рядок текстових полів, то в кінці ви запустите рядок текстового поля методами, які ви створили, ви просто відключите Таймер.


0

application:handleOpenURL:називається, коли інша програма відкриває ваш додаток, відкриваючи URL-адресу зі схемою, яку підтримує ваш додаток. Він не називається, коли ваш додаток починає відкривати URL-адресу.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.