Como interceptar clique no link no UITextView?


101

É possível realizar uma ação personalizada quando o usuário toca no link do telefone detectado automaticamente no UITextView. Não aconselhe usar o UIWebView em vez disso.

E, por favor, não repita apenas o texto de referência de classes da apple - certamente eu já li isso.

Obrigado.

Respostas:


144

Atualização: De ,

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

De and Later UITextViewtem o método delegate:

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

para interceptar os cliques em links. E esta é a melhor forma de o fazer.

Para e antes, uma boa maneira de fazer isso é subclassificando UIApplicatione substituindo o-(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

Você precisará implementar openURL:em seu delegado.

Agora, para que o aplicativo comece com sua nova subclasse de UIApplication, localize o arquivo main.m em seu projeto. Nesse pequeno arquivo que inicializa seu aplicativo, geralmente há esta linha:

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

O terceiro parâmetro é o nome da classe do seu aplicativo. Então, substituindo esta linha por:

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

Isso funcionou para mim.


Finalmente uma resposta real! Ideia simples e legal. Obrigado, funciona. Foi há muito tempo, então já consegui sem ele. Ainda pode ajudar no futuro. No entanto, uma pequena correção, o resultado de super em outro ramo deve ser retornado: return [super openURL: url];
Vladimir

2
Você também pode categorizar UIApplicatione substituir a implementação openURL. Embora dessa forma seja complicado (mas não impossível) fazer referência à implementação original.
mxcl

1
Para sua informação - postei um BrowserViewController no GitHub que implementa totalmente isso, bem como links de suporte clicados em um UIWebView aqui: github.com/nbuggia/Browser-View-Controller--iPhone- .
Nathan Buggia

3
Isso parece funcionar apenas para links da web, não para números de telefone autoformatados.
azdev

Em que método posso definir o delegado do meu aplicativo?
ratsimihah

50

No iOS 7 ou posterior

Você pode usar o seguinte método de delegado UITextView:

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

A visualização de texto chama esse método se o usuário tocar ou manter pressionado o link do URL. A implementação deste método é opcional. Por padrão, a visualização de texto abre o aplicativo responsável por manipular o tipo de URL e passa a URL. Você pode usar este método para acionar uma ação alternativa, como exibir o conteúdo da web no URL em uma visualização da web no aplicativo atual.

Importante:

Os links em visualizações de texto são interativos apenas se a visualização de texto for selecionável, mas não editável. Ou seja, se o valor de UITextView, a propriedade selecionável for YES e a propriedade isEditable for NO.


Estou feliz que eles adicionaram isso ao UITextViewDelegate.
fraco

Infelizmente, você ainda vai acabar usando UIWebViewse quiser fazer com que algum outro texto seja o link e não a própria URL. A <a>etiqueta ainda é a melhor maneira de ir nesse caso.
MLQ

Caso outras pessoas vejam isso, agora você pode transformar outro texto no link.
Schemetrical

10

Para 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 se o alvo for> = IOS 10

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool

7

Com o Swift 5 e iOS 12, você pode usar um dos três padrões a seguir para interagir com links em a UITextView.


# 1. Usando UITextViewa dataDetectorTypespropriedade de.

A maneira mais simples de interagir com números de telefone, urls ou endereços em um UITextView é usar dataDetectorTypespropriedade. O código de exemplo abaixo mostra como implementá-lo. Com este código, quando o usuário toca no número do telefone, um UIAlertControlleraparece.

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. Usando UITextViewDelegateo textView(_:shouldInteractWith:in:interaction:)método de

Se quiser realizar alguma ação personalizada em vez de fazer um UIAlertControllerpop-up ao tocar em um número de telefone durante o uso dataDetectorTypes, você deve fazer o seuUIViewController conformidade com o UITextViewDelegateprotocolo e implementar textView(_:shouldInteractWith:in:interaction:). O código abaixo mostra como implementá-lo:

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. Usando NSAttributedStringeNSAttributedString.Key.link

Como alternativa, você pode usar NSAttributedStringe definir um URLpara seu NSAttributedString.Key.linkatributo. O código de exemplo abaixo mostra uma possível implementação dele. Com este código, quando o usuário toca na string atribuída, um UIAlertControlleraparece.

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

Versão Swift:

Sua configuração UITextView padrão deve ser semelhante a isto, não se esqueça do delegate e 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)

Depois que sua aula terminar, adicione esta peça:

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

Swift 4:

1) Crie a seguinte classe (subclasse 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) Onde quer que você configure sua visualização de texto, faça o seguinte:

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


Se você estiver usando um storyboard, certifique-se de que sua visualização de texto tenha a seguinte aparência no painel direito do inspetor de identidade:
insira a descrição da imagem aqui



Voila! Agora você obtém o link toque imediatamente em vez de quando o URL deve Interactar com o método de URL


também: se você quiser que o url não seja manipulado, apenas defina o método shouldInteractWith para retornar falso
Josh O'Connor

Acho que isso tem vários problemas, como o que acontece quando você não toca em um link. Ou seja, não acho que a visualização de texto funcionará mais normalmente, pois nil está sendo retornado. A seleção também muda quando você toca em um link, porque, nesse caso, self é retornado.
Drew McCormack

funciona muito bem para mim, você precisa lidar com esses casos para suas necessidades
Josh O'Connor

fraco var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa

@vyachaslav não é para mim, você deve estar fazendo algo errado
Josh O'Connor

0

Eu não tentei fazer isso, mas você pode tentar implementar o application:handleOpenURL:método em seu delegado de aplicativo - parece que todas as openURLsolicitações passam por este retorno de chamada.


0

Não tenho certeza de como você interceptaria o link de dados detectado ou que tipo de função você precisa executar. Mas você pode ser capaz de utilizar o método didBeginEditing TextField para executar um teste / varredura através do campo de texto se você souber o que está procurando ... como comparar strings de texto que atendem ao formato ### - ### - ####, ou comece com "www." para pegar esses campos, mas você precisaria escrever um pequeno código para farejar a string textfields, reconectar o que você precisa e, em seguida, extraí-lo para uso de sua função. Eu não acho que isso seria tão difícil, uma vez que você restringiu exatamente o que você queria e então focou seus filtros de instrução if () em um padrão de correspondência muito específico do que você precisava.

Obviamente, isso implica que o usuário irá tocar na caixa de texto para ativar o didBeginEditing (). Se esse não for o tipo de interação do usuário que você está procurando, você pode usar um temporizador de gatilho, que começa em ViewDidAppear () ou outro com base na necessidade e percorre a string textfields, então, no final de você, execute a string textfield métodos que você construiu, basta desligar o cronômetro.


0

application:handleOpenURL:é chamado quando outro aplicativo abre seu aplicativo abrindo uma URL com um esquema que seu aplicativo suporta. Não é chamado quando seu aplicativo começa a abrir um URL.

Acho que a única maneira de fazer o que Vladimir deseja é usar um UIWebView em vez de um UITextView. Faça seu controlador de visualização implementar UIWebViewDelegate, definir o delegado de UIWebView para o controlador de visualização e, no controlador de visualização, implementar webView:shouldStartLoadWithRequest:navigationType:para abrir [request URL]em uma visualização em vez de encerrar seu aplicativo e abri-lo no Mobile Safari.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.