Je crée une application de chat et pour des raisons de performances, je dois utiliser UILabel au lieu de UITextView pour afficher les messages de chat. J'ai déjà utilisé TextView, mais la détection des données sur le défilement est très lente et saccadée.
Le problème est qu'il n'y a actuellement aucune détection de lien/téléphone/adresse etc ... pour UILabels.
Comment puis-je savoir où un lien ou un numéro de téléphone existe dans une chaîne, puis le mettre en surbrillance et le rendre cliquable dans un UILabel?
J'ai lu de nombreux articles sur la façon d'ajouter des attributs pour les liens pour faire exactement cela, mais ce sont tous des liens dont vous connaissez la plage ou la sous-chaîne.
Je voudrais prendre n'importe quelle chaîne et savoir s'il contient des liens et où se trouvent ces liens, puis ajouter le tapGestureRecognizer à l'étiquette et effectuer des actions en fonction de l'endroit où le tap s'est produit.
J'ai essayé d'incorporer une bibliothèque externe (TTTAttributedLabel) mais j'utilise Swift et j'ai trouvé la documentation pour Swift limitée). J'ai réussi à importer la bibliothèque mais les liens ne sont pas détectés automatiquement.
Source de référence -
Créer des "liens" utilisables dans la chaîne NSAttributedString d'un UILabel?
Il est converti en Swift 4.0
Essaye ça -
Créez une sous-classe pour UILabel comme ci-dessous -
Swift 4.0
class CustomLabel: UILabel {
let layoutManager = NSLayoutManager()
let textContainer = NSTextContainer(size: CGSize.zero)
var textStorage = NSTextStorage() {
didSet {
textStorage.addLayoutManager(layoutManager)
}
}
var onCharacterTapped: ((_ label: UILabel, _ characterIndex: Int) -> Void)?
let tapGesture = UITapGestureRecognizer()
override var attributedText: NSAttributedString? {
didSet {
if let attributedText = attributedText {
textStorage = NSTextStorage(attributedString: attributedText)
} else {
textStorage = NSTextStorage()
}
}
}
override var lineBreakMode: NSLineBreakMode {
didSet {
textContainer.lineBreakMode = lineBreakMode
}
}
override var numberOfLines: Int {
didSet {
textContainer.maximumNumberOfLines = numberOfLines
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setUp()
}
override init(frame: CGRect) {
super.init(frame: frame)
setUp()
}
func setUp() {
isUserInteractionEnabled = true
layoutManager.addTextContainer(textContainer)
textContainer.lineFragmentPadding = 0
textContainer.lineBreakMode = lineBreakMode
textContainer.maximumNumberOfLines = numberOfLines
tapGesture.addTarget(self, action: #selector(CustomLabel.labelTapped(_:)))
addGestureRecognizer(tapGesture)
}
override func layoutSubviews() {
super.layoutSubviews()
textContainer.size = bounds.size
}
@objc func labelTapped(_ gesture: UITapGestureRecognizer) {
guard gesture.state == .ended else {
return
}
let locationOfTouch = gesture.location(in: gesture.view)
let textBoundingBox = layoutManager.usedRect(for: textContainer)
let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX,
y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY)
let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x, y: locationOfTouch.y - textContainerOffset.y)
let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer,
in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)
onCharacterTapped?(self, indexOfCharacter)
}
}
Dans votre méthode viewDidLoad
du contrôleur View, créez une instance de cette classe comme ci-dessous -
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let label = CustomLabel()
label.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(label)
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[view]-|",
options: [], metrics: nil, views: ["view" : label]))
let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil)
let linkRange = NSMakeRange(14, 4); // for the Word "link" in the string above
let linkAttributes: [NSAttributedStringKey : AnyObject] = [
NSAttributedStringKey.foregroundColor : UIColor.blue, NSAttributedStringKey.underlineStyle : NSUnderlineStyle.styleSingle.rawValue as AnyObject,
NSAttributedStringKey.link: "http://www.Apple.com" as AnyObject ]
attributedString.setAttributes(linkAttributes, range:linkRange)
label.attributedText = attributedString
label.onCharacterTapped = { label, characterIndex in
// DO YOUR STUFF HERE
}
}