Bagaimana cara mencegat klik pada tautan di UITextView?

101

Apakah mungkin untuk melakukan tindakan kustom saat pengguna menyentuh link telepon yang terdeteksi otomatis di UITextView. Mohon jangan saran untuk menggunakan UIWebView sebagai gantinya.

Dan tolong jangan hanya mengulang teks dari referensi kelas apel - pasti saya sudah membacanya.

Terima kasih.

Vladimir
sumber

Jawaban:

144

Pembaruan: Dari ,

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

Dari dan Nanti UITextViewmemiliki metode delegasi:

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

untuk menghalangi klik ke tautan. Dan inilah cara terbaik untuk melakukannya.

Untuk dan sebelumnya cara yang bagus untuk melakukannya adalah dengan membuat subkelas UIApplicationdan menimpa file-(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

Anda perlu menerapkan openURL:di delegasi Anda.

Sekarang, untuk memulai aplikasi dengan subkelas baru Anda UIApplication, cari file main.m dalam proyek Anda. Dalam file kecil ini yang mem-bootstrap aplikasi Anda, biasanya ada baris ini:

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

Parameter ketiga adalah nama kelas untuk aplikasi Anda. Jadi, ganti baris ini untuk:

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

Ini melakukan trik untuk saya.

pingsan
sumber
Akhirnya jawaban nyata! Ide sederhana dan keren. Terima kasih, ini berhasil. Itu sudah lama sekali, jadi saya sudah berhasil tanpanya. Masih dapat membantu di masa depan. Koreksi kecil, hasil dari super di cabang lain harus dikembalikan: return [super openURL: url];
Vladimir
2
Anda juga dapat mengkategorikan UIApplicationdan mengganti implementasi openURL. Meskipun cara ini rumit (tetapi bukan tidak mungkin) untuk mereferensikan implementasi asli.
mxcl
1
FYI - Saya memposting BrowserViewController di GitHub yang sepenuhnya mengimplementasikan ini, serta tautan pendukung yang diklik dari dalam UIWebView di sini: github.com/nbuggia/Browser-View-Controller--iPhone- .
Nathan Buggia
3
Ini sepertinya hanya berfungsi untuk tautan web, bukan nomor telepon yang diformat otomatis.
azdev
Dalam metode apa saya dapat mengatur delegasi aplikasi saya?
ratsimihah
50

Di iOS 7 atau Nanti

Anda dapat menggunakan Metode delegasi UITextView berikut:

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

Tampilan teks memanggil metode ini jika pengguna mengetuk atau menekan lama tautan URL. Penerapan metode ini bersifat opsional. Secara default, tampilan teks membuka aplikasi yang bertanggung jawab untuk menangani jenis URL dan meneruskannya ke URL. Anda dapat menggunakan metode ini untuk memicu tindakan alternatif, seperti menampilkan konten web di URL dalam tampilan web dalam aplikasi saat ini.

Penting:

Tautan dalam tampilan teks bersifat interaktif hanya jika tampilan teks dapat dipilih tetapi tidak dapat diedit. Artinya, jika nilai UITextView properti yang dapat dipilih adalah YA dan properti isEditable adalah NO.

Rajan Balana
sumber
Saya senang mereka menambahkan ini ke UITextViewDelegate.
pingsan
Sayangnya, Anda tetap akan menggunakan UIWebViewjika ingin membuat teks lain sebagai tautan dan bukan URL itu sendiri. The <a>tag masih merupakan cara terbaik untuk pergi dalam kasus itu.
MLQ
Jika orang lain melihat ini, Anda sekarang dapat menjadikan teks lain sebagai tautan.
Skema
10

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

atau jika targetnya adalah> = IOS 10

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

Dengan Swift 5 dan iOS 12, Anda dapat menggunakan salah satu dari tiga pola berikut untuk berinteraksi dengan tautan di file UITextView.


# 1. Menggunakan UITextView's dataDetectorTypesproperti.

Cara termudah untuk berinteraksi dengan nomor telepon, url, atau alamat di a UITextViewadalah dengan menggunakan dataDetectorTypesproperti. Kode contoh di bawah ini menunjukkan cara menerapkannya. Dengan kode ini, ketika pengguna mengetuk nomor telepon, UIAlertControllerakan muncul.

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. Menggunakan UITextViewDelegate's textView(_:shouldInteractWith:in:interaction:)metode

Jika Anda ingin melakukan beberapa tindakan kustom alih-alih membuat UIAlertControllermunculan saat Anda mengetuk nomor telepon saat menggunakan dataDetectorTypes, Anda harus UIViewControllermenyesuaikan diri dengan UITextViewDelegateprotokol dan menerapkan textView(_:shouldInteractWith:in:interaction:). Kode di bawah ini menunjukkan bagaimana menerapkannya:

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. MenggunakanNSAttributedString danNSAttributedString.Key.link

Sebagai alternatif, Anda dapat menggunakan NSAttributedStringdan menetapkan a URLuntuk NSAttributedString.Key.linkatributnya. Kode contoh di bawah ini menunjukkan kemungkinan implementasinya. Dengan kode ini, ketika pengguna mengetuk string yang diatribusikan, sebuah UIAlertControllermuncul.

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
sumber
6

Versi Swift:

Setup UITextView standar Anda akan terlihat seperti ini, jangan lupa delegate dan 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)

Setelah kelas Anda berakhir, tambahkan bagian ini:

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
sumber
1

Cepat 4:

1) Buat kelas berikut (subclass 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) Di mana pun Anda menyiapkan textview, lakukan ini:

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


Jika Anda menggunakan storyboard, pastikan textview Anda terlihat seperti ini di panel kanan inspektur identitas:
masukkan deskripsi gambar di sini



Voila! Sekarang Anda mendapatkan tautan, ketuk segera, bukan kapan metode URL shouldInteractWith URL

Josh O'Connor
sumber
juga: jika Anda ingin url tidak ditangani, cukup setel metode shouldInteractWith untuk mengembalikan false
Josh O'Connor
Menurut Anda ini memiliki banyak masalah, seperti apa yang terjadi jika Anda tidak mengetuk tautan. Yaitu saya tidak berpikir tampilan teks akan berfungsi normal lagi, karena nol dikembalikan. Pilihan juga akan berubah ketika Anda mengetuk tautan, karena dalam hal ini, diri dikembalikan.
Drew McCormack
bekerja sangat baik untuk saya, Anda perlu menangani kasus seperti itu untuk kebutuhan Anda
Josh O'Connor
lemah var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa
@vyachaslav bukan untuk saya, Anda pasti melakukan sesuatu yang salah
Josh O'Connor
0

Saya belum mencobanya sendiri, tetapi Anda dapat mencoba menerapkan application:handleOpenURL:metode dalam delegasi aplikasi Anda - sepertinya semua openURLpermintaan melewati callback ini.

Vladimir
sumber
0

Tidak yakin bagaimana Anda akan mencegat tautan data yang terdeteksi, atau jenis fungsi apa yang perlu Anda jalankan. Tapi Anda mungkin bisa menggunakan metode didBeginEditing TextField untuk menjalankan tes / scan melalui textfield jika Anda tahu apa yang Anda cari..seperti membandingkan string teks yang memenuhi format ### - ### - ####, atau mulai dengan "www." untuk mengambil bidang tersebut, tetapi Anda perlu menulis sedikit kode untuk mengendus melalui string bidang teks, menyusun kembali apa yang Anda butuhkan, dan kemudian mengekstraknya untuk digunakan fungsi Anda. Saya tidak berpikir ini akan sesulit itu, setelah Anda mempersempit apa yang Anda inginkan dan kemudian memfokuskan pernyataan if () Anda ke pola pencocokan yang sangat spesifik dari apa yang Anda butuhkan.

Dari couse ini menyiratkan bahwa pengguna akan menyentuh kotak teks untuk mengaktifkan didBeginEditing (). Jika itu bukan jenis interaksi pengguna yang Anda cari, Anda bisa menggunakan Timer pemicu, yang dimulai pada ViewDidAppear () atau lainnya berdasarkan kebutuhan dan berjalan melalui string bidang teks, kemudian di akhir Anda menjalankan melalui string bidang teks metode yang Anda buat, Anda cukup mematikan Timer kembali.

Newbyman
sumber
0

application:handleOpenURL:dipanggil saat aplikasi lain membuka aplikasi Anda dengan membuka URL dengan skema yang didukung aplikasi Anda. Ini tidak dipanggil saat aplikasi Anda mulai membuka URL.

Saya pikir satu-satunya cara untuk melakukan apa yang diinginkan Vladimir adalah dengan menggunakan UIWebView, bukan UITextView. Jadikan pengontrol tampilan Anda mengimplementasikan UIWebViewDelegate, setel delegasi UIWebView ke pengontrol tampilan, dan di implementasi pengontrol tampilan webView:shouldStartLoadWithRequest:navigationType:untuk membuka [request URL]dalam tampilan alih-alih menutup aplikasi Anda dan membukanya di Mobile Safari.

A. Jesse Jiryu Davis
sumber