จะสกัดกั้นการคลิกที่ลิงค์ใน UITextView ได้อย่างไร?


104

เป็นไปได้หรือไม่ที่จะดำเนินการแบบกำหนดเองเมื่อผู้ใช้แตะลิงก์โทรศัพท์ที่ตรวจจับอัตโนมัติใน UITextView โปรดอย่าแนะนำให้ใช้ UIWebView แทน

และโปรดอย่าทำซ้ำข้อความจากการอ้างอิงคลาสของ apple - แน่นอนฉันได้อ่านแล้ว

ขอบคุณ.

คำตอบ:


147

ปรับปรุง: จาก ,

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

จาก และในภายหลังUITextViewมีวิธีการมอบหมาย:

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

เพื่อสกัดกั้นการคลิกไปยังลิงก์ และนี่คือวิธีที่ดีที่สุดที่จะทำ

สำหรับ และก่อนหน้านี้วิธีที่ดีในการทำเช่นนี้คือการคลาสย่อยUIApplicationและเขียนทับไฟล์-(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

คุณจะต้องดำเนินการopenURL:ในตัวแทนของคุณ

ตอนนี้เพื่อให้แอปพลิเคชันเริ่มต้นด้วยคลาสย่อยใหม่ของคุณUIApplicationให้ค้นหาไฟล์ main.m ในโครงการของคุณ ในไฟล์ขนาดเล็กที่บูตสแตรปแอปของคุณมักจะมีบรรทัดนี้:

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

พารามิเตอร์ที่สามคือชื่อคลาสสำหรับแอปพลิเคชันของคุณ ดังนั้นแทนที่บรรทัดนี้สำหรับ:

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

นี้ได้เคล็ดลับสำหรับฉัน.


คำตอบสุดท้ายที่แท้จริง! ความคิดที่เรียบง่ายและยอดเยี่ยม ขอบคุณมันได้ผล มันนานมาแล้วดังนั้นฉันจึงจัดการโดยที่ไม่มีมันอยู่แล้ว ยังอาจช่วยได้ในอนาคต แม้ว่าการแก้ไขเพียงเล็กน้อยควรส่งคืนผลลัพธ์จาก super in else branch: return [super openURL: url];
Vladimir

2
คุณยังสามารถจัดหมวดหมู่UIApplicationและแทนที่การใช้งาน openURL แม้ว่าวิธีนี้จะเป็นเรื่องยุ่งยาก (แต่ไม่เป็นไปไม่ได้) ในการอ้างอิงการใช้งานดั้งเดิม
mxcl

1
FYI - ผมโพสต์ BrowserViewController บน GitHub อย่างเต็มที่ดำเนินการนี้เช่นเดียวกับการเชื่อมโยงการสนับสนุนการคลิกจากภายใน UIWebView ที่นี่: github.com/nbuggia/Browser-View-Controller--iPhone-
Nathan Buggia

3
ดูเหมือนว่าจะใช้ได้กับลิงก์เว็บเท่านั้นไม่ใช่หมายเลขโทรศัพท์ที่ฟอร์แมตอัตโนมัติ
azdev

ฉันจะตั้งค่าตัวแทนของแอปพลิเคชันของฉันได้อย่างไร?
ratsimihah

51

ใน iOS 7 หรือใหม่กว่า

คุณสามารถใช้วิธีการมอบสิทธิ์ UITextView ต่อไปนี้:

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

มุมมองข้อความเรียกวิธีนี้หากผู้ใช้แตะหรือกดลิงก์ URL ค้างไว้ การใช้วิธีนี้เป็นทางเลือก ตามค่าเริ่มต้นมุมมองข้อความจะเปิดแอปพลิเคชันที่รับผิดชอบในการจัดการประเภท URL และส่งผ่าน URL นั้น คุณสามารถใช้วิธีนี้เพื่อทริกเกอร์การดำเนินการอื่นเช่นการแสดงเนื้อหาเว็บที่ URL ในมุมมองเว็บภายในแอปพลิเคชันปัจจุบัน

สิ่งสำคัญ:

ลิงก์ในมุมมองข้อความเป็นแบบโต้ตอบได้ก็ต่อเมื่อเลือกมุมมองข้อความได้ แต่แก้ไขไม่ได้ นั่นคือถ้าค่าของ UITextView คุณสมบัติที่เลือกได้คือ YES และคุณสมบัติ isEditable คือ NO


ฉันดีใจที่พวกเขาเพิ่มสิ่งนี้ใน UITextViewDelegate
fsaint

น่าเสียดายที่คุณจะยังคงใช้UIWebViewหากคุณต้องการสร้างข้อความอื่นให้เป็นลิงก์ไม่ใช่ URL นั้นเอง <a>แท็กยังคงเป็นวิธีที่ดีที่สุดที่จะไปในกรณีที่ว่า
MLQ

ในกรณีที่คนอื่นเห็นสิ่งนี้คุณสามารถสร้างลิงก์ข้อความอื่นได้แล้ว
Schemetrical

10

สำหรับ 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
    }
}

หรือถ้าเป้าหมายคือ> = IOS 10

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

8

ด้วย Swift 5 และ iOS 12 คุณสามารถใช้หนึ่งในสามรูปแบบต่อไปนี้เพื่อโต้ตอบกับลิงก์ในไฟล์UITextView.


# 1. การใช้UITextViewของdataDetectorTypesสถานที่ให้บริการ

วิธีที่ง่ายที่สุดในการโต้ตอบกับหมายเลขโทรศัพท์ URL หรือที่อยู่ใน a UITextViewคือการใช้dataDetectorTypesคุณสมบัติ โค้ดตัวอย่างด้านล่างแสดงวิธีการใช้งาน ด้วยรหัสนี้เมื่อผู้ใช้แตะที่หมายเลขโทรศัพท์UIAlertControllerจะปรากฏขึ้น

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. ใช้UITextViewDelegate's textView(_:shouldInteractWith:in:interaction:)วิธี

หากคุณต้องการที่จะดำเนินการที่กำหนดเองแทนการUIAlertControllerปรากฏขึ้นเมื่อคุณแตะที่หมายเลขโทรศัพท์ในขณะที่ใช้dataDetectorTypesคุณจะต้องทำให้UIViewControllerสอดคล้องกับโปรโตคอลและการดำเนินการUITextViewDelegate textView(_:shouldInteractWith:in:interaction:)โค้ดด้านล่างแสดงวิธีการใช้งาน:

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. การใช้NSAttributedStringและNSAttributedString.Key.link

คุณสามารถใช้NSAttributedStringและตั้งค่าแอตทริบิวต์URLสำหรับNSAttributedString.Key.linkแอตทริบิวต์ได้อีกทางเลือกหนึ่งโค้ดตัวอย่างด้านล่างแสดงการใช้งานที่เป็นไปได้ ด้วยรหัสนี้เมื่อผู้ใช้แตะที่สตริงที่เป็นที่มาUIAlertControllerจะปรากฏขึ้น

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

เวอร์ชัน Swift:

การตั้งค่า UITextView มาตรฐานของคุณควรมีลักษณะดังนี้อย่าลืมผู้รับมอบสิทธิ์และ 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)

หลังจากจบชั้นเรียนให้เพิ่มชิ้นส่วนนี้:

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

2

Swift 4:

1) สร้างคลาสต่อไปนี้ (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) ทุกที่ที่คุณตั้งค่า textview ให้ทำสิ่งนี้:

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


หากคุณใช้สตอรีบอร์ดตรวจสอบให้แน่ใจว่ามุมมองข้อความของคุณมีลักษณะเช่นนี้ในตัวตรวจสอบข้อมูลประจำตัวของบานหน้าต่างด้านขวา:
ป้อนคำอธิบายภาพที่นี่



โวลา! ตอนนี้คุณจะได้รับลิงค์แตะทันทีแทนที่จะเป็นเมื่อ URL shouldInteractWith URL วิธี


ด้วย: หากคุณไม่ต้องการให้ URL ถูกจัดการเพียงแค่ตั้งค่าวิธี shouldInteractWith เพื่อส่งคืนเท็จ
Josh O'Connor

คิดว่าปัญหานี้มีหลายประการเช่นจะเกิดอะไรขึ้นเมื่อคุณไม่แตะลิงก์ คือฉันไม่คิดว่ามุมมองข้อความจะทำงานได้ตามปกติอีกต่อไปเนื่องจากไม่มีการส่งคืน การเลือกจะเปลี่ยนไปด้วยเมื่อคุณแตะลิงก์เพราะในกรณีนี้ระบบจะส่งคืนตัวเอง
Drew McCormack

ใช้งานได้ดีสำหรับฉันคุณต้องจัดการกรณีดังกล่าวตามความต้องการของคุณ
Josh O'Connor

อ่อนแอ var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa

@vyachaslav ไม่ใช่สำหรับฉันคุณต้องทำอะไรผิดพลาด
Josh O'Connor

0

ฉันยังไม่ได้ลองใช้ตัวเอง แต่คุณสามารถลองใช้application:handleOpenURL:วิธีการในตัวแทนแอปพลิเคชันของคุณได้ดูเหมือนว่าopenURLคำขอทั้งหมดจะผ่านการติดต่อกลับนี้


0

ไม่แน่ใจว่าคุณจะสกัดกั้นการเชื่อมโยงข้อมูลที่ตรวจพบได้อย่างไรหรือฟังก์ชันประเภทใดที่คุณต้องเรียกใช้ แต่คุณอาจสามารถใช้เมธอด didBeginEditing TextField เพื่อเรียกใช้การทดสอบ / สแกนผ่านฟิลด์ข้อความหากคุณรู้ว่ากำลังมองหาอะไร.. เช่นการเปรียบเทียบสตริงข้อความที่ตรงตามรูปแบบ ### - ### - #### หรือขึ้นต้นด้วย "www." เพื่อคว้าฟิลด์เหล่านั้น แต่คุณจะต้องเขียนโค้ดเล็กน้อยเพื่อสูดดมผ่านสตริงฟิลด์ข้อความกำหนดสิ่งที่คุณต้องการใหม่จากนั้นแยกออกมาเพื่อใช้งานฟังก์ชันของคุณ ฉันไม่คิดว่ามันจะยากขนาดนั้นเมื่อคุณ จำกัด สิ่งที่คุณต้องการให้แคบลงอย่างแน่นอนจากนั้นจึงเน้นคำสั่ง if () ของคุณกรองลงไปตามรูปแบบการจับคู่ที่เฉพาะเจาะจงมากสำหรับสิ่งที่คุณต้องการ

จาก couse หมายความว่าผู้ใช้จะแตะกล่องข้อความเพื่อเปิดใช้งาน didBeginEditing () หากนั่นไม่ใช่ประเภทของการโต้ตอบกับผู้ใช้ที่คุณกำลังมองหาคุณสามารถใช้ตัวจับเวลาทริกเกอร์ที่เริ่มต้นใน ViewDidAppear () หรืออื่น ๆ ตามความต้องการและทำงานผ่านสตริงฟิลด์ข้อความจากนั้นในตอนท้ายของคุณจะเรียกใช้สตริงฟิลด์ข้อความ วิธีการที่คุณสร้างขึ้นคุณเพียงแค่ปิดตัวจับเวลา


0

application:handleOpenURL:เรียกว่าเมื่อ app อื่นเปิดของคุณโดยการเปิด URL ด้วยรูปแบบที่แอปของคุณรองรับ ไม่เรียกว่าเมื่อแอปของคุณเริ่มเปิด URL

ฉันคิดว่าวิธีเดียวที่จะทำในสิ่งที่ Vladimir ต้องการคือใช้ UIWebView แทน UITextView ทำให้ตัวควบคุมมุมมองของคุณใช้ UIWebViewDelegate ตั้งค่าผู้รับมอบสิทธิ์ของ UIWebView เป็นตัวควบคุมมุมมองและในตัวควบคุมมุมมองใช้webView:shouldStartLoadWithRequest:navigationType:เพื่อเปิด[request URL]ในมุมมองแทนที่จะออกจากแอปของคุณและเปิดใน Mobile Safari

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.