Detección de toques en texto atribuido con Swift
A veces para los principiantes es un poco difícil saber cómo configurar las cosas (de todos modos fue para mí), por lo que este ejemplo es un poco más completo.
Agregue un UITextView
a su proyecto.
Toma de corriente
Conecte el UITextView
al ViewController
con una toma de corriente con nombre textView
.
Atributo personalizado
Vamos a hacer un atributo personalizado haciendo una Extensión .
Nota: Este paso es técnicamente opcional, pero si no lo hace, deberá editar el código en la siguiente parte para usar un atributo estándar como NSAttributedString.Key.foregroundColor
. La ventaja de utilizar un atributo personalizado es que puede definir qué valores desea almacenar en el rango de texto atribuido.
Agregue un nuevo archivo swift con Archivo> Nuevo> Archivo ...> iOS> Fuente> Archivo Swift . Puedes llamarlo como quieras. Estoy llamando al mío NSAttributedStringKey + CustomAttribute.swift .
Pega el siguiente código:
import Foundation
extension NSAttributedString.Key {
static let myAttributeName = NSAttributedString.Key(rawValue: "MyCustomAttribute")
}
Código
Reemplace el código en ViewController.swift con lo siguiente. Tenga en cuenta el UIGestureRecognizerDelegate
.
import UIKit
class ViewController: UIViewController, UIGestureRecognizerDelegate {
@IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
// Create an attributed string
let myString = NSMutableAttributedString(string: "Swift attributed text")
// Set an attribute on part of the string
let myRange = NSRange(location: 0, length: 5) // range of "Swift"
let myCustomAttribute = [ NSAttributedString.Key.myAttributeName: "some value"]
myString.addAttributes(myCustomAttribute, range: myRange)
textView.attributedText = myString
// Add tap gesture recognizer to Text View
let tap = UITapGestureRecognizer(target: self, action: #selector(myMethodToHandleTap(_:)))
tap.delegate = self
textView.addGestureRecognizer(tap)
}
@objc func myMethodToHandleTap(_ sender: UITapGestureRecognizer) {
let myTextView = sender.view as! UITextView
let layoutManager = myTextView.layoutManager
// location of tap in myTextView coordinates and taking the inset into account
var location = sender.location(in: myTextView)
location.x -= myTextView.textContainerInset.left;
location.y -= myTextView.textContainerInset.top;
// character index at tap location
let characterIndex = layoutManager.characterIndex(for: location, in: myTextView.textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
// if index is valid then do something.
if characterIndex < myTextView.textStorage.length {
// print the character index
print("character index: \(characterIndex)")
// print the character at the index
let myRange = NSRange(location: characterIndex, length: 1)
let substring = (myTextView.attributedText.string as NSString).substring(with: myRange)
print("character at index: \(substring)")
// check if the tap location has a certain attribute
let attributeName = NSAttributedString.Key.myAttributeName
let attributeValue = myTextView.attributedText?.attribute(attributeName, at: characterIndex, effectiveRange: nil)
if let value = attributeValue {
print("You tapped on \(attributeName.rawValue) and the value is: \(value)")
}
}
}
}
Ahora, si toca la "w" de "Swift", debería obtener el siguiente resultado:
character index: 1
character at index: w
You tapped on MyCustomAttribute and the value is: some value
Notas
- Aquí utilicé un atributo personalizado, pero podría haber sido igual de fácil
NSAttributedString.Key.foregroundColor
(color de texto) que tiene un valor de UIColor.green
.
- Anteriormente, la vista de texto no podía ser editable o seleccionable, pero en mi respuesta actualizada para Swift 4.2 parece funcionar bien sin importar si están seleccionados o no.
Estudio adicional
Esta respuesta se basó en varias otras respuestas a esta pregunta. Además de estos, ver también