Làm thế nào để chặn nhấp chuột vào liên kết trong UITextView?


101

Có thể thực hiện hành động tùy chỉnh khi người dùng chạm vào liên kết điện thoại được phát hiện tự động trong UITextView không. Xin đừng khuyên bạn nên sử dụng UIWebView để thay thế.

Và xin đừng chỉ lặp lại văn bản từ tham chiếu lớp apple - chắc chắn tôi đã đọc nó rồi.

Cảm ơn.

Câu trả lời:


144

Cập nhật: Từ ,

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

Từ và Sau đó UITextViewcó phương thức ủy nhiệm:

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

để chặn các nhấp chuột vào liên kết. Và đây là cách tốt nhất để làm điều đó.

Đối với và trước đó, một cách hay để làm điều này là bằng cách phân lớp UIApplicationvà ghi đè-(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

Bạn sẽ cần phải triển khai openURL:trong ủy quyền của mình.

Bây giờ, để ứng dụng bắt đầu với lớp con mới của bạn UIApplication, hãy định vị tệp main.m trong dự án của bạn. Trong tệp nhỏ khởi động ứng dụng của bạn, thường có dòng sau:

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

Tham số thứ ba là tên lớp cho ứng dụng của bạn. Vì vậy, thay thế dòng này cho:

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

Điều này đã làm các mẹo cho tôi.


Tại câu trả lời thực cuối cùng! Ý tưởng đơn giản và mát mẻ. Cảm ơn, nó hoạt động. Nó đã lâu rồi, vì vậy tôi đã xoay sở mà không có nó. Vẫn có thể hữu ích trong tương lai. Tuy nhiên, điều chỉnh nhỏ, kết quả từ nhánh super trong else phải được trả về: return [super openURL: url];
Vladimir

2
Bạn cũng có thể phân loại UIApplicationvà thay thế việc triển khai openURL. Mặc dù theo cách này, rất khó (nhưng không phải là không thể) để tham chiếu việc triển khai ban đầu.
mxcl

1
FYI - Tôi đã đăng một BrowserViewController trên GitHub triển khai đầy đủ điều này, cũng như hỗ trợ các liên kết được nhấp từ bên trong UIWebView tại đây: github.com/nbuggia/Browser-View-Controller--iPhone- .
Nathan Buggia

3
Điều này dường như chỉ hoạt động đối với các liên kết web, không phải số điện thoại được định dạng tự động.
azdev

Tôi có thể đặt người đại diện cho ứng dụng của mình bằng phương pháp nào?
Ratimihah,

50

Trong iOS 7 trở lên

Bạn có thể sử dụng Phương pháp ủy quyền UITextView sau:

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

Chế độ xem văn bản gọi phương thức này nếu người dùng nhấn hoặc nhấn và giữ liên kết URL. Việc thực hiện phương pháp này là tùy chọn. Theo mặc định, chế độ xem văn bản sẽ mở ứng dụng chịu trách nhiệm xử lý loại URL và chuyển URL cho nó. Bạn có thể sử dụng phương pháp này để kích hoạt một hành động thay thế, chẳng hạn như hiển thị nội dung web tại URL trong chế độ xem web trong ứng dụng hiện tại.

Quan trọng:

Các liên kết trong chế độ xem văn bản chỉ tương tác nếu chế độ xem văn bản có thể chọn được nhưng không thể chỉnh sửa. Nghĩa là, nếu giá trị của UITextView thì thuộc tính selectable là CÓ và thuộc tính isEditable là KHÔNG.


Tôi rất vui vì họ đã thêm cái này vào UITextViewDelegate.
fsaint

Đáng tiếc là bạn vẫn sẽ kết thúc sử dụng UIWebViewnếu bạn muốn thực hiện một số văn bản khác các liên kết và không chính URL đó. Các <a>thẻ vẫn là cách tốt nhất để đi trong trường hợp đó.
Matthew Quiros

Trong trường hợp người khác nhìn thấy điều này, bây giờ bạn có thể đặt một số văn bản khác vào liên kết.
Schemetrical

10

Đối với 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
    }
}

hoặc nếu mục tiêu là> = IOS 10

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

6

Phiên bản Swift:

Thiết lập UITextView tiêu chuẩn của bạn sẽ trông giống như thế này, đừng quên ủy nhiệm và 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)

Sau khi lớp học của bạn kết thúc, hãy thêm phần này:

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

6

Với Swift 5 và iOS 12, bạn có thể sử dụng một trong ba mẫu sau để tương tác với các liên kết trong a UITextView.


# 1. Sử dụng UITextView's dataDetectorTypestài sản.

Cách đơn giản nhất để tương tác với số điện thoại, url hoặc địa chỉ trong a UITextViewlà sử dụng thuộc dataDetectorTypestính. Đoạn mã mẫu dưới đây cho biết cách triển khai nó. Với mã này, khi người dùng nhấn vào số điện thoại, một UIAlertControllercửa sổ bật lên.

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. Sử dụng UITextViewDelegate's textView(_:shouldInteractWith:in:interaction:)phương pháp

Nếu bạn muốn thực hiện một số hành động tùy chỉnh thay vì UIAlertControllerbật lên khi nhấn vào một số điện thoại trong khi sử dụng dataDetectorTypes, bạn phải UIViewControllertuân thủ UITextViewDelegategiao thức và triển khai textView(_:shouldInteractWith:in:interaction:). Đoạn mã dưới đây cho thấy cách triển khai nó:

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. Sử dụng NSAttributedStringNSAttributedString.Key.link

Là một thay thế, bạn có thể sử dụng NSAttributedStringvà thiết lập một URLcho mình NSAttributedString.Key.linkmẫu mã attribute.The dưới đây cho thấy một thi thể của nó. Với mã này, khi người dùng nhấn vào chuỗi phân bổ, một UIAlertControllercửa sổ bật lên.

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
    }

}

1

Swift 4:

1) Tạo lớp sau (UITextView phân lớp):

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) Bất cứ nơi nào bạn thiết lập chế độ xem văn bản của mình, hãy làm như sau:

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


Nếu bạn đang sử dụng bảng phân cảnh, hãy đảm bảo rằng chế độ xem văn bản của bạn trông giống như thế này trong trình kiểm tra danh tính bảng bên phải:
nhập mô tả hình ảnh ở đây



Thì đấy! Bây giờ bạn nhận được liên kết chạm ngay lập tức thay vì khi URL shouldInteractWith phương thức URL


thêm: nếu bạn muốn url để không bị xử lý, chỉ cần thiết lập các phương pháp shouldInteractWith để trả về false
Josh O'Connor

Hãy nghĩ rằng điều này có nhiều vấn đề, chẳng hạn như điều gì sẽ xảy ra khi bạn không nhấn vào một liên kết. Tức là tôi không nghĩ rằng chế độ xem văn bản sẽ hoạt động bình thường nữa, vì nil đang được trả về. Lựa chọn cũng sẽ thay đổi khi bạn nhấn vào một liên kết, vì trong trường hợp đó, bản thân sẽ được trả về.
Drew McCormack

hoạt động tuyệt vời đối với tôi, bạn cần phải xử lý những trường hợp này cho các nhu cầu của bạn
Josh O'Connor

yếu var linkDetectDelegate: QuickDetectLinkTextViewDelegate?
maslovsa

@vyachaslav không phải cho tôi, bạn phải làm gì đó sai
Josh O'Connor

0

Bản thân tôi chưa thử nhưng bạn có thể thử triển khai application:handleOpenURL:phương thức trong đại biểu ứng dụng của mình - có vẻ như tất cả openURLyêu cầu đều chuyển qua lệnh gọi lại này.


0

Không chắc chắn bạn sẽ chặn liên kết dữ liệu được phát hiện như thế nào hoặc loại chức năng bạn cần chạy. Nhưng bạn có thể sử dụng phương thức didBeginE Edit TextField để chạy kiểm tra / quét qua trường văn bản nếu bạn biết mình đang tìm kiếm gì..chẳng hạn như so sánh các chuỗi văn bản đáp ứng định dạng ### - ### - ####, hoặc bắt đầu bằng "www." để lấy các trường đó, nhưng bạn sẽ cần viết một đoạn mã nhỏ để dò tìm qua chuỗi trường văn bản, nhận lại những gì bạn cần và sau đó trích xuất nó để sử dụng cho chức năng của bạn. Tôi không nghĩ điều này sẽ khó đến vậy, một khi bạn thu hẹp chính xác những gì bạn muốn và sau đó tập trung vào các bộ lọc câu lệnh if () của bạn xuống thành mẫu đối sánh rất cụ thể về những gì bạn cần.

Điều này ngụ ý rằng người dùng sẽ chạm vào hộp văn bản để kích hoạt didBeginE Chỉnh sửa (). Nếu đó không phải là loại tương tác người dùng mà bạn đang tìm kiếm, bạn có thể chỉ cần sử dụng Bộ hẹn giờ kích hoạt, bắt đầu trên ViewDidAppear () hoặc loại khác dựa trên nhu cầu và chạy qua chuỗi textfields, sau đó chạy qua chuỗi textfield mà bạn đã xây dựng, bạn chỉ cần tắt Bộ hẹn giờ trở lại.


0

application:handleOpenURL:được gọi khi một ứng dụng khác mở ứng dụng của bạn bằng cách mở một URL có lược đồ mà ứng dụng của bạn hỗ trợ. Nó không được gọi khi ứng dụng của bạn bắt đầu mở một URL.

Tôi nghĩ rằng cách duy nhất để làm những gì Vladimir muốn là sử dụng UIWebView thay vì UITextView. Làm cho bộ điều khiển chế độ xem của bạn triển khai UIWebViewDelegate, đặt đại biểu của UIWebView thành bộ điều khiển chế độ xem và trong triển khai bộ điều khiển chế độ xem webView:shouldStartLoadWithRequest:navigationType:để mở [request URL]trong một chế độ xem thay vì thoát ứng dụng của bạn và mở nó trong Mobile Safari.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.