在UILabel的NSAttributedString中创build可点击的“链接”?
我一直在寻找这个小时,但我失败了。 我可能甚至不知道我应该找什么。
许多应用程序都有文本,在这个文本中是四舍五入的web超链接。 当我点击它们UIWebView
打开。 令我困惑的是他们经常有自定义链接,例如,如果单词以#开始,它也是可点击的,应用程序通过打开另一个视图来响应。 我怎样才能做到这一点? UILabel
可能吗?还是我需要UITextView
或其他?
一般来说,如果我们想在UILabel显示的文本中有一个可点击的链接,我们需要解决两个独立的任务:
- 改变文本的一部分外观看起来像一个链接
- 检测和处理链接的触摸(打开一个URL是一个特定的情况)
第一个很容易。 从iOS 6开始,UILabel支持显示属性string。 所有你需要做的是创build和configuration一个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显示带有链接的string
现在我们应该检测这个链接。 这个想法是捕捉UILabel中的所有水龙头,并找出水龙头的位置是否足够接近链接。 为了赶上触摸,我们可以添加点击手势识别器的标签。 确保为标签启用userInteraction,默认closures:
label.userInteractionEnabled = YES; [label addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]];
现在是最复杂的东西:找出水龙头是在哪里显示的链接,而不是在标签的任何其他部分。 如果我们有单线UILabel,这个任务可以通过对链接显示的区域进行硬编码来解决,但是我们可以更加优雅地解决这个问题,而对于一般情况下的多线UILabel,如果没有关于链接布局的初步知识。
其中一种方法是使用iOS 7中引入的Text Kit API的function:
// 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;
保存创build和configurationNSLayoutManager,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/"]]; } }
FancyLabel正是我需要:)
UIButtonTypeCustom是一个可点击的标签,如果你没有设置任何图像。
(我的答案build立在@NAlexN的优秀答案上 ,我不会在这里重复每一步的详细解释。)
我发现最方便直接的方式是将支持可点击的UILabel文本作为类别添加到UITapGestureRecognizer。 (您不必使用UITextView的数据检测器,因为有些答案显示。)
将以下方法添加到您的UITapGestureRecognizer类别中:
/** Returns YES if the tap gesture was within the specified range of the attributed text of the label. */ - (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange { NSParameterAssert(label != nil); CGSize labelSize = label.bounds.size; // create instances of NSLayoutManager, NSTextContainer and NSTextStorage NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero]; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText]; // configure layoutManager and textStorage [layoutManager addTextContainer:textContainer]; [textStorage addLayoutManager:layoutManager]; // configure textContainer for the label textContainer.lineFragmentPadding = 0.0; textContainer.lineBreakMode = label.lineBreakMode; textContainer.maximumNumberOfLines = label.numberOfLines; textContainer.size = labelSize; // find the tapped character location and compare it to the specified range CGPoint locationOfTouchInLabel = [self locationInView:label]; CGRect textBoundingBox = [layoutManager usedRectForTextContainer: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 = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nil]; if (NSLocationInRange(indexOfCharacter, targetRange)) { return YES; } else { return NO; } }
示例代码
// (in your view controller) // create your label, gesture recognizer, attributed text, and get the range of the "link" in your label myLabel.userInteractionEnabled = YES; [myLabel addGestureRecognizer: [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnLabel:)]]; // create your attributed text and keep an ivar of your "link" text range NSAttributedString *plainText; NSAttributedString *linkText; plainText = [[NSMutableAttributedString alloc] initWithString:@"Add label links with UITapGestureRecognizer" attributes:nil]; linkText = [[NSMutableAttributedString alloc] initWithString:@" Learn more..." attributes:@{ NSForegroundColorAttributeName:[UIColor blueColor] }]; NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] init]; [attrText appendAttributedString:plainText]; [attrText appendAttributedString:linkText]; // ivar -- keep track of the target range so you can compare in the callback targetRange = NSMakeRange(plainText.length, linkText.length);
手势callback
// handle the gesture recognizer callback and call the category method - (void)handleTapOnLabel:(UITapGestureRecognizer *)tapGesture { BOOL didTapLink = [tapGesture didTapAttributedTextInLabel:myLabel inRange:targetRange]; NSLog(@"didTapLink: %d", didTapLink); }
我正在扩展@NAlexN原有的详细解决scheme,用@zekel优秀的扩展UITapGestureRecognizer
,并在Swift中提供。
扩展UITapGestureRecognizer
extension 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") } }
老问题,但如果任何人都可以使用UITextView
而不是UILabel
,那么很容易。 标准url,电话号码等将被自动检测(并可点击)。
但是,如果您需要自定义检测,也就是说,如果希望在用户单击特定单词后能够调用任何自定义方法,则需要使用带有NSLinkAttributeName
属性的NSAttributedStrings
,该属性将指向自定义URLscheme(如与默认的http urlscheme相反)。 Ray Wenderlich在这里报道
引用上述链接中的代码:
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 links textView.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
。
UITextView
支持OS3.0中的数据检测器,而UILabel
则不支持。
如果您在UITextView
上启用数据检测器,并且您的文本包含URL,电话号码等,则它们将显示为链接。
以下是超链接UILabel的示例代码:来源: http : //sickprogrammersarea.blogspot.in/2014/03/adding-links-to-uilabel.html
#import "ViewController.h" #import "TTTAttributedLabel.h" @interface ViewController () @end @implementation ViewController { UITextField *loc; TTTAttributedLabel *data; } - (void)viewDidLoad { [super viewDidLoad]; UILabel *lbl = [[UILabel alloc] initWithFrame:CGRectMake(5, 20, 80, 25) ]; [lbl setText:@"Text:"]; [lbl setFont:[UIFont fontWithName:@"Verdana" size:16]]; [lbl setTextColor:[UIColor grayColor]]; loc=[[UITextField alloc] initWithFrame:CGRectMake(4, 20, 300, 30)]; //loc.backgroundColor = [UIColor grayColor]; loc.borderStyle=UITextBorderStyleRoundedRect; loc.clearButtonMode=UITextFieldViewModeWhileEditing; //[loc setText:@"Enter Location"]; loc.clearsOnInsertion = YES; loc.leftView=lbl; loc.leftViewMode=UITextFieldViewModeAlways; [loc setDelegate:self]; [self.view addSubview:loc]; [loc setRightViewMode:UITextFieldViewModeAlways]; CGRect frameimg = CGRectMake(110, 70, 70,30); UIButton *srchButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; srchButton.frame=frameimg; [srchButton setTitle:@"Go" forState:UIControlStateNormal]; [srchButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; srchButton.backgroundColor=[UIColor clearColor]; [srchButton addTarget:self action:@selector(go:) forControlEvents:UIControlEventTouchDown]; [self.view addSubview:srchButton]; data = [[TTTAttributedLabel alloc] initWithFrame:CGRectMake(5, 120,self.view.frame.size.width,200) ]; [data setFont:[UIFont fontWithName:@"Verdana" size:16]]; [data setTextColor:[UIColor blackColor]]; data.numberOfLines=0; data.delegate = self; data.enabledTextCheckingTypes=NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumber; [self.view addSubview:data]; } - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithURL:(NSURL *)url { NSString *val=[[NSString alloc]initWithFormat:@"%@",url]; if ([[url scheme] hasPrefix:@"mailto"]) { NSLog(@" mail URL Selected : %@",url); MFMailComposeViewController *comp=[[MFMailComposeViewController alloc]init]; [comp setMailComposeDelegate:self]; if([MFMailComposeViewController canSendMail]) { NSString *recp=[[val substringToIndex:[val length]] substringFromIndex:7]; NSLog(@"Recept : %@",recp); [comp setToRecipients:[NSArray arrayWithObjects:recp, nil]]; [comp setSubject:@"From my app"]; [comp setMessageBody:@"Hello bro" isHTML:NO]; [comp setModalTransitionStyle:UIModalTransitionStyleCrossDissolve]; [self presentViewController:comp animated:YES completion:nil]; } } else{ [[UIApplication sharedApplication] openURL:[NSURL URLWithString:val]]; } } -(void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error{ if(error) { UIAlertView *alrt=[[UIAlertView alloc]initWithTitle:@"Erorr" message:@"Some error occureed" delegate:nil cancelButtonTitle:@"" otherButtonTitles:nil, nil]; [alrt show]; [self dismissViewControllerAnimated:YES completion:nil]; } else{ [self dismissViewControllerAnimated:YES completion:nil]; } } - (void)attributedLabel:(TTTAttributedLabel *)label didSelectLinkWithPhoneNumber:(NSString *)phoneNumber { NSLog(@"Phone Number Selected : %@",phoneNumber); UIDevice *device = [UIDevice currentDevice]; if ([[device model] isEqualToString:@"iPhone"] ) { [[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"tel:%@",phoneNumber]]]; } else { UIAlertView *Notpermitted=[[UIAlertView alloc] initWithTitle:@"Alert" message:@"Your device doesn't support this feature." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [Notpermitted show]; } } -(void)go:(id)sender { [data setText:loc.text]; } -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { NSLog(@"Reached"); [loc resignFirstResponder]; }
正如我在这篇文章中提到的 ,这里是我为UILabel FRHyperLabel中的链接专门创build的一个轻量级库。
要达到这样的效果:
Lorem ipsum dolor sit amet,consectetur adipiscing elit。 Pellentesque quis blandit爱神,坐amet车辆justo。 Nam在urna neque。 Maecenas ac sem eu sem porta dictum nec prec de tellus。
使用代码:
//Step 1: Define a normal attributed string for non-link texts NSString *string = @"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque quis blandit eros, sit amet vehicula justo. Nam at urna neque. Maecenas ac sem eu sem porta dictum nec vel tellus."; NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]}; label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes]; //Step 2: Define a selection handler block void(^handler)(FRHyperLabel *label, NSString *substring) = ^(FRHyperLabel *label, NSString *substring){ NSLog(@"Selected: %@", substring); }; //Step 3: Add link substrings [label setLinksForSubstrings:@[@"Lorem", @"Pellentesque", @"blandit", @"Maecenas"] withLinkHandler:handler];
我创build了一个名为ResponsiveLabel的 UILabel子类,它基于iOS 7中引入的textkit API。它使用NAlexN提出的相同方法。 它提供了灵活性来指定在文本中search的模式。 可以指定应用于这些模式的样式,以及在敲击模式时执行的操作。
//Detects email in text NSString *emailRegexString = @"[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[AZ]{2,4}"; NSError *error; NSRegularExpression *regex = [[NSRegularExpression alloc]initWithPattern:emailRegexString options:0 error:&error]; PatternDescriptor *descriptor = [[PatternDescriptor alloc]initWithRegex:regex withSearchType:PatternSearchTypeAll withPatternAttributes:@{NSForegroundColorAttributeName:[UIColor redColor]}]; [self.customLabel enablePatternDetection:descriptor];
如果你想让一个string可点击,你可以这样做。 此代码将属性应用于string“text”的每个出现处。
PatternTapResponder tapResponder = ^(NSString *string) { NSLog(@"tapped = %@",string); }; [self.customLabel enableStringDetection:@"text" withAttributes:@{NSForegroundColorAttributeName:[UIColor redColor], RLTapResponderAttributeName: tapResponder}];
在Swift 3中工作,在这里粘贴整个代码
//****Make sure the textview 'Selectable' = checked, and 'Editable = Unchecked' import UIKit class ViewController: UIViewController, UITextViewDelegate { @IBOutlet var theNewTextView: UITextView! override func viewDidLoad() { super.viewDidLoad() //****textview = Selectable = checked, and Editable = Unchecked theNewTextView.delegate = self let theString = NSMutableAttributedString(string: "Agree to Terms") let theRange = theString.mutableString.range(of: "Terms") theString.addAttribute(NSLinkAttributeName, value: "ContactUs://", range: theRange) let theAttribute = [NSForegroundColorAttributeName: UIColor.blue, NSUnderlineStyleAttributeName: NSUnderlineStyle.styleSingle.rawValue] as [String : Any] theNewTextView.linkTextAttributes = theAttribute theNewTextView.attributedText = theString theString.setAttributes(theAttribute, range: theRange) } func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool { if (URL.scheme?.hasPrefix("ContactUs://"))! { return false //interaction not allowed } //*** Set storyboard id same as VC name self.navigationController!.pushViewController((self.storyboard?.instantiateViewController(withIdentifier: "TheLastViewController"))! as UIViewController, animated: true) return true } }
这是NAlexN答案的一个快速版本。
class TapabbleLabel: UILabel { let layoutManager = NSLayoutManager() let textContainer = NSTextContainer(size: CGSize.zero) var textStorage = NSTextStorage() { didSet { textStorage.addLayoutManager(layoutManager) } } var onCharacterTapped: ((label: UILabel, characterIndex: Int) -> Void)? let tapGesture = UITapGestureRecognizer() override var attributedText: NSAttributedString? { didSet { if let attributedText = attributedText { textStorage = NSTextStorage(attributedString: attributedText) } else { textStorage = NSTextStorage() } } } override var lineBreakMode: NSLineBreakMode { didSet { textContainer.lineBreakMode = lineBreakMode } } override var numberOfLines: Int { didSet { textContainer.maximumNumberOfLines = numberOfLines } } /** Creates a new view with the passed coder. :param: aDecoder The a decoder :returns: the created new view. */ required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) setUp() } /** Creates a new view with the passed frame. :param: frame The frame :returns: the created new view. */ override init(frame: CGRect) { super.init(frame: frame) setUp() } /** Sets up the view. */ func setUp() { userInteractionEnabled = true layoutManager.addTextContainer(textContainer) textContainer.lineFragmentPadding = 0 textContainer.lineBreakMode = lineBreakMode textContainer.maximumNumberOfLines = numberOfLines tapGesture.addTarget(self, action: #selector(TapabbleLabel.labelTapped(_:))) addGestureRecognizer(tapGesture) } override func layoutSubviews() { super.layoutSubviews() textContainer.size = bounds.size } func labelTapped(gesture: UITapGestureRecognizer) { guard gesture.state == .Ended else { return } let locationOfTouch = gesture.locationInView(gesture.view) let textBoundingBox = layoutManager.usedRectForTextContainer(textContainer) let textContainerOffset = CGPoint(x: (bounds.width - textBoundingBox.width) / 2 - textBoundingBox.minX, y: (bounds.height - textBoundingBox.height) / 2 - textBoundingBox.minY) let locationOfTouchInTextContainer = CGPoint(x: locationOfTouch.x - textContainerOffset.x, y: locationOfTouch.y - textContainerOffset.y) let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) onCharacterTapped?(label: self, characterIndex: indexOfCharacter) }
}
然后你可以在你的viewDidLoad
方法里面创build这个类的一个实例,像这样:
let label = TapabbleLabel() label.translatesAutoresizingMaskIntoConstraints = false view.addSubview(label) view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[view]-|", options: [], metrics: nil, views: ["view" : label])) view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[view]-|", options: [], metrics: nil, views: ["view" : label])) let attributedString = NSMutableAttributedString(string: "String with a link", attributes: nil) let linkRange = NSMakeRange(14, 4); // for the word "link" in the string above let linkAttributes: [String : AnyObject] = [ NSForegroundColorAttributeName : UIColor.blueColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleSingle.rawValue, NSLinkAttributeName: "http://www.apple.com"] attributedString.setAttributes(linkAttributes, range:linkRange) label.attributedText = attributedString label.onCharacterTapped = { label, characterIndex in if let attribute = label.attributedText?.attribute(NSLinkAttributeName, atIndex: characterIndex, effectiveRange: nil) as? String, let url = NSURL(string: attribute) { UIApplication.sharedApplication().openURL(url) } }
点击字符时最好有一个自定义属性。 现在,这是NSLinkAttributeName
,但可以是任何东西,你可以使用该值做其他事情,而不是打开一个URL,你可以做任何自定义操作。
我正在扩展@ samwize的答案来处理多行UILabel并给出一个使用UIButton的例子
extension UITapGestureRecognizer { func didTapAttributedTextInButton(button: UIButton, inRange targetRange: NSRange) -> Bool { guard let label = button.titleLabel else { return false } return didTapAttributedTextInLabel(label, inRange: targetRange) } 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), 0 ); // Adjust for multiple lines of text let lineModifier = Int(ceil(locationOfTouchInLabel.y / label.font.lineHeight)) - 1 let rightMostFirstLinePoint = CGPointMake(labelSize.width, 0) let charsPerLine = layoutManager.characterIndexForPoint(rightMostFirstLinePoint, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) let indexOfCharacter = layoutManager.characterIndexForPoint(locationOfTouchInTextContainer, inTextContainer: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) let adjustedRange = indexOfCharacter + (lineModifier * charsPerLine) return NSLocationInRange(adjustedRange, targetRange) } }
对于完全自定义的链接,你需要使用一个UIWebView – 你可以拦截这些调用,这样,当你按下一个链接的时候,你可以去应用程序的其他部分。
我有一个很难处理这个… UILabel的链接上它归属文本…这只是一个头痛,所以我结束了使用ZSWTappableLabel 。
I'd strongly recommend using a library that automatically detects URLs in text and converts them to links. 尝试:
- TTTAttributedLabel ( pod )
- ZSWTappableLabel ( pod ).
Both are under MIT license.
Like there is reported in earlier awnser the UITextView is able to handle touches on links. This can easily be extended by making other parts of the text work as links. The AttributedTextView library is a UITextView subclass that makes it very easy to handle these. For more info see: https://github.com/evermeer/AttributedTextView
You can make any part of the text interact like this (where textView1 is a UITextView IBoutlet):
textView1.attributer = "1. ".red .append("This is the first test. ").green .append("Click on ").black .append("evict.nl").makeInteract { _ in UIApplication.shared.open(URL(string: "http://evict.nl")!, options: [:], completionHandler: { completed in }) }.underline .append(" for testing links. ").black .append("Next test").underline.makeInteract { _ in print("NEXT") } .all.font(UIFont(name: "SourceSansPro-Regular", size: 16)) .setLinkColor(UIColor.purple)
And for handling hashtags and mentions you can use code like this:
textView1.attributer = "@test: What #hashtags do we have in @evermeer #AtributedTextView library" .matchHashtags.underline .matchMentions .makeInteract { link in UIApplication.shared.open(URL(string: "https://twitter.com\(link.replacingOccurrences(of: "@", with: ""))")!, options: [:], completionHandler: { completed in }) }
Create the class with the following .h and .m files. In the .m file there is the following function
- (void)linkAtPoint:(CGPoint)location
Inside this function we will check the ranges of substrings for which we need to give actions. Use your own logic to put your ranges.
And following is the usage of the subclass
TaggedLabel *label = [[TaggedLabel alloc] initWithFrame:CGRectMake(100, 100, 100, 100)]; [self.view addSubview:label]; label.numberOfLines = 0; NSMutableAttributedString *attributtedString = [[NSMutableAttributedString alloc] initWithString : @"My name is @jjpp" attributes : @{ NSFontAttributeName : [UIFont systemFontOfSize:10],}]; //Do not forget to add the font attribute.. else it wont work.. it is very important [attributtedString addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:NSMakeRange(11, 5)];//you can give this range inside the .m function mentioned above
following is the .h file
#import <UIKit/UIKit.h> @interface TaggedLabel : UILabel<NSLayoutManagerDelegate> @property(nonatomic, strong)NSLayoutManager *layoutManager; @property(nonatomic, strong)NSTextContainer *textContainer; @property(nonatomic, strong)NSTextStorage *textStorage; @property(nonatomic, strong)NSArray *tagsArray; @property(readwrite, copy) tagTapped nameTagTapped; @end
following is the .m file
#import "TaggedLabel.h" @implementation TaggedLabel - (id)initWithFrame:(CGRect)frame { self = [super initWithFrame:frame]; if (self) { self.userInteractionEnabled = YES; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { self.userInteractionEnabled = YES; } return self; } - (void)setupTextSystem { _layoutManager = [[NSLayoutManager alloc] init]; _textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero]; _textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText]; // Configure layoutManager and textStorage [_layoutManager addTextContainer:_textContainer]; [_textStorage addLayoutManager:_layoutManager]; // Configure textContainer _textContainer.lineFragmentPadding = 0.0; _textContainer.lineBreakMode = NSLineBreakByWordWrapping; _textContainer.maximumNumberOfLines = 0; self.userInteractionEnabled = YES; self.textContainer.size = self.bounds.size; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { if (!_layoutManager) { [self setupTextSystem]; } // Get the info for the touched link if there is one CGPoint touchLocation = [[touches anyObject] locationInView:self]; [self linkAtPoint:touchLocation]; } - (void)linkAtPoint:(CGPoint)location { // Do nothing if we have no text if (_textStorage.string.length == 0) { return; } // Work out the offset of the text in the view CGPoint textOffset = [self calcGlyphsPositionInView]; // Get the touch location and use text offset to convert to text cotainer coords location.x -= textOffset.x; location.y -= textOffset.y; NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer]; // If the touch is in white space after the last glyph on the line we don't // count it as a hit on the text NSRange lineRange; CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange]; if (CGRectContainsPoint(lineRect, location) == NO) { return; } // Find the word that was touched and call the detection block NSRange range = NSMakeRange(11, 5);//for this example i'm hardcoding the range here. In a real scenario it should be iterated through an array for checking all the ranges if ((touchedChar >= range.location) && touchedChar < (range.location + range.length)) { NSLog(@"range-->>%@",self.tagsArray[i][@"range"]); } } - (CGPoint)calcGlyphsPositionInView { CGPoint textOffset = CGPointZero; CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer]; textBounds.size.width = ceil(textBounds.size.width); textBounds.size.height = ceil(textBounds.size.height); if (textBounds.size.height < self.bounds.size.height) { CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0; textOffset.y = paddingHeight; } if (textBounds.size.width < self.bounds.size.width) { CGFloat paddingHeight = (self.bounds.size.width - textBounds.size.width) / 2.0; textOffset.x = paddingHeight; } return textOffset; } @end
based on Charles Gamble answer, this what I used (I removed some lines that confused me and gave me wrong indexed) :
- (BOOL)didTapAttributedTextInLabel:(UILabel *)label inRange:(NSRange)targetRange TapGesture:(UIGestureRecognizer*) gesture{ NSParameterAssert(label != nil); // create instances of NSLayoutManager, NSTextContainer and NSTextStorage NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:label.attributedText]; // configure layoutManager and textStorage [textStorage addLayoutManager:layoutManager]; // configure textContainer for the label NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height)]; textContainer.lineFragmentPadding = 0.0; textContainer.lineBreakMode = label.lineBreakMode; textContainer.maximumNumberOfLines = label.numberOfLines; // find the tapped character location and compare it to the specified range CGPoint locationOfTouchInLabel = [gesture locationInView:label]; [layoutManager addTextContainer:textContainer]; //(move here, not sure it that matter that calling this line after textContainer is set NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInLabel inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nil]; if (NSLocationInRange(indexOfCharacter, targetRange)) { return YES; } else { return NO; } }
Here's a drop-in Objective-C category that enables clickable links in existing UILabel.attributedText
strings, exploiting the existing NSLinkAttributeName
attribute.
@interface UILabel (GSBClickableLinks) <UIGestureRecognizerDelegate> @property BOOL enableLinks; @end #import <objc/runtime.h> static const void *INDEX; static const void *TAP; @implementation UILabel (GSBClickableLinks) - (void)setEnableLinks:(BOOL)enableLinks { UITapGestureRecognizer *tap = objc_getAssociatedObject(self, &TAP); // retreive tap if (enableLinks && !tap) { // add a gestureRegonzier to the UILabel to detect taps tap = [UITapGestureRecognizer.alloc initWithTarget:self action:@selector(openLink)]; tap.delegate = self; [self addGestureRecognizer:tap]; objc_setAssociatedObject(self, &TAP, tap, OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save tap } self.userInteractionEnabled = enableLinks; // note - when false UILAbel wont receive taps, hence disable links } - (BOOL)enableLinks { return (BOOL)objc_getAssociatedObject(self, &TAP); // ie tap != nil } // First check whether user tapped on a link within the attributedText of the label. // If so, then the our label's gestureRecogizer will subsequently fire, and open the corresponding NSLinkAttributeName. // If not, then the tap will get passed along, eg to the enclosing UITableViewCell... // Note: save which character in the attributedText was clicked so that we dont have to redo everything again in openLink. - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if (gestureRecognizer != objc_getAssociatedObject(self, &TAP)) return YES; // dont block other gestures (eg swipe) // Re-layout the attributedText to find out what was tapped NSTextContainer *textContainer = [NSTextContainer.alloc initWithSize:self.frame.size]; textContainer.lineFragmentPadding = 0; textContainer.maximumNumberOfLines = self.numberOfLines; textContainer.lineBreakMode = self.lineBreakMode; NSLayoutManager *layoutManager = NSLayoutManager.new; [layoutManager addTextContainer:textContainer]; NSTextStorage *textStorage = [NSTextStorage.alloc initWithAttributedString:self.attributedText]; [textStorage addLayoutManager:layoutManager]; NSUInteger index = [layoutManager characterIndexForPoint:[gestureRecognizer locationInView:self] inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:NULL]; objc_setAssociatedObject(self, &INDEX, @(index), OBJC_ASSOCIATION_RETAIN_NONATOMIC); // save index return (BOOL)[self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL]; // tapped on part of a link? } - (void)openLink { NSUInteger index = [objc_getAssociatedObject(self, &INDEX) unsignedIntegerValue]; // retrieve index NSURL *url = [self.attributedText attribute:NSLinkAttributeName atIndex:index effectiveRange:NULL]; if (url && [UIApplication.sharedApplication canOpenURL:url]) [UIApplication.sharedApplication openURL:url]; } @end
This would be a bit cleaner done via a UILabel subclass (ie none of the objc_getAssociatedObject mess), but if you are like me you prefer to avoid having to make unnecessary (3rd party) subclasses just to add some extra function to existing UIKit classes. Also, this has the beauty that it adds clickable-links to any existing UILabel, eg existing UITableViewCells
!
I've tried to make it as minimally invasive as possible by using the existing NSLinkAttributeName
attribute stuff already available in NSAttributedString. So its a simple as:
NSURL *myURL = [NSURL URLWithString:@"http://www.google.com"]; NSMutableAttributedString *myString = [NSMutableAttributedString.alloc initWithString:@"This string has a clickable link: "]; [myString appendAttributedString:[NSAttributedString.alloc initWithString:@"click here" attributes:@{NSLinkAttributeName:myURL}]]; ... myLabel.attributedText = myString; myLabel.enableLinks = YES; // yes, that's all! :-)
Basically, it works by adding a UIGestureRecognizer
to your UILabel. The hard work is done in gestureRecognizerShouldBegin:
, which re-layouts the attributedText string to find out which character was tapped on. If this character was part of a NSLinkAttributeName then the gestureRecognizer will subsequently fire, retrieve the corresponding URL (from the NSLinkAttributeName value), and open the link per the usual [UIApplication.sharedApplication openURL:url]
process.
Note – by doing all this in gestureRecognizerShouldBegin:
, if you dont happen to tap on a link in the label, the event is passed along. So, for example, your UITableViewCell will capture taps on links, but otherwise behave normally (select cell, unselect, scroll, …).
I've put this in a GitHub repository here . Adapted from Kai Burghardt's SO posting here .
Here's a Swift implementation that is about as minimal as possible that also includes touch feedback. 注意事项:
- You must set fonts in your NSAttributedStrings
- You can only use NSAttributedStrings!
- You must ensure your links cannot wrap (use non breaking spaces:
"\u{a0}"
) - You cannot change the lineBreakMode or numberOfLines after setting the text
- You create links by adding attributes with
.link
keys
。
public class LinkLabel: UILabel { private var storage: NSTextStorage? private let textContainer = NSTextContainer() private let layoutManager = NSLayoutManager() private var selectedBackgroundView = UIView() override init(frame: CGRect) { super.init(frame: frame) textContainer.lineFragmentPadding = 0 layoutManager.addTextContainer(textContainer) textContainer.layoutManager = layoutManager isUserInteractionEnabled = true selectedBackgroundView.isHidden = true selectedBackgroundView.backgroundColor = UIColor(white: 0, alpha: 0.3333) selectedBackgroundView.layer.cornerRadius = 4 addSubview(selectedBackgroundView) } public required convenience init(coder: NSCoder) { self.init(frame: .zero) } public override func layoutSubviews() { super.layoutSubviews() textContainer.size = frame.size } public override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesBegan(touches, with: event) setLink(for: touches) } public override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesMoved(touches, with: event) setLink(for: touches) } private func setLink(for touches: Set<UITouch>) { if let pt = touches.first?.location(in: self), let (characterRange, _) = link(at: pt) { let glyphRange = layoutManager.glyphRange(forCharacterRange: characterRange, actualCharacterRange: nil) selectedBackgroundView.frame = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer).insetBy(dx: -3, dy: -3) selectedBackgroundView.isHidden = false } else { selectedBackgroundView.isHidden = true } } public override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesCancelled(touches, with: event) selectedBackgroundView.isHidden = true } public override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { super.touchesEnded(touches, with: event) selectedBackgroundView.isHidden = true if let pt = touches.first?.location(in: self), let (_, url) = link(at: pt) { UIApplication.shared.open(url) } } private func link(at point: CGPoint) -> (NSRange, URL)? { let touchedGlyph = layoutManager.glyphIndex(for: point, in: textContainer) let touchedChar = layoutManager.characterIndexForGlyph(at: touchedGlyph) var range = NSRange() let attrs = attributedText!.attributes(at: touchedChar, effectiveRange: &range) if let urlstr = attrs[.link] as? String { return (range, URL(string: urlstr)!) } else { return nil } } public override var attributedText: NSAttributedString? { didSet { textContainer.maximumNumberOfLines = numberOfLines textContainer.lineBreakMode = lineBreakMode if let txt = attributedText { storage = NSTextStorage(attributedString: txt) storage!.addLayoutManager(layoutManager) layoutManager.textStorage = storage textContainer.size = frame.size } } } }
Translating @samwize 's Extension in Swift 4:
extension 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.location(in: label) let textBoundingBox = layoutManager.usedRect(for: textContainer) let textContainerOffset = CGPoint(x: (labelSize.width - textBoundingBox.size.width) * 0.5 - textBoundingBox.origin.x, y: (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y) let locationOfTouchInTextContainer = CGPoint(x:locationOfTouchInLabel.x - textContainerOffset.x, y: locationOfTouchInLabel.y - textContainerOffset.y) let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouchInTextContainer, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil) return NSLocationInRange(indexOfCharacter, targetRange) } }
To Setup the Recognizer (Once you colored the Text and Stuff)
// to Identify Touch: lblTermsOfUse.isUserInteractionEnabled = true lblTermsOfUse.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTapOnLabel(recognizer:))))
then, the Gesture Recognizer:
@objc func handleTapOnLabel(recognizer:UITapGestureRecognizer) { if let rangeTerms = lblAgreeToTerms.text?.range(of: NSLocalizedString("_onboarding_terms", comment: "terms")) { let range = lblAgreeToTerms.text?.nsRange(from: rangeTerms) if (recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: range!)) { goToTermsAndConditions() } } if let rangePolicy = lblAgreeToTerms.text?.range(of: NSLocalizedString("onboarding_Privacy", comment: "Privacy Policy")) { let range = lblAgreeToTerms.text?.nsRange(from: rangePolicy) if (recognizer.didTapAttributedTextInLabel(label: lblAgreeToTerms, inRange: range!)) { goToPrivacyPolicy() } } }
and FINALLY, you'll need the String Extension to convert the Range to NSRange:
extension String { func nsRange(from range: Range<Index>) -> NSRange { return NSRange(range, in: self) } }
TAGS #Swift2.0
I take inspiration on – excellent – @NAlexN's answer and I decide to write by myself a wrapper of UILabel.
I also tried TTTAttributedLabel but I can't make it works.
Hope you can appreciate this code, any suggestions are welcome!
import Foundation @objc protocol TappableLabelDelegate { optional func tappableLabel(tabbableLabel: TappableLabel, didTapUrl: NSURL, atRange: NSRange) } /// Represent a label with attributed text inside. /// We can add a correspondence between a range of the attributed string an a link (URL) /// By default, link will be open on the external browser @see 'openLinkOnExternalBrowser' class TappableLabel: UILabel { // MARK: - Public properties - var links: NSMutableDictionary = [:] var openLinkOnExternalBrowser = true var delegate: TappableLabelDelegate? // MARK: - Constructors - override func awakeFromNib() { super.awakeFromNib() self.enableInteraction() } override init(frame: CGRect) { super.init(frame: frame) self.enableInteraction() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } private func enableInteraction() { self.userInteractionEnabled = true self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: Selector("didTapOnLabel:"))) } // MARK: - Public methods - /** Add correspondence between a range and a link. - parameter url: url. - parameter range: range on which couple url. */ func addLink(url url: String, atRange range: NSRange) { self.links[url] = range } // MARK: - Public properties - /** Action rised on user interaction on label. - parameter tapGesture: gesture. */ func didTapOnLabel(tapGesture: UITapGestureRecognizer) { let labelSize = self.bounds.size; let layoutManager = NSLayoutManager() let textContainer = NSTextContainer(size: CGSizeZero) let textStorage = NSTextStorage(attributedString: self.attributedText!) // configure textContainer for the label textContainer.lineFragmentPadding = 0 textContainer.lineBreakMode = self.lineBreakMode textContainer.maximumNumberOfLines = self.numberOfLines textContainer.size = labelSize; // configure layoutManager and textStorage layoutManager.addTextContainer(textContainer) textStorage.addLayoutManager(layoutManager) // find the tapped character location and compare it to the specified range let locationOfTouchInLabel = tapGesture.locationInView(self) 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) for (url, value) in self.links { if let range = value as? NSRange { if NSLocationInRange(indexOfCharacter, range) { let url = NSURL(string: url as! String)! if self.openLinkOnExternalBrowser { UIApplication.sharedApplication().openURL(url) } self.delegate?.tappableLabel?(self, didTapUrl: url, atRange: range) } } } } }
Drop-in solution as a category on UILabel
(this assumes your UILabel
uses an attributed string with some NSLinkAttributeName
attributes in it):
@implementation UILabel (Support) - (BOOL)openTappedLinkAtLocation:(CGPoint)location { CGSize labelSize = self.bounds.size; NSTextContainer* textContainer = [[NSTextContainer alloc] initWithSize:CGSizeZero]; textContainer.lineFragmentPadding = 0.0; textContainer.lineBreakMode = self.lineBreakMode; textContainer.maximumNumberOfLines = self.numberOfLines; textContainer.size = labelSize; NSLayoutManager* layoutManager = [[NSLayoutManager alloc] init]; [layoutManager addTextContainer:textContainer]; NSTextStorage* textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedText]; [textStorage addAttribute:NSFontAttributeName value:self.font range:NSMakeRange(0, textStorage.length)]; [textStorage addLayoutManager:layoutManager]; CGRect textBoundingBox = [layoutManager usedRectForTextContainer: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(location.x - textContainerOffset.x, location.y - textContainerOffset.y); NSInteger indexOfCharacter = [layoutManager characterIndexForPoint:locationOfTouchInTextContainer inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:nullptr]; if (indexOfCharacter >= 0) { NSURL* url = [textStorage attribute:NSLinkAttributeName atIndex:indexOfCharacter effectiveRange:nullptr]; if (url) { [[UIApplication sharedApplication] openURL:url]; return YES; } } return NO; } @end
NSString *string = name; NSError *error = NULL; NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:(NSTextCheckingTypes)NSTextCheckingTypeLink | NSTextCheckingTypePhoneNumber error:&error]; NSArray *matches = [detector matchesInString:string options:0 range:NSMakeRange(0, [string length])]; for (NSTextCheckingResult *match in matches) { if (([match resultType] == NSTextCheckingTypePhoneNumber)) { NSString *phoneNumber = [match phoneNumber]; NSLog(@" Phone Number is :%@",phoneNumber); label.enabledTextCheckingTypes = NSTextCheckingTypePhoneNumber; } if(([match resultType] == NSTextCheckingTypeLink)) { NSURL *email = [match URL]; NSLog(@"Email is :%@",email); label.enabledTextCheckingTypes = NSTextCheckingTypeLink; } if (([match resultType] == NSTextCheckingTypeLink)) { NSURL *url = [match URL]; NSLog(@"URL is :%@",url); label.enabledTextCheckingTypes = NSTextCheckingTypeLink; } } label.text =name; }