Comment intercepter le clic sur le lien dans UITextView?

101

Est-il possible d'effectuer une action personnalisée lorsque l'utilisateur touche le lien téléphonique détecté automatiquement dans UITextView. Veuillez ne pas utiliser UIWebView à la place.

Et s'il vous plaît, ne répétez pas simplement le texte de la référence des classes Apple - je l'ai certainement déjà lu.

Merci.

Vladimir
la source

Réponses:

144

Mise à jour: De ,

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

De et plus tard UITextViewa la méthode déléguée:

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

pour intercepter les clics sur les liens. Et c'est la meilleure façon de le faire.

Pour et plus tôt, une bonne façon de le faire est de sous UIApplication-classer et d'écraser le-(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

Vous devrez mettre openURL:en œuvre dans votre délégué.

Maintenant, pour que l'application démarre avec votre nouvelle sous-classe de UIApplication, recherchez le fichier main.m dans votre projet. Dans ce petit fichier qui démarre votre application, il y a généralement cette ligne:

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

Le troisième paramètre est le nom de classe de votre application. Donc, en remplaçant cette ligne pour:

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

Cela a fait l'affaire pour moi.

faible
la source
Enfin une vraie réponse! Idée simple et cool. Merci, ça marche. C'était il y a longtemps, donc je me suis déjà débrouillé sans. Peut encore aider à l'avenir. Petite correction cependant, le résultat de la branche super in else doit être retourné: return [super openURL: url];
Vladimir
2
Vous pouvez également classer UIApplicationet remplacer l'implémentation openURL. Bien que de cette façon, il soit difficile (mais pas impossible) de référencer l'implémentation d'origine.
mxcl
1
FYI - J'ai publié un BrowserViewController sur GitHub qui l'implémente pleinement, ainsi que des liens de support cliqués à partir d'un UIWebView ici: github.com/nbuggia/Browser-View-Controller--iPhone- .
Nathan Buggia
3
Cela ne semble fonctionner que pour les liens Web, pas pour les numéros de téléphone mis en forme automatiquement.
azdev
Dans quelle méthode puis-je définir le délégué de mon application?
ratsimihah
50

Sous iOS 7 ou version ultérieure

Vous pouvez utiliser la méthode de délégué UITextView suivante:

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

La vue texte appelle cette méthode si l'utilisateur appuie ou appuie longuement sur le lien URL. La mise en œuvre de cette méthode est facultative. Par défaut, la vue texte ouvre l'application chargée de gérer le type d'URL et lui transmet l'URL. Vous pouvez utiliser cette méthode pour déclencher une autre action, telle que l'affichage du contenu Web à l'URL dans une vue Web de l'application actuelle.

Important:

Les liens dans les vues de texte ne sont interactifs que si la vue de texte est sélectionnable mais non modifiable. Autrement dit, si la valeur de UITextView, la propriété sélectionnable est YES et la propriété isEditable est NO.

Rajan Balana
la source
Je suis heureux qu'ils aient ajouté cela à UITextViewDelegate.
fsaint
Malheureusement, vous finirez toujours par utiliser UIWebViewsi vous souhaitez créer un autre texte avec le lien et non l'URL elle-même. La <a>balise reste la meilleure solution dans ce cas.
MLQ
Si d'autres personnes voient cela, vous pouvez maintenant créer un autre texte sur le lien.
Schéma
10

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

ou si la cible est> = IOS 10

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
Ryan Heitner
la source
7

Avec Swift 5 et iOS 12, vous pouvez utiliser l'un des trois modèles suivants pour interagir avec les liens dans un fichier UITextView.


#1. Utilisation de UITextViewla dataDetectorTypespropriété.

Le moyen le plus simple d'interagir avec les numéros de téléphone, les URL ou les adresses dans un UITextViewest d'utiliser une dataDetectorTypespropriété. L'exemple de code ci-dessous montre comment l'implémenter. Avec ce code, lorsque l'utilisateur appuie sur le numéro de téléphone, un UIAlertControllermessage apparaît.

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. Utilisation de UITextViewDelegatela textView(_:shouldInteractWith:in:interaction:)méthode de

Si vous souhaitez effectuer une action personnalisée au lieu de créer une UIAlertControllerfenêtre contextuelle lorsque vous appuyez sur un numéro de téléphone lors de l'utilisation dataDetectorTypes, vous devez vous UIViewControllerconformer au UITextViewDelegateprotocole et à l'implémenter textView(_:shouldInteractWith:in:interaction:). Le code ci-dessous montre comment l'implémenter:

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. Utilisation NSAttributedStringetNSAttributedString.Key.link

Comme alternative, vous pouvez utiliser NSAttributedStringet définir un URLpour son NSAttributedString.Key.linkattribut. L'exemple de code ci-dessous en montre une implémentation possible. Avec ce code, lorsque l'utilisateur appuie sur la chaîne attribuée, un UIAlertControllermessage apparaît.

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
    }

}
Imanou Petit
la source
6

Version Swift:

Votre configuration UITextView standard devrait ressembler à quelque chose comme ceci, n'oubliez pas le délégué et les 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)

À la fin de votre cours, ajoutez cette pièce:

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
    }
}
Esqarrouth
la source
1

Swift 4:

1) Créez la classe suivante (sous-classée 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) Partout où vous configurez votre texte, procédez comme suit:

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


Si vous utilisez un storyboard, assurez-vous que votre texte ressemble à ceci dans l'inspecteur d'identité du volet droit:
entrez la description de l'image ici



Voila! Maintenant, vous obtenez le lien appuyez immédiatement au lieu de quand l'URL devrait interagir avec la méthode URL

Josh O'Connor
la source
aussi: si vous voulez que l'url ne soit pas gérée, définissez simplement la méthode shouldInteractWith pour qu'elle renvoie false
Josh O'Connor
Pensez que cela pose un tas de problèmes, comme ce qui se passe lorsque vous n'appuyez pas sur un lien. Ie je ne pense pas que la vue de texte fonctionnera plus normalement, puisque nil est retourné. La sélection change également lorsque vous appuyez sur un lien, car dans ce cas, self est renvoyé.
Drew McCormack
fonctionne très bien pour moi, vous devez gérer de tels cas pour vos besoins
Josh O'Connor
faible var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa
@vyachaslav pas pour moi, vous devez faire quelque chose de mal
Josh O'Connor
0

Je n'ai pas essayé cela moi-même, mais vous pouvez essayer d'implémenter la application:handleOpenURL:méthode dans votre délégué d'application - il semble que toutes les openURLdemandes passent par ce rappel.

Vladimir
la source
0

Je ne sais pas comment vous intercepteriez la liaison de données détectée ou quel type de fonction vous devez exécuter. Mais vous pourrez peut-être utiliser la méthode didBeginEditing TextField pour exécuter un test / scan à travers le champ de texte si vous savez ce que vous recherchez ... comme comparer des chaînes de texte qui correspondent au format ### - ### - ####, ou commencez par "www." pour saisir ces champs, mais vous auriez besoin d'écrire un peu de code pour renifler la chaîne textfields, reconiser ce dont vous avez besoin, puis l'extraire pour l'utilisation de votre fonction. Je ne pense pas que ce serait si difficile, une fois que vous avez défini exactement ce que vous vouliez et que vous avez ensuite concentré vos filtres d'instruction if () sur un modèle de correspondance très spécifique de ce dont vous aviez besoin.

Bien entendu, cela implique que l'utilisateur va toucher la zone de texte pour activer didBeginEditing (). Si ce n'est pas le type d'interaction utilisateur que vous recherchiez, vous pouvez simplement utiliser un déclencheur Timer, qui démarre sur ViewDidAppear () ou autre en fonction des besoins et traverse la chaîne textfields, puis à la fin de vous exécutez la chaîne textfield méthodes que vous avez créées, il vous suffit de désactiver la minuterie.

Newbyman
la source
0

application:handleOpenURL:est appelée lorsqu'une autre application ouvre votre application en ouvrant une URL avec un schéma pris en charge par votre application. Il n'est pas appelé lorsque votre application commence à ouvrir une URL.

Je pense que la seule façon de faire ce que Vladimir veut est d'utiliser un UIWebView au lieu d'un UITextView. Faites en sorte que votre contrôleur de vue implémente UIWebViewDelegate, définissez le délégué de UIWebView sur le contrôleur de vue et, dans l'implémentation du contrôleur de vue, webView:shouldStartLoadWithRequest:navigationType:pour ouvrir [request URL]dans une vue au lieu de quitter votre application et de l'ouvrir dans Mobile Safari.

A. Jesse Jiryu Davis
la source