En general, si queremos que UILabel muestre un enlace en el que se puede hacer clic, tendremos que resolver dos tareas independientes:
- Cambiar la apariencia de una parte del texto para que parezca un enlace
- Detectar y manejar toques en el enlace (abrir una URL es un caso particular)
El primero es facil. A partir de iOS 6, UILabel admite la visualización de cadenas atribuidas. Todo lo que necesita hacer es crear y configurar una instancia de NSMutableAttributedString:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];
NSRange linkRange = NSMakeRange(14, 4); // for the word "link" in the string above
NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],
NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };
[attributedString setAttributes:linkAttributes range:linkRange];
// Assign attributedText to UILabel
label.attributedText = attributedString;
¡Eso es! El código anterior hace que UILabel muestre String con un enlace
Ahora deberíamos detectar toques en este enlace. La idea es capturar todos los grifos dentro de UILabel y determinar si la ubicación del grifo estaba lo suficientemente cerca del enlace. Para capturar toques, podemos agregar un reconocimiento de gestos de toque a la etiqueta. Asegúrese de habilitar userInteraction para la etiqueta, está desactivada por defecto:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Ahora lo más sofisticado: averiguar si el toque estaba en el lugar donde se muestra el enlace y no en ninguna otra parte de la etiqueta. Si tuviéramos UILabel de una sola línea, esta tarea podría resolverse de manera relativamente fácil codificando los límites del área donde se muestra el enlace, pero solucionemos este problema de manera más elegante y para el caso general: UILabel multilínea sin conocimiento preliminar sobre el diseño del enlace.
Uno de los enfoques es utilizar las capacidades de Text API API introducidas en iOS 7:
// Create instances of NSLayoutManager, NSTextContainer and NSTextStorage
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
// Configure layoutManager and textStorage
[layoutManager addTextContainer:textContainer];
[textStorage addLayoutManager:layoutManager];
// Configure textContainer
textContainer.lineFragmentPadding = 0.0;
textContainer.lineBreakMode = label.lineBreakMode;
textContainer.maximumNumberOfLines = label.numberOfLines;
Guarde las instancias creadas y configuradas de NSLayoutManager, NSTextContainer y NSTextStorage en las propiedades de su clase (probablemente el descendiente de UIViewController): las necesitaremos en otros métodos.
Ahora, cada vez que la etiqueta cambie su marco, actualice el tamaño de textContainer:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
Y finalmente, detecta si el toque estaba exactamente en el enlace:
- (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture
{
CGPoint locationOfTouchInLabel = [tapGesture locationInView:tapGesture.view];
CGSize labelSize = tapGesture.view.bounds.size;
CGRect textBoundingBox = [self.layoutManager usedRectForTextContainer:self.textContainer];
CGPoint textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,
(labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);
CGPoint locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,
locationOfTouchInLabel.y - textContainerOffset.y);
NSInteger indexOfCharacter = [self.layoutManager characterIndexForPoint:locationOfTouchInTextContainer
inTextContainer:self.textContainer
fractionOfDistanceBetweenInsertionPoints:nil];
NSRange linkRange = NSMakeRange(14, 4); // it's better to save the range somewhere when it was originally used for marking link in attributed string
if (NSLocationInRange(indexOfCharacter, linkRange)) {
// Open an URL, or handle the tap on the link in any other way
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];
}
}
Swift 4
solución totalmente funcional . UtilizaUITextView
pero hace que se comporte como aUILabel
. Probé las soluciones aquí y no pude obtener una detección de enlace precisa.