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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 23:23:04  来源:igfitidea点击:

How can I make a clickable link in an NSAttributedString?

iosobjective-chyperlinkuitextviewnsattributedstring

提问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 UILabelor UITextField, for that matter.) However, when that attributed text includes a link, it is not clickable.

将属性文本安装到文本视图(或UILabelUITextField,就此而言)很容易。但是,当该属性文本包含链接时,它是不可点击的。

Is there a way to make user-readable text clickable in a UITextView, UILabelor 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 NSAttributedStringdoesn't let you initialize an attributed string from an RTF file. The OS X version of NSAttributedStringdoeshave an initializer that takes an RTF file as input.

此外,iOS 版本NSAttributedString不允许您从 RTF 文件初始化属性字符串。OS X 版本NSAttributedString确实有一个将 RTF 文件作为输入的初始化程序。

NSAttributedStringconforms 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

在应用程序中打开的 UITextField 中创建可点击的 URL

回答by ujell

Use NSMutableAttributedString.

使用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, UITextFieldand UILabeldoes not support opening URLs. If you want to use UILabelwith links you can check TTTAttributedLabel.

这不是直接关于问题而只是为了澄清,UITextField并且UILabel不支持打开 URL。如果您想UILabel与链接一起使用,您可以检查TTTAttributedLabel

Also you should set dataDetectorTypesvalue of your UITextViewto UIDataDetectorTypeLinkor UIDataDetectorTypeAllto open URLs when clicked. Or you can use delegate method as suggested in the comments.

您还应该设置dataDetectorTypes您的UITextViewtoUIDataDetectorTypeLinkUIDataDetectorTypeAllto 打开 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. Xcode NSTextField behavior attribute

确保 NSTextField 的 Behavior 属性设置为 Selectable。 Xcode NSTextField 行为属性

回答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)

示例屏幕截图(在这种情况下,处理程序设置为弹出警报而不是打开网址)

facedancer

面对舞者

回答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 NSMutableAttributedStringto 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 URLUITextFieldDelegate method.

同样,您可以使用shouldInteractWith URLUITextFieldDelegate 方法添加您想要的任何操作。

Cheers!!

干杯!!

回答by Dody Rachmat Wicaksono

The quick answer is using UITextView instead of UILabel. You need to enable Selectableand disable Editable.

快速答案是使用 UITextView 而不是 UILabel。您需要启用Selectable和禁用Editable.

Then disable scroll indicators and bounces.

然后禁用滚动指示器和弹跳。

Screen Shot

截屏

Screen Shot

截屏

My solution using NSMutableAttributedStringfrom 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;