В общем, если мы хотим, чтобы в тексте UILabel отображалась кликабельная ссылка, нам нужно было бы решить две независимые задачи:
- Изменение внешнего вида части текста, чтобы она выглядела как ссылка
- Обнаружение и обработка прикосновений к ссылке (открытие URL является частным случаем)
Первый легко. Начиная с iOS 6 UILabel поддерживает отображение приписанных строк. Все, что вам нужно сделать, это создать и настроить экземпляр 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;
Это оно! Приведенный выше код заставляет UILabel отображать строку со ссылкой
Теперь мы должны обнаружить касания по этой ссылке. Идея состоит в том, чтобы перехватить все ответвления в UILabel и выяснить, было ли расположение ответвления достаточно близко к ссылке. Чтобы поймать прикосновения, мы можем добавить распознаватель жестов к ярлыку. Не забудьте включить userInteraction для метки, по умолчанию она отключена:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Теперь самое сложное: выяснить, был ли тап в том месте, где отображается ссылка, а не в какой-либо другой части ярлыка. Если бы у нас был ULabel с одной линией, эту задачу можно было бы относительно легко решить путем жесткого кодирования границ области, в которой отображается ссылка, но давайте решим эту проблему более элегантно и в общем случае - многострочную UILabel без предварительного знания структуры ссылок.
Одним из подходов является использование возможностей API Text Kit, представленных в 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;
Сохраните созданные и настроенные экземпляры NSLayoutManager, NSTextContainer и NSTextStorage в свойствах вашего класса (скорее всего, потомка UIViewController) - они нам понадобятся в других методах.
Теперь каждый раз, когда метка меняет свой фрейм, обновляйте размер textContainer:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
И, наконец, определите, был ли кран точно по ссылке:
- (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
решения. Он использует,UITextView
но заставляет его вести себя какUILabel
. Я попробовал решения здесь, и не смог получить точное обнаружение ссылки.