一般に、UILabel によって表示されるテキスト内にクリック可能なリンクが必要な場合は、2 つの独立したタスクを解決する必要があります。
- テキストの一部の外観をリンクのように変更する
- リンクへのタッチの検出と処理 (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:)]];
最も洗練された機能は、ラベルの他の部分ではなく、リンクが表示されている場所でタップが行われたかどうかを調べることです。単一行の UILabel がある場合、このタスクは、リンクが表示される領域の境界をハードコーディングすることで比較的簡単に解決できますが、この問題をよりエレガントに、一般的なケース (リンク レイアウトに関する予備知識なしで複数行の UILabel) で解決しましょう。
アプローチの 1 つは、iOS 7 で導入された Text Kit API の機能を使用することです。
// 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/"]];
}
}