多くのアプリケーションにはテキストがあり、このテキストには角丸の四角形の Web ハイパーリンクがあります。クリックするとUIWebView
開きます。困惑するのは、多くの場合カスタム リンクがあることです。たとえば、単語が # で始まる場合もクリック可能で、アプリケーションは別のビューを開いて応答します。どうすればそれができますか? 何か他の方法で可能ですか、それとも何か他のものUILabel
が必要ですか?UITextView
ベストアンサー1
一般的に、UILabel によって表示されるテキスト内にクリック可能なリンクを配置する場合、次の 2 つの独立したタスクを解決する必要があります。
- テキストの一部をリンクのように見せるための外観の変更
- リンクへのタッチを検出して処理する(URL を開くのは特別なケースです)
1 つ目は簡単です。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/"]];
}
}