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.

Vladimir
fonte

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.

fraco
fonte
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.

Rajan Balana
fonte
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
Ryan Heitner
fonte
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
    }

}
Imanou Petit
fonte
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
    }
}
Esqarrouth
fonte
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

Josh O'Connor
fonte
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.

Vladimir
fonte
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.

Newbyman
fonte
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.

A. Jesse Jiryu Davis
fonte