Загалом, якщо ми хочемо, щоб текст, який можна натиснути, відображався 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 і з'ясувати, чи розташування крана було досить близько до посилання. Щоб доторкнутися до дотиків, ми можемо додати розпізнавач жестів дотику до мітки. Переконайтеся, що ввімкнено взаємодію користувачів для мітки, вона вимкнена за замовчуванням:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Тепер найскладніші речі: з’ясувати, чи був краном, де відображається посилання, а не на будь-якій іншій частині етикетки. Якби у нас був однолінійний UILabel, цю задачу можна було б вирішити порівняно легко, жорстко кодуючи межі області, де відображається посилання, але давайте вирішимо цю проблему більш елегантно і для загального випадку - багаторядковий UILabel без попередніх знань про компонування посилання.
Одним із підходів є використання можливостей Text Kit API, введеного в 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
. Я спробував рішення тут, і не вдалося отримати точне виявлення посилання.