Jak przechwycić kliknięcie na link w UITextView?

Czy możliwe jest wykonanie niestandardowej akcji, gdy użytkownik dotknie automatycznie wykrytego łącza telefonu w UITextView. Proszę nie doradzać, aby zamiast tego używać UIWebView.

I proszę nie powtarzaj tylko tekstu z Apple classes reference - oczywiście, że już to przeczytałem.

Dzięki.
Author: Vladimir, 2010-03-30

10 answers

Update: From ios10 ,

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

Od ios7 i Później UITextView ma metodę delegata:

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

Przechwytywanie kliknięć na linki. I to jest najlepszy sposób, aby to zrobić.

Dla ios6 a wcześniej dobrym sposobem na to jest podkategoryzowanie UIApplication i nadpisanie -(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

Będziesz musiał zaimplementować openURL: w swoim delegacie.

Teraz, Aby aplikacja zaczęła się od nowej podklasy UIApplication, Znajdź plik główny.m w Twoim projekcie. W tym małym pliku, który uruchamia aplikację, zwykle znajduje się ta linia:

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

Trzeci parametr to nazwa klasy dla Twojej aplikacji. Tak więc, zastąpienie tej linii dla:

int retVal = UIApplicationMain(argc, argv, @"MyApplication", nil);
To mi pomogło.
 126
Author: fsaint,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-16 09:38:01

W systemie iOS 7 lub nowszym

Możesz użyć następującej metody UITextView delegate:

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

Widok tekstowy wywołuje tę metodę, jeśli użytkownik dotknie lub długo naciśnie link URL. Implementacja tej metody jest opcjonalna. Domyślnie widok tekstowy otwiera aplikację odpowiedzialną za obsługę typu adresu URL i przekazuje mu adres URL. Za pomocą tej metody można wywołać alternatywną akcję, na przykład wyświetlanie zawartości sieci web pod adresem URL w widoku sieci web w bieżącym podanie.

Ważne:

Linki w widokach tekstowych są interaktywne tylko wtedy, gdy Widok tekstowy jest do wyboru, ale nie do edycji. Czyli, jeśli wartość UITextView właściwość do wyboru to tak, a właściwość isEditable to nie.

 47
Author: Rajan Balana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-11 08:42:00

Wersja Swift:

Twoja standardowa konfiguracja UITextView powinna wyglądać mniej więcej tak, nie zapomnij o typach delegate i datadetectortyp.

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)

Po zakończeniu zajęć dodaj ten kawałek:

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
    }
}
 3
Author: Esqarrouth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-24 20:33:21

Dla 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
    }
}

Lub jeśli target jest > = IOS 10

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
 3
Author: Ryan Heitner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-03 07:35:58

W Swift 3 i i0S 10 najprostszym sposobem interakcji z numerami telefonów, adresami URL lub adresami w UITextView jest użycie UIDataDetectorTypes. Poniższy kod pokazuje, jak wyświetlić numer telefonu w UITextView, aby użytkownik mógł z nim współpracować.

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]
    }

}

Z tym kodem, po kliknięciu na numer telefonu, pojawi się UIAlertController.


Jako alternatywę możesz użyć 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
    }

}

Z tym kodem, po kliknięciu na przypisany ciąg, a UIAlertController pojawi się w górę.


Jednak możesz chcieć wykonać niestandardową akcję zamiast tworzyć wyskakujące okienko UIAlertController Po kliknięciu numeru telefonu. Możesz też zachować UIAlertController, aby wyskakiwać i wykonywać własne niestandardowe działania w tym samym czasie.

W obu przypadkach będziesz musiał dostosować swój UIViewController do UITextViewDelegate protokołu i wdrożyć textView(_:shouldInteractWith:in:interaction:). Poniższy kod pokazuje, jak to zrobić.

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
    }

}

Z tym kodem, po kliknięciu na numer telefonu, Nie UIAlertController pojawi się i Ty zamiast tego zostanie wyświetlony następujący wydruk w konsoli:

Tel: + 33687654321

 2
Author: Imanou Petit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-22 08:40:01

Mam dynamiczną zawartość w widoku tekstowym, która może zawierać link. ShouldInteractWithURL jest wywoływany tylko wtedy, gdy użytkownik jest długo naciśnięty na link, ale nie wywołuje na dotknięcie link. Proszę przekierować, jeśli jakiś kumpel ma do niego odniesienie.

Poniżej znajduje się fragment kodu.

UITextView *ltextView = [[UITextView alloc] init];
[ltextView setScrollEnabled:YES];
[ltextView setDataDetectorTypes:UIDataDetectorTypeLink];
ltextView.selectable = YES;
[ltextView setEditable:NO];
ltextView.userInteractionEnabled = YES;
ltextView.delegate = (id)self;
ltextView.delaysContentTouches = NO;
[self.view addsubview:ltextview];

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

    [self.mActionDelegate userClickedOnImageLinkFromWebview:URL];

    NSLog(@"urls is :- %@",URL);
    return FALSE;
}

Powyższa metoda delegata nie jest wywoływana na tap.

Pozdrawiam, Rohit Jankar

 1
Author: Rohit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-26 02:32:37

Swift 4:

1) Utwórz następującą klasę (podklasowaną 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) gdziekolwiek skonfigurujesz textview, zrób to:

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


Jeśli używasz storyboard, upewnij się, że widok tekstu wygląda tak w Inspektorze tożsamości po prawej stronie panelu:
Tutaj wpisz opis obrazka



Voila! Teraz otrzymasz link dotknij natychmiast, zamiast gdy URL shouldInteractWith URL metoda

 1
Author: Josh O'Connor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 18:59:09

Sam tego nie próbowałem, ale możesz spróbować zaimplementować metodę application:handleOpenURL: w swoim delegacie aplikacji-wygląda na to, że wszystkie żądania openURL przechodzą przez ten callback.

 0
Author: Vladimir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-31 08:57:59

Nie wiem, w jaki sposób przechwycisz wykryte łącze danych, ani jakiego rodzaju funkcję musisz uruchomić. Ale możesz być w stanie wykorzystać metodę didBeginEditing TextField do uruchomienia testu/skanowania przez textfield, jeśli wiesz, czego szukasz..takich jak porównywanie ciągów tekstowych, które spełniają ###-###-#### formatować, lub zaczynać od " www."aby pobrać te pola, ale musisz napisać mały kod, aby powąchać przez ciąg textfields, ponownie skonfigurować to, czego potrzebujesz, a następnie wyodrębnić go dla swojego zastosowanie funkcji. Nie sądzę, że byłoby to takie trudne, gdy zawęzisz dokładnie to, czego chciałeś, a następnie skupisz swoją instrukcję if() na bardzo specyficznym wzorcu dopasowania tego, czego potrzebujesz.

Z couse oznacza to, że użytkownik dotknie pola tekstowego w celu aktywacji funkcji didBeginEditing (). Jeśli nie jest to typ interakcji użytkownika, którego szukałeś, możesz po prostu użyć timera wyzwalającego, który rozpoczyna się w viewdidappear () lub w inny sposób oparty na potrzebujesz i uruchamia łańcuch textfields, a następnie na końcu uruchamiasz metody TextField string, które zbudowałeś, po prostu wyłączasz zegar z powrotem.

 0
Author: Newbyman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-04-04 23:47:34

application:handleOpenURL: jest wywoływany, gdy inna aplikacja otworzy Twoją aplikację, otwierając adres URL ze schematem, który obsługuje Twoja aplikacja. Nie jest wywoływany, gdy aplikacja zaczyna otwierać adres URL.

Myślę, że jedynym sposobem, aby zrobić to, czego chce Vladimir, jest użycie UIWebView zamiast UITextView. Upewnij się, że kontroler widoku zaimplementuje UIWebViewDelegate, Ustaw delegata UIWebView na kontroler widoku, a w kontrolerze widoku zaimplementuj webView:shouldStartLoadWithRequest:navigationType:, aby otworzyć [request URL] w widoku zamiast zamykać aplikację i otwierać ją w Mobilne Safari.

 0
Author: A. Jesse Jiryu Davis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-08 02:31:18