猿问
下载APP

在UILabel的NSAttributedString中创建可点击的“链接”?

在UILabel的NSAttributedString中创建可点击的“链接”?

我已经找了好几个小时了,但我失败了。我可能都不知道该找什么了。

许多应用程序都有文本,在这篇文章中,Web超链接是四舍五入的RECT。当我点击它们UIWebView打开。令我困惑的是,它们通常都有自定义链接,例如,如果单词以#开头,它也是可点击的,应用程序通过打开另一个视图来响应。我怎么能这么做?有没有可能UILabel还是我需要UITextView还是别的什么?


largeQ
浏览 137回答 3
3回答

ibeautiful

一般来说,如果我们想在UILabel显示的文本中有一个可点击的链接,我们需要解决两个独立的任务:更改部分文本的外观,使其看起来像链接检测和处理链接上的触摸(打开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 aboveNSDictionary *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 UILabellabel.attributedText = attributedString;就这样!上面的代码使UILabel显示字符串具有链接现在我们应该检测到这个链接的触点。这样做的目的是捕捉UILabel中的所有点击,并确定点击的位置是否足够接近链接。为了捕捉触摸,我们可以添加点击手势识别器到标签。确保为标签启用userInteraction,默认情况下它被关闭:label.userInteractionEnabled = YES;[label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget: self action:@selector(handleTapOnLabel:)]];现在,最复杂的东西:找出点击是否在链接显示的位置,而不是在标签的任何其他部分。如果我们有单行UILabel,这个任务可以通过硬编码显示链接的区域边界来相对容易地解决,但是让我们更优雅地解决这个问题,对于一般情况-多行UILabel,不需要对链接布局有初步的了解。其中一种方法是使用IOS 7中引入的文本工具包API的功能:// Create instances of NSLayoutManager, NSTextContainer and NSTextStorageNSLayoutManager *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 textContainertextContainer.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/"]];     }}

Qyouu

我在扩展@NAlexN最初的详细解决方案@zekel优秀扩展UITapGestureRecognizer,并提供斯威夫特.扩展UITapGestureRecognerextension UITapGestureRecognizer {     func didTapAttributedTextInLabel(label: UILabel, inRange targetRange: NSRange) -> Bool {         // Create instances of NSLayoutManager, NSTextContainer and NSTextStorage         let layoutManager = NSLayoutManager()         let textContainer = NSTextContainer(size: CGSize.zero)         let textStorage = NSTextStorage(attributedString: label.attributedText!)         // Configure layoutManager and textStorage         layoutManager.addTextContainer(textContainer)         textStorage.addLayoutManager(layoutManager)         // Configure textContainer         textContainer.lineFragmentPadding = 0.0         textContainer.lineBreakMode = label.lineBreakMode         textContainer.maximumNumberOfLines = label.numberOfLines        let labelSize = label.bounds.size         textContainer.size = labelSize        // Find the tapped character location and compare it to the specified range         let locationOfTouchInLabel = self.locationInView(label)         let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer)         let textContainerOffset = CGPointMake((labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x,             (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y);         let locationOfTouchInTextContainer = CGPointMake(locationOfTouchInLabel.x - textContainerOffset.x,             locationOfTouchInLabel.y - textContainerOffset.y);         let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer:          textContainer, fractionOfDistanceBetweenInsertionPoints: nil)         return NSLocationInRange(indexOfCharacter, targetRange)     }}使用设置UIGestureRecognizer发送操作到tapLabel:,您可以检测到目标范围是否正在被窃听。myLabel.@IBAction func tapLabel(gesture: UITapGestureRecognizer) {     if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange1) {         print("Tapped targetRange1")     } else if gesture.didTapAttributedTextInLabel(myLabel, inRange: targetRange2) {         print("Tapped targetRange2")     } else {         print("Tapped none")     }}重要:UILabel必须将换行模式设置为按Word/char包装。不知何故,NSTextContainer假设只有在换行模式为换行模式时,文本才是单行。

春华秋衣

老问题但如果有人可以用UITextView而不是UILabel,那就很容易了。标准URL、电话号码等将被自动检测(并可点击)。但是,如果您需要自定义检测,也就是说,如果您希望能够在用户单击特定单词之后调用任何自定义方法,则需要使用NSAttributedStrings带着NSLinkAttributeName属性,该属性指向自定义URL方案(与默认情况下的http url方案相反)。雷·温德利希已经把它盖在这里了引用上述链接中的代码:NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:@"This is an example by @marcelofabri_"]; [attributedString addAttribute:NSLinkAttributeName                      value:@"username://marcelofabri_"                      range:[[attributedString string] rangeOfString:@"@marcelofabri_"]];                      NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor greenColor],                              NSUnderlineColorAttributeName: [UIColor lightGrayColor],                              NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};                              // assume that textView is a UITextView previously created (either by code or Interface Builder)                              textView.linkTextAttributes = linkAttributes;                               // customizes the appearance of linkstextView.attributedText = attributedString;                              textView.delegate = self;若要检测这些链接单击,请实现以下操作:- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {     if ([[URL scheme] isEqualToString:@"username"]) {         NSString *username = [URL host];          // do something with this username         // ...         return NO;     }     return YES; // let the system open this URL}PS:确保你的UITextView是selectable.
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答