Как перехватить клик по ссылке в UITextView?
-
23-09-2019 - |
Вопрос
Возможно ли выполнить пользовательское действие, когда пользователь нажимает на автоматически обнаруженную телефонную ссылку в UITextView.Пожалуйста, не советуйте использовать вместо этого UIWebView.
И, пожалуйста, не просто повторяйте текст из справочника Apple classes - конечно, я его уже читал.
Спасибо.
Решение
Обновить:От ios10,
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange interaction:(UITextItemInteraction)interaction;
От ios7 и Позже UITextView
имеет метод делегирования:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange *NS_DEPRECATED_IOS(7_0, 10_0, "Use textView:shouldInteractWithURL:inRange:forInteractionType: instead");*
перехватывать переходы по ссылкам.И это лучший способ сделать это.
Для ios6 и ранее хорошим способом сделать это было создание подкласса 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);
Это сделало свое дело за меня.
Другие советы
В iOS 7 или более поздней версии
Вы можете использовать следующий метод делегирования UITextView:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
Текстовое представление вызывает этот метод, если пользователь нажимает или долго нажимает на URL-ссылку.Реализация этого метода необязательна.По умолчанию текстовое представление открывает приложение, ответственное за обработку типа URL, и передает ему URL.Вы можете использовать этот метод для запуска альтернативного действия, такого как отображение веб-содержимого по URL-адресу в веб-представлении в текущем приложении.
Важный:
Ссылки в текстовых представлениях являются интерактивными, только если текстовое представление выбирается, но не редактируется.То есть, если значение UITextView выбираемого свойства равно YES, а редактируемого свойства равно NO.
Для 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
}
}
или, если target >= IOS 10
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool
Быстрая версия:
Ваша стандартная настройка UITextView должна выглядеть примерно так, не забудьте о delegate и 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
}
}
В Swift 5 и iOS 12 вы можете использовать один из трех следующих шаблонов для взаимодействия со ссылками в UITextView
.
#1.Используя UITextView
's dataDetectorTypes
собственность.
Самый простой способ взаимодействия с телефонными номерами, URL-адресами или 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
}
}
Я сам этого не пробовал, но вы можете попробовать реализовать application:handleOpenURL:
метод в вашем приложении delegate - он выглядит как все openURL
запрос проходит через этот обратный вызов.
Не уверен, как вы будете перехватывать обнаруженный канал передачи данных или какой тип функции вам нужно запустить.Но вы можете использовать метод didBeginEditing TextField для запуска теста / сканирования через текстовое поле, если вы знаете, что ищете .. например, сравнивая текстовые строки, которые соответствуют ###-###-#### форматируйте или начинайте с "www.", чтобы захватить эти поля, но вам нужно будет написать небольшой код, чтобы просмотреть строку textfields, повторно определить, что вам нужно, а затем извлечь это для использования вашей функцией.Я не думаю, что это было бы так сложно, как только вы точно определили, что именно вам нужно, а затем сфокусировали свои фильтры оператора if () на очень конкретном шаблоне соответствия тому, что вам нужно.
Конечно, это подразумевает, что пользователь собирается коснуться текстового поля, чтобы активировать функцию didBeginEditing().Если это не тот тип взаимодействия с пользователем, который вы искали, вы могли бы просто использовать таймер запуска, который запускается в viewDidAppear() или другом, основанном на необходимости, и выполняется через строку textfields, затем в конце вы запускаете построчные методы textfield, которые вы создали, вы просто выключаете таймер.
application:handleOpenURL:
вызывается при открытии другого приложения ваш приложение, открыв URL-адрес со схемой, поддерживаемой вашим приложением.Он не вызывается, когда ваше приложение начинает открывать URL-адрес.
Я думаю, что единственный способ сделать то, что хочет Владимир, - это использовать UIWebView вместо UITextView.Сделайте так, чтобы ваш контроллер представления реализовал UIWebViewDelegate, установите делегат UIWebView для контроллера представления и в контроллере представления реализуйте webView:shouldStartLoadWithRequest:navigationType:
чтобы открыть [request URL]
в представлении вместо того, чтобы выходить из своего приложения и открывать его в мобильном Safari.
Быстрый 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")
}
}
Если вы используете раскадровку, убедитесь, что ваш textview выглядит следующим образом в инспекторе идентификации правой панели:
Вуаля!Теперь вы получаете ссылку tap немедленно, а не тогда, когда URL должен взаимодействовать с методом URL