ios 如何在 NSAttributedString 中创建可点击的链接?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/21629784/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I make a clickable link in an NSAttributedString?
提问by Duncan C
It's trivial to make hyperlinks clickable in a UITextView
. You just set the "detect links" checkbox on the view in IB, and it detects HTTP links and turns them into hyperlinks.
让超链接在UITextView
. 您只需在 IB 中的视图上设置“检测链接”复选框,它就会检测 HTTP 链接并将它们转换为超链接。
However, that still means that what the user sees is the "raw" link. RTF files and HTML both allow you to set up a user-readable string with a link "behind" it.
但是,这仍然意味着用户看到的是“原始”链接。RTF 文件和 HTML 都允许您设置用户可读的字符串,并在其“后面”带有链接。
It's easy to install attributed text into a text view (or a UILabel
or UITextField
, for that matter.) However, when that attributed text includes a link, it is not clickable.
将属性文本安装到文本视图(或UILabel
或UITextField
,就此而言)很容易。但是,当该属性文本包含链接时,它是不可点击的。
Is there a way to make user-readable text clickable in a UITextView
, UILabel
or UITextField
?
有没有办法让用户可读的文本可以在UITextView
,UILabel
或 中点击UITextField
?
The markup is different on SO, but here is the general idea. What I want is text like this:
SO 上的标记有所不同,但这是总体思路。我想要的是这样的文字:
This morph was generated with Face Dancer, Click to view in the app store.
这个变形是用Face Dancer生成的,点击应用商店查看。
The only thing I can get is this:
我唯一能得到的是:
This morph was generated with Face Dancer, Click on http://example.com/facedancerto view in the app store.
这个变形是用 Face Dancer 生成的,点击http://example.com/facedancer在应用商店中查看。
采纳答案by Duncan C
The heart of my question was that I wanted to be able to create clickable links in text views/fields/labels without having to write custom code to manipulate the text and add the links. I wanted it to be data-driven.
我的问题的核心是我希望能够在文本视图/字段/标签中创建可点击的链接,而无需编写自定义代码来操作文本和添加链接。我希望它是数据驱动的。
I finally figured out how to do it. The issue is that IB doesn't honor embedded links.
我终于想出了怎么做。问题是 IB 不尊重嵌入式链接。
Furthermore, the iOS version of NSAttributedString
doesn't let you initialize an attributed string from an RTF file. The OS X version of NSAttributedString
doeshave an initializer that takes an RTF file as input.
此外,iOS 版本NSAttributedString
不允许您从 RTF 文件初始化属性字符串。OS X 版本NSAttributedString
确实有一个将 RTF 文件作为输入的初始化程序。
NSAttributedString
conforms to the NSCoding protocol, so you can convert it to/from NSData
NSAttributedString
符合 NSCoding 协议,因此您可以将其转换为/从 NSData
I created an OS X command line tool that takes an RTF file as input and outputs a file with the extension .data that contains the NSData from NSCoding. I then put the .data file into my project and add a couple of lines of code that loads the text into the view. The code looks like this (this project was in Swift) :
我创建了一个 OS X 命令行工具,它将 RTF 文件作为输入并输出一个扩展名为 .data 的文件,其中包含来自 NSCoding 的 NSData。然后,我将 .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
}
For apps that use a lot of formatted text, I create a build rule that tells Xcode that all the .rtf files in a given folder are source and the .data files are the output. Once I do that, I simply add .rtf files to the designated directory, (or edit existing files) and the build process figures out that they are new/updated, runs the command line tool, and copies the files into the app bundle. It works beautifully.
对于使用大量格式化文本的应用程序,我创建了一个构建规则,告诉 Xcode 给定文件夹中的所有 .rtf 文件都是源文件,而 .data 文件是输出文件。一旦我这样做了,我只需将 .rtf 文件添加到指定目录(或编辑现有文件),构建过程就会确定它们是新的/更新的,运行命令行工具,并将文件复制到应用程序包中。它工作得很好。
I wrote a blog post that links to a sample (Swift) project demonstrating the technique. You can see it here:
我写了一篇博客文章,链接到演示该技术的示例 (Swift) 项目。你可以在这里看到它:
Creating clickable URLs in a UITextField that open in your app
回答by ujell
Use NSMutableAttributedString.
NSMutableAttributedString * str = [[NSMutableAttributedString alloc] initWithString:@"Google"];
[str addAttribute: NSLinkAttributeName value: @"http://www.google.com" range: NSMakeRange(0, str.length)];
yourTextView.attributedText = str;
Edit:
编辑:
This is not directly about the question but just to clarify, UITextField
and UILabel
does not support opening URLs. If you want to use UILabel
with links you can check TTTAttributedLabel.
这不是直接关于问题而只是为了澄清,UITextField
并且UILabel
不支持打开 URL。如果您想UILabel
与链接一起使用,您可以检查TTTAttributedLabel。
Also you should set dataDetectorTypes
value of your UITextView
to UIDataDetectorTypeLink
or UIDataDetectorTypeAll
to open URLs when clicked. Or you can use delegate method as suggested in the comments.
您还应该设置dataDetectorTypes
您的UITextView
toUIDataDetectorTypeLink
或UIDataDetectorTypeAll
to 打开 URL 的值。或者您可以按照评论中的建议使用委托方法。
回答by Karl Nosworthy
I found this really useful but I needed to do it in quite a few places so I've wrapped my approach up in a simple extension to NSMutableAttributedString
:
我发现这真的很有用,但我需要在很多地方这样做,所以我将我的方法包装在一个简单的扩展中NSMutableAttributedString
:
Swift 3
斯威夫特 3
extension NSMutableAttributedString {
public func setAsLink(textToFind:String, linkURL:String) -> Bool {
let foundRange = self.mutableString.range(of: textToFind)
if foundRange.location != NSNotFound {
self.addAttribute(.link, value: linkURL, range: foundRange)
return true
}
return false
}
}
Swift 2
斯威夫特 2
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
}
}
Example usage:
用法示例:
let attributedString = NSMutableAttributedString(string:"I love stackoverflow!")
let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com")
if linkWasSet {
// adjust more attributedString properties
}
Objective-C
目标-C
I've just hit a requirement to do the same in a pure Objective-C project, so here's the Objective-C category.
我刚刚遇到了在纯 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
Example usage:
用法示例:
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];
BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];
if (linkWasSet) {
// adjust more attributedString properties
}
Make Sure that the NSTextField's Behavior attribute is set as Selectable.
回答by Jinghan Wang
I just created a subclass of UILabel to specially address such use cases. You can add multiple links easily and define different handlers for them. It also supports highlighting the pressed link when you touch down for touch feedback. Please refer to https://github.com/null09264/FRHyperLabel.
我刚刚创建了 UILabel 的一个子类来专门解决此类用例。您可以轻松添加多个链接并为它们定义不同的处理程序。它还支持在您触摸反馈时突出显示按下的链接。请参考https://github.com/null09264/FRHyperLabel。
In your case, the code may like this:
在你的情况下,代码可能是这样的:
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];
}];
Sample Screenshot(the handler is set to pop an alert instead of open a url in this case)
示例屏幕截图(在这种情况下,处理程序设置为弹出警报而不是打开网址)
回答by Hans One
Minor improvement to ujell's solution: If you use NSURL instead of a NSString, you can use any URL (e.g. custom urls)
ujell 解决方案的小改进:如果您使用 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;
Have fun!
玩得开心!
回答by Bill Chan
Swift 4:
斯威夫特 4:
var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSAttributedStringKey.link: URL(string: "http://www.google.com")!])
yourTextView.attributedText = attributedString
Swift 3.1:
斯威夫特 3.1:
var string = "Google"
var attributedString = NSMutableAttributedString(string: string, attributes:[NSLinkAttributeName: URL(string: "http://www.google.com")!])
yourTextView.attributedText = attributedString
回答by anoop4real
I too had a similar requirement, initially I used UILabel and then I realized that UITextView is better. I made UITextView behave like UILabel by disabling interaction and scrolling and made a category method for NSMutableAttributedString
to set link to text same as what Karl had done (+1 for that) this is my obj c version
我也有类似的需求,最初我使用 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];
}
}
you can use the below delegate then to handle the action
您可以使用下面的委托来处理操作
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)url inRange:(NSRange)characterRange
{
// do the task
return YES;
}
回答by Nitheesh George
Use UITextView it supports clickable Links. Create attributed string using the following code
使用 UITextView 它支持可点击的链接。使用以下代码创建属性字符串
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];
Then set UITextView text as follows
然后设置 UITextView 文字如下
NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],
NSUnderlineColorAttributeName: [UIColor blueColor],
NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};
customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;
Make sure that you enable "Selectable" behavior of the UITextView in XIB.
确保在 XIB 中启用 UITextView 的“可选”行为。
回答by Akila Wasala
Swift 3 example to detect actions on attributed text taps
Swift 3 示例,用于检测属性文本点击上的操作
https://stackoverflow.com/a/44226491/5516830
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
}
Like wise you can add any action you want with shouldInteractWith URL
UITextFieldDelegate method.
同样,您可以使用shouldInteractWith URL
UITextFieldDelegate 方法添加您想要的任何操作。
Cheers!!
干杯!!
回答by Dody Rachmat Wicaksono
The quick answer is using UITextView instead of UILabel. You need to enable Selectable
and disable Editable
.
快速答案是使用 UITextView 而不是 UILabel。您需要启用Selectable
和禁用Editable
.
Then disable scroll indicators and bounces.
然后禁用滚动指示器和弹跳。
My solution using NSMutableAttributedString
from html string NSHTMLTextDocumentType
我的解决方案使用NSMutableAttributedString
来自 html 字符串NSHTMLTextDocumentType
NSString *s = @"<p><a href='https://itunes.apple.com/us/app/xxxx/xxxx?mt=8'>https://itunes.apple.com/us/app/xxxx/xxxx?mt=8</a></p>";
NSMutableAttributedString *text = [[NSMutableAttributedString alloc]
initWithData: [s dataUsingEncoding:NSUnicodeStringEncoding]
options: @{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType }
documentAttributes: nil
error: nil
];
cell.content.attributedText = text;