Ogólnie, jeśli chcemy mieć klikalny link w tekście wyświetlany przez UILabel, musielibyśmy rozwiązać dwa niezależne zadania:
- Zmiana wyglądu części tekstu, aby wyglądał jak link
- Wykrywanie i obsługa zmian w linkach (otwarcie adresu URL to szczególny przypadek)
Pierwszy jest łatwy. Począwszy od iOS 6 UILabel obsługuje wyświetlanie przypisanych ciągów. Wszystko, co musisz zrobić, to utworzyć i skonfigurować instancję NSMutableAttributString:
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;
Otóż to! Powyższy kod powoduje, że UILabel wyświetla String wraz z linkiem
Teraz powinniśmy wykryć poprawki tego linku. Chodzi o to, aby złapać wszystkie krany w UILabel i dowiedzieć się, czy lokalizacja kranu była wystarczająco blisko linku. Aby złapać akcenty, możemy dodać do etykiety rozpoznawanie gestów dotykowych. Pamiętaj, aby włączyć etykietę UserInteraction dla etykiety, jest ona domyślnie wyłączona:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Teraz najbardziej wyrafinowane rzeczy: sprawdzenie, czy kran był w miejscu, w którym wyświetlany jest link, a nie w żadnej innej części etykiety. Gdybyśmy mieli UILabel z pojedynczą linią, zadanie to można by rozwiązać stosunkowo łatwo, na stałe zakreślając granice obszaru, w którym wyświetlany jest link, ale rozwiążmy ten problem bardziej elegancko i ogólnie - wielowierszowy UILabel bez wstępnej wiedzy o układzie łącza.
Jednym z podejść jest wykorzystanie możliwości Text Kit API wprowadzonych w 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;
Zapisz utworzone i skonfigurowane instancje NSLayoutManager, NSTextContainer i NSTextStorage we właściwościach w swojej klasie (najprawdopodobniej potomek UIViewController) - będziemy potrzebować ich w innych metodach.
Teraz za każdym razem, gdy etykieta zmienia ramkę, aktualizuj tekst Rozmiar kontenera:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
Na koniec sprawdź, czy kran był dokładnie na linku:
- (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
rozwiązania. Używa,UITextView
ale sprawia, że zachowuje się jakUILabel
. Wypróbowałem tutaj rozwiązania i nie udało mi się uzyskać dokładnego wykrywania łącza.