创建可点击的“链接"在 UILabel 的 NSAttributedString 中? [英] Create tap-able "links" in the NSAttributedString of a UILabel?

查看:38
本文介绍了创建可点击的“链接"在 UILabel 的 NSAttributedString 中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经搜索了几个小时,但我失败了.我可能甚至不知道我应该寻找什么.

许多应用程序都有文本,在此文本中是圆角矩形中的网络超链接.当我点击它们时 UIWebView 打开.让我感到困惑的是,他们经常有自定义链接,例如,如果单词以 # 开头,它也是可点击的,应用程序会通过打开另一个视图来响应.我怎样才能做到这一点?是否可以使用 UILabel 或者我是否需要 UITextView 或其他东西?

解决方案

一般来说,如果我们想在 UILabel 显示的文本中有一个可点击的链接,我们需要解决两个独立的任务:

  1. 更改部分文本的外观,使其看起来像一个链接
  2. 检测和处理对链接的触摸(打开 URL 是一种特殊情况)

第一个很简单.从 iOS 6 UILabel 开始支持属性字符串的显示.您需要做的就是创建和配置 NSMutableAttributedString 的实例:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"String with a link" attributes:nil];NSRange 链接范围 = NSMakeRange(14, 4);//对于上面字符串中的单词link"NSDictionary *linkAttributes = @{ NSForegroundColorAttributeName : [UIColor colorWithRed:0.05 green:0.4 blue:0.65 alpha:1.0],NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) };[attributedString setAttributes:linkAttributes range:linkRange];//将属性文本分配给 UILabellabel.attributedText = 属性字符串;

就是这样!上面的代码使 UILabel 显示带有 链接

字符串

现在我们应该检测此链接上的触摸.这个想法是捕捉 UILabel 中的所有点击,并确定点击的位置是否足够接近链接.为了捕捉触摸,我们可以向标签添加点击手势识别器.确保为标签启用 userInteraction,默认情况下它是关闭的:

label.userInteractionEnabled = YES;[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];

现在是最复杂的东西:找出水龙头是否在显示链接的位置,而不是在标签的任何其他部分.如果我们有单行 UILabel,这个任务可以通过硬编码显示链接的区域边界来相对容易地解决,但让我们更优雅地解决这个问题,对于一般情况 - 多行 UILabel 没有关于链接布局的初步知识.

其中一种方法是使用 iOS 7 中引入的 Text Kit API 的功能:

//创建 NSLayoutManager、NSTextContainer 和 NSTextStorage 的实例NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero];NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];//配置 layoutManager 和 textStorage[layoutManager addTextContainer:textContainer];[textStorage addLayoutManager:layoutManager];//配置文本容器textContainer.lineFragmentPadding = 0.0;textContainer.lineBreakMode = label.lineBreakMode;textContainer.maximumNumberOfLines = label.numberOfLines;

将创建和配置的 NSLayoutManager、NSTextContainer 和 NSTextStorage 实例保存在类的属性中(很可能是 UIViewController 的后代)——我们将在其他方法中需要它们.

现在,每次标签改变其框架时,更新 textContainer 的大小:

- (void)viewDidLayoutSubviews{[超级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:locationOfTouchInTextContainerinTextContainer:self.textContainerfractionOfDistanceBetweenInsertionPoints:nil];NSRange 链接范围 = NSMakeRange(14, 4);//最初用于标记属性字符串中的链接时,最好将范围保存在某处如果(NSLocationInRange(indexOfCharacter,linkRange)){//打开一个 URL,或以任何其他方式处理链接上的点击[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"https://stackoverflow.com/"]];}}

I have been searching this for hours but I've failed. I probably don't even know what I should be looking for.

Many applications have text and in this text are web hyperlinks in rounded rect. When I click them UIWebView opens. What puzzles me is that they often have custom links, for example if words starts with # it is also clickable and the application responds by opening another view. How can I do that? Is it possible with UILabel or do I need UITextView or something else?

解决方案

In general, if we want to have a clickable link in text displayed by UILabel, we would need to resolve two independent tasks:

  1. Changing the appearance of a portion of the text to look like a link
  2. Detecting and handling touches on the link (opening an URL is a particular case)

The first one is easy. Starting from iOS 6 UILabel supports display of attributed strings. All you need to do is to create and configure an instance of 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;

That's it! The code above makes UILabel to display String with a link

Now we should detect touches on this link. The idea is to catch all taps within UILabel and figure out whether the location of the tap was close enough to the link. To catch touches we can add tap gesture recognizer to the label. Make sure to enable userInteraction for the label, it's turned off by default:

label.userInteractionEnabled = YES;
[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]]; 

Now the most sophisticated stuff: finding out whether the tap was on where the link is displayed and not on any other portion of the label. If we had single-lined UILabel, this task could be solved relatively easy by hardcoding the area bounds where the link is displayed, but let's solve this problem more elegantly and for general case - multiline UILabel without preliminary knowledge about the link layout.

One of the approaches is to use capabilities of Text Kit API introduced in 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;

Save created and configured instances of NSLayoutManager, NSTextContainer and NSTextStorage in properties in your class (most likely UIViewController's descendant) - we'll need them in other methods.

Now, each time the label changes its frame, update textContainer's size:

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    self.textContainer.size = self.label.bounds.size;
}

And finally, detect whether the tap was exactly on the link:

- (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/"]];
    }
}

这篇关于创建可点击的“链接"在 UILabel 的 NSAttributedString 中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆