如何在一个NSAttributedString中创build一个可点击的链接

使超链接在UITextView可点击是微不足道的。 您只需在IB的视图中设置“检测链接”checkbox,并检测http链接并将其转换为超链接。

但是,这仍然意味着用户看到的是“原始”链接。 RTF文件和HTML都允许你设置一个用户可读的string,并在其后面有一个链接。

将属性文本安装到文本视图(或UILabelUITextField )很容易。但是,如果该属性文本包含链接,则该链接不可点击。

有没有办法让用户可读的文本在UITextViewUILabelUITextField

SO的标记是不同的,但这里是一般的想法。 我想要的是这样的文字:

这个变形是由Face Dancer生成的,点击查看app store。

我唯一能得到的就是:

这个变形是用Face Dancer生成的,点击http://tinyurl.com/facedancer在app store中查看。

使用NSMutableAttributedString 。

 NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"]; [str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)]; yourTextView.attributedText = str; 

编辑

这不是直接关于这个问题,只是为了澄清, UITextFieldUILabel不支持开放的url。 如果你想使用UILabel的链接,你可以检查TTTAttributedLabel 。

另外,您应该将UITextView dataDetectorTypes值设置为UIDataDetectorTypeLinkUIDataDetectorTypeAll以便在单击时打开URL。 或者您可以按照注释中的build议使用委托方法。

我发现这非常有用,但是我需要在很多地方完成这个工作,所以我将自己的方法简单地扩展到NSMutableAttributedString

迅速

 import Foundation extension NSMutableAttributedString { public func setAsLink(textToFind:String, linkURL:String) -> Bool { let foundRange = self.mutableString.rangeOfString(textToFind) if foundRange.location != NSNotFound { self.addAttribute(NSLinkAttributeName, value: linkURL, range: foundRange) return true } return false } }` 

用法示例:

 let attributedString = NSMutableAttributedString(string:"I love stackoverflow!") let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com") if linkWasSet { // adjust more attributedString properties } 

`

Objective-C的

我只是要求在纯粹的Objective-C项目中做同样的事情,所以这里是Objective-C类。

 @interface NSMutableAttributedString (SetAsLinkSupport) - (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL; @end @implementation NSMutableAttributedString (SetAsLinkSupport) - (BOOL)setAsLink:(NSString*)textToFind linkURL:(NSString*)linkURL { NSRange foundRange = [self.mutableString rangeOfString:textToFind]; if (foundRange.location != NSNotFound) { [self addAttribute:NSLinkAttributeName value:linkURL range:foundRange]; return YES; } return NO; } @end 

用法示例:

 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"]; BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"]; if (linkWasSet) { // adjust more attributedString properties } 

ujell的解决scheme略有改进:如果你使用NSURL而不是NSString,你可以使用任何URL(例如自定义URL)

 NSURL *URL = [NSURL URLWithString: @"whatsapp://app"]; NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"start Whatsapp"]; [str addAttribute: NSLinkAttributeName value:URL range: NSMakeRange(0, str.length)]; yourTextField.attributedText = str; 

玩的开心!

我刚刚创build了一个UILabel的子类来专门解决这种用例。 您可以轻松地添加多个链接并为它们定义不同的处理程序。 它还支持突出显示按下的链接,当你触摸触摸反馈。 请参考https://github.com/null09264/FRHyperLabel

在你的情况下,代码可能会这样:

 FRHyperLabel *label = [FRHyperLabel new]; NSString *string = @"This morph was generated with Face Dancer, Click to view in the app store."; NSDictionary *attributes = @{NSFontAttributeName: [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]}; label.attributedText = [[NSAttributedString alloc]initWithString:string attributes:attributes]; [label setLinkForSubstring:@"Face Dancer" withLinkHandler:^(FRHyperLabel *label, NSString *substring){ [[UIApplication sharedApplication] openURL:aURL]; }]; 

截图示例 (处理程序设置为popup警报,而不是在这种情况下打开一个URL)

facedancer

我也有类似的要求,最初我使用UILabel,然后我意识到,UITextView更好。 我使UITextView行为像UILabel通过禁用交互和滚动,并为NSMutableAttributedString设置链接文本相同的Karl做了(+1)这是我的OBJ C版本

 -(void)setTextAsLink:(NSString*) textToFind withLinkURL:(NSString*) url { NSRange range = [self.mutableString rangeOfString:textToFind options:NSCaseInsensitiveSearch]; if (range.location != NSNotFound) { [self addAttribute:NSLinkAttributeName value:url range:range]; [self addAttribute:NSForegroundColorAttributeName value:[UIColor URLColor] range:range]; } } 

你可以使用下面的委托来处理这个动作

 - (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange { // do the task return YES; } 

使用UITextView它支持可点击的链接。 使用以下代码创build属性string

 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks]; 

然后设置UITextView文本如下

 NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor], NSUnderlineColorAttributeName: [UIColor blueColor], NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)}; customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links textView.attributedText = attributedString; 

确保在XIB中启用UITextView的“可选”行为。

我的问题的核心是,我想能够在文本视图/字段/标签中创build可点击的链接,而不必编写自定义代码来操纵文本并添加链接。 我希望它是数据驱动的。

我终于想出了如何去做。 问题是,IB不尊重embedded式链接。

此外, NSAttributedString的iOS版本NSAttributedString您从RTF文件初始化一个属性的string。 NSAttributedString的OS X版本确实有一个将RTF文件作为input的初始化程序。

NSAttributedString符合NSCoding协议,所以你可以把它转换成/从NSData

我创build了一个OS X命令行工具,它将一个RTF文件作为input,并输出一个包含NSCoding NSData的扩展名为.data的文件。 然后,将.data文件放到我的项目中,并添加几行代码,将文本加载到视图中。 代码看起来像这样(这个项目在Swift中):

 /* If we can load a file called "Dates.data" from the bundle and convert it to an attributed string, install it in the dates field. The contents contain clickable links with custom URLS to select each date. */ if let datesPath = NSBundle.mainBundle().pathForResource("Dates", ofType: "data"), let datesString = NSKeyedUnarchiver.unarchiveObjectWithFile(datesPath) as? NSAttributedString { datesField.attributedText = datesString } 

对于使用大量格式化文本的应用程序,我创build了一个构build规则,告诉Xcode给定文件夹中的所有.rtf文件都是源文件,.data文件是输出文件。 一旦我这样做了,我只需将.rtf文件添加到指定的目录(或编辑现有文件),构build过程就会发现它们是新的/更新的,运行命令行工具并将文件复制到应用程序包中。 它的作品非常漂亮。

我写了一篇博客文章,链接到一个示例(Swift)示范技术的项目。 你可以在这里看到它:

在您的应用中打开的UITextField中创build可点击的url

我写了一个方法,它添加一个链接(linkString)到一个string(fullString)与一个特定的url(urlString):

 - (NSAttributedString *)linkedStringFromFullString:(NSString *)fullString withLinkString:(NSString *)linkString andUrlString:(NSString *)urlString { NSRange range = [fullString rangeOfString:linkString options:NSLiteralSearch]; NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:fullString]; NSMutableParagraphStyle *paragraphStyle = NSMutableParagraphStyle.new; paragraphStyle.alignment = NSTextAlignmentCenter; NSDictionary *attributes = @{NSForegroundColorAttributeName:RGB(0x999999), NSFontAttributeName:[UIFont fontWithName:@"HelveticaNeue-Light" size:10], NSParagraphStyleAttributeName:paragraphStyle}; [str addAttributes:attributes range:NSMakeRange(0, [str length])]; [str addAttribute: NSLinkAttributeName value:urlString range:range]; return str; } 

你应该这样称呼它:

 NSString *fullString = @"A man who bought the Google.com domain name for $12 and owned it for about a minute has been rewarded by Google for uncovering the flaw."; NSString *linkString = @"Google.com"; NSString *urlString = @"http://www.google.com"; _youTextView.attributedText = [self linkedStringFromFullString:fullString withLinkString:linkString andUrlString:urlString]; 

更新:

我的问题有两个关键部分:

  1. 如何创build链接,其中可点击链接显示的文本与调用的实际链接不同:
  2. 如何设置链接而不必使用自定义代码来设置文本上的属性。

事实certificate,iOS 7添加了从NSData加载属性文本的能力。

我创build了一个UITextView的自定义子类,它利用了@IBInspectable属性,并允许直接在IB中从RTF文件加载内容。 您只需在IB中键入文件名,然后自定义类就可以完成。

以下是详细信息:

在iOS 7中, NSAttributedString获得了方法initWithData:options:documentAttributes:error: NSAttributedString 该方法允许您从NSData对象加载NSAttributedString。 您可以先将一个RTF文件加载到NSData中,然后使用initWithData:options:documentAttributes:error:将该NSData加载到文本视图中。 (请注意,还有一个方法initWithFileURL:options:documentAttributes:error:它将直接从文件加载一个属性string,但该方法在iOS 9中已被弃用。使用方法initWithData:options:documentAttributes:error:更安全initWithData:options:documentAttributes:error: ,这是不被弃用。

我想要一种方法,可以将可点击链接安装到我的文本视图中,而不必创build任何特定于我正在使用的链接的代码。

我想出的解决scheme是创build一个UITextView的自定义子类我调用RTF_UITextView并给它一个名为RTF_Filename@IBInspectable属性。 将@IBInspectable属性添加到属性会导致Interface Builder在“属性检查器”中公开该属性。 然后,您可以从IB设置该值,而无需自定义代码。

我还为我的自定义类添加了@IBDesignable属性。 @IBDesignable属性告诉Xcode它应该将自定义视图类的运行副本安装到“界面”构build器中,以便您可以在视图层次结构的graphics显示中看到它。 ()不幸的是,对于这个类, @IBDesignable属性似乎是片状的。 当我第一次添加它时,它就起作用了,但是随后我删除了我的文本视图的纯文本内容,并且在视图中的可点击链接消失了,我还没有把它们还回去。

我的RTF_UITextView的代码非常简单。 除了使用@IBDesignable属性添加@IBDesignable属性和@IBInspectable属性外,我还向@IBInspectable属性添加了didSet()方法。 didSet() RTF_Filename属性的值发生更改, didSet()方法就会被调用。 didSet()方法的代码非常简单:

 @IBDesignable class RTF_UITextView: UITextView { @IBInspectable var RTF_Filename: String? { didSet(newValue) { //If the RTF_Filename is nil or the empty string, don't do anything if ((RTF_Filename ?? "").isEmpty) { return } //Use optional binding to try to get an URL to the //specified filename in the app bundle. If that succeeds, try to load //NSData from the file. if let fileURL = NSBundle.mainBundle().URLForResource(RTF_Filename, withExtension: "rtf"), //If the fileURL loads, also try to load NSData from the URL. let theData = NSData(contentsOfURL: fileURL) { var aString:NSAttributedString do { //Try to load an NSAttributedString from the data try aString = NSAttributedString(data: theData, options: [:], documentAttributes: nil ) //If it succeeds, install the attributed string into the field. self.attributedText = aString; } catch { print("Nerp."); } } } } } 

请注意,如果@IBDesignable属性不能可靠地让您在“接口”构build器中预览样式化文本,那么将上面的代码设置为UITextView的扩展而不是自定义子类可能会更好。 这样,您可以在任何文本视图中使用它,而不必将文本视图更改为自定义类。

如果您需要支持iOS 7之前的iOS版本,请参阅我的其他答案。

你可以从gitHub下载一个包含这个新类的示例项目:

Github上的DatesInSwift演示项目

Swift版本:

  // Attributed String for Label let plainText = "Apkia" let styledText = NSMutableAttributedString(string: plainText) // Set Attribuets for Color, HyperLink and Font Size let attributes = [NSFontAttributeName: UIFont.systemFontOfSize(14.0), NSLinkAttributeName:NSURL(string: "http://apkia.com/")!, NSForegroundColorAttributeName: UIColor.blueColor()] styledText.setAttributes(attributes, range: NSMakeRange(0, plainText.characters.count)) registerLabel.attributedText = styledText 

我需要继续使用一个纯粹的UILabel,这个来自我的tap识别器(这是基于malex的回应: UILabel的触摸点的字符索引 )

 UILabel* label = (UILabel*)gesture.view; CGPoint tapLocation = [gesture locationInView:label]; // create attributed string with paragraph style from label NSMutableAttributedString* attr = [label.attributedText mutableCopy]; NSMutableParagraphStyle* paragraphStyle = [NSMutableParagraphStyle new]; paragraphStyle.alignment = label.textAlignment; [attr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, label.attributedText.length)]; // init text storage NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:attr]; NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; [textStorage addLayoutManager:layoutManager]; // init text container NSTextContainer *textContainer = [[NSTextContainer alloc] initWithSize:CGSizeMake(label.frame.size.width, label.frame.size.height+100) ]; textContainer.lineFragmentPadding = 0; textContainer.maximumNumberOfLines = label.numberOfLines; textContainer.lineBreakMode = label.lineBreakMode; [layoutManager addTextContainer:textContainer]; // find tapped character NSUInteger characterIndex = [layoutManager characterIndexForPoint:tapLocation inTextContainer:textContainer fractionOfDistanceBetweenInsertionPoints:NULL]; // process link at tapped character [attr enumerateAttributesInRange:NSMakeRange(characterIndex, 1) options:0 usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) { if (attrs[NSLinkAttributeName]) { NSString* urlString = attrs[NSLinkAttributeName]; NSURL* url = [NSURL URLWithString:urlString]; [[UIApplication sharedApplication] openURL:url]; } }]; 

Swift 4:

 var string = "Google" var attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!]) yourTextView.attributedText = attributedString 

Swift 3.1:

 var string = "Google" var attributedString = NSMutableAttributedString(string: string, attributes:[NSLinkAttributeName: URL(string: "http://www.google.com")!]) yourTextView.attributedText = attributedString 

只需findUITextView的无代码解决scheme: 在这里输入图像描述

启用检测 – >链接选项,URL和电子邮件将被检测到并可点击!

邓肯C的原始描述对IB行为的快速补充。 他写道:“在UITextView中使超级链接可点击是微不足道的,您只需在IB的视图中设置”检测链接“checkbox,并检测http链接并将其转换为超链接。

我的经验(至less在xcode 7中)是,你还必须取消点击“可编辑”的行为才能检测到可点击的url。

Swift 3的例子来检测属性文本水龙头的行为

https://stackoverflow.com/a/44226491/5516830

 let termsAndConditionsURL = TERMS_CONDITIONS_URL; let privacyURL = PRIVACY_URL; override func viewDidLoad() { super.viewDidLoad() self.txtView.delegate = self let str = "By continuing, you accept the Terms of use and Privacy policy" let attributedString = NSMutableAttributedString(string: str) var foundRange = attributedString.mutableString.range(of: "Terms of use") //mention the parts of the attributed text you want to tap and get an custom action attributedString.addAttribute(NSLinkAttributeName, value: termsAndConditionsURL, range: foundRange) foundRange = attributedString.mutableString.range(of: "Privacy policy") attributedString.addAttribute(NSLinkAttributeName, value: privacyURL, range: foundRange) txtView.attributedText = attributedString } func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool { let storyboard = UIStoryboard(name: "Main", bundle: nil) let vc = storyboard.instantiateViewController(withIdentifier: "WebView") as! SKWebViewController if (URL.absoluteString == termsAndConditionsURL) { vc.strWebURL = TERMS_CONDITIONS_URL self.navigationController?.pushViewController(vc, animated: true) } else if (URL.absoluteString == privacyURL) { vc.strWebURL = PRIVACY_URL self.navigationController?.pushViewController(vc, animated: true) } return false } 

就像智者一样,你可以添加任何你想要的与shouldInteractWith URL UITextFieldDelegate方法的行动。

干杯!!

如果你想在UITextView中使用NSLinkAttributeName,那么你可以考虑使用AttributedTextView库。 这是一个UITextView的子类,使得它很容易处理。 欲了解更多信息,请参阅: https : //github.com/evermeer/AttributedTextView

你可以使文本的任何部分像这样交互(其中textView1是一个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) 

而处理标签和提及,你可以使用这样的代码:

 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 }) } 

来自@AliSoftware OHAttributedStringAdditions的优秀库可以很容易地在UILabel添加链接,这里是文档: https : //github.com/AliSoftware/OHAttributedStringAdditions/wiki/link-in-UILabel

如果你想在你的UITextView活动子string,那么你可以使用我的扩展TextView …其简单和简单。 你可以编辑它,只要你想。

结果: 在这里输入图像描述

代码: https : //github.com/marekmand/ActiveSubstringTextView

 NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks]; NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor], NSUnderlineColorAttributeName: [UIColor blueColor], NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)}; customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links textView.attributedText = attributedString; 

关键点:

  • 确保在XIB中启用UITextView的“可选”行为。
  • 确保在XIB中禁用了UITextView的“可编辑”行为。

使用UITextView并为链接设置dataDetectorTypes。

喜欢这个:

 testTextView.editable = false testTextView.dataDetectorTypes = .link 

如果你想检测链接,电话号码,地址等..然后

 testTextView.dataDetectorTypes = .all