Wenn ein anklickbarer Link in Text von UILabel angezeigt werden soll, müssen im Allgemeinen zwei unabhängige Aufgaben gelöst werden:
- Ändern des Erscheinungsbilds eines Teils des Textes, um wie ein Link auszusehen
- Erkennen und Behandeln von Berührungen des Links (das Öffnen einer URL ist ein besonderer Fall)
Der erste ist einfach. Ab iOS 6 unterstützt UILabel die Anzeige von zugeordneten Zeichenfolgen. Sie müssen lediglich eine Instanz von NSMutableAttributedString erstellen und konfigurieren:
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;
Das ist es! Mit dem obigen Code zeigt UILabel String mit einem Link an
Jetzt sollten wir Berührungen dieses Links erkennen. Die Idee ist, alle Abgriffe in UILabel zu erfassen und herauszufinden, ob die Position des Abgriffs nahe genug am Link war. Um Berührungen zu erfassen, können wir dem Etikett eine Tippgestenerkennung hinzufügen. Stellen Sie sicher, dass userInteraction für das Etikett aktiviert ist. Es ist standardmäßig deaktiviert:
label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
Jetzt das raffinierteste Zeug: herauszufinden, ob sich der Tipp dort befand, wo der Link angezeigt wird, und nicht auf einem anderen Teil des Etiketts. Wenn wir ein einzeiliges UILabel hätten, könnte diese Aufgabe relativ einfach gelöst werden, indem die Bereichsgrenzen, in denen der Link angezeigt wird, fest codiert werden. Lassen Sie uns dieses Problem jedoch eleganter und für den allgemeinen Fall lösen - mehrzeiliges UILabel ohne vorherige Kenntnisse über das Linklayout.
Einer der Ansätze besteht darin, die in iOS 7 eingeführten Funktionen der Text Kit-API zu verwenden:
// 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;
Speichern Sie erstellte und konfigurierte Instanzen von NSLayoutManager, NSTextContainer und NSTextStorage in Eigenschaften in Ihrer Klasse (höchstwahrscheinlich der Nachkomme von UIViewController) - wir benötigen sie in anderen Methoden.
Aktualisieren Sie jetzt jedes Mal, wenn das Etikett seinen Rahmen ändert, die Größe von textContainer:
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.textContainer.size = self.label.bounds.size;
}
Und schließlich erkennen Sie, ob der Hahn genau auf dem Link war:
- (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
. Es verwendet,UITextView
aber es verhält sich wie einUILabel
. Ich habe die Lösungen hier ausprobiert und konnte keine genaue Linkerkennung erhalten.