如何在NSAttributedString中创建可单击的链接?

Dun*_*n C 186 objective-c hyperlink uitextview nsattributedstring ios

在a中点击超链接是微不足道的UITextView.您只需在IB中的视图上设置"检测链接"复选框,它就会检测HTTP链接并将其转换为超链接.

但是,这仍然意味着用户看到的是"原始"链接.RTF文件和HTML都允许您设置一个用户可读的字符串,其中包含一个"后面"的链接.

可以很容易地将属性文本安装到文本视图中(或者,UILabel或者UITextField就此而言).但是,当该属性文本包含链接时,它不可单击.

有没有办法让用户可读的文本可以点击UITextView,UILabelUITextField

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

此变形是使用Face Dancer生成的,单击可在应用商店中查看.

我唯一能得到的是:

使用Face Dancer生成此变形,单击http://example.com/facedancer以在应用商店中查看.

uje*_*ell 150

使用NSMutableAttributedString.

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

编辑:

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

你还应该设置dataDetectorTypes你的价值UITextViewUIDataDetectorTypeLinkUIDataDetectorTypeAll点击时打开的网址.或者您可以使用注释中建议的委托方法.

  • 是的,它正在工作,只需将它放在UITextView中并覆盖委托方法: - (BOOL)textView:(UITextView*)textView shouldInteractWithURL:(NSURL*)url inRange:(NSRange)characterRange (7认同)
  • @saboehnke你的意思是在链接点击时调用方法?如果是这样实现委托方法,给出一个虚拟url作为属性并在` - (BOOL)中调用你的方法textView:(UITextView*)textView shouldInteractWithURL:(NSURL*)URL inRange:(NSRange)characterRange` (6认同)
  • 我不知道它是如何工作的.属性的值应该是````NSURL````的类型.----` [str addAttribute:NSLinkAttributeName value:[NSURL URLWithString:@"http://www.google.com"]范围:NSMakeRange(0,str.length)];` (2认同)

Kar*_*thy 124

我发现这非常有用,但我需要在很多地方做到这一点,所以我将我的方法包含在一个简单的扩展中NSMutableAttributedString:

斯威夫特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
    }
}
Run Code Online (Sandbox Code Playgroud)

斯威夫特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
   }
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

let attributedString = NSMutableAttributedString(string:"I love stackoverflow!")
let linkWasSet = attributedString.setAsLink("stackoverflow", linkURL: "http://stackoverflow.com")

if linkWasSet {
    // adjust more attributedString properties
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

用法示例:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:"I love stackoverflow!"];

BOOL linkWasSet = [attributedString setAsLink:@"stackoverflow" linkURL:@"http://stackoverflow.com"];

if (linkWasSet) {
    // adjust more attributedString properties
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这工作正常.只是想说你需要让你的UITextView可选,以允许链接可点击 (7认同)
  • @ioop.我在上面的原帖中添加了一个非常小的例子,希望有所帮助. (3认同)

Jin*_*ang 33

我刚刚创建了一个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];
}];
Run Code Online (Sandbox Code Playgroud)

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

facedancer


Han*_*One 26

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;
Run Code Online (Sandbox Code Playgroud)

玩得开心!


ano*_*eal 19

我也有类似的要求,最初我使用UILabel然后我意识到UITextView更好.我通过禁用交互和滚动使UITextView表现得像UILabel,并使用类别方法NSMutableAttributedString设置链接到文本,就像Karl所做的那样(+1为此)这是我的对象版本

-(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];
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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


Bil*_*han 19

斯威夫特4:

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

yourTextView.attributedText = attributedString
Run Code Online (Sandbox Code Playgroud)

Swift 3.1:

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

yourTextView.attributedText = attributedString
Run Code Online (Sandbox Code Playgroud)

  • 这个答案按原样完美运行。似乎不需要其他答案使用的任何着色或自定义子类。 (2认同)

Nit*_*rge 16

使用UITextView它支持可点击的链接.使用以下代码创建属性字符串

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:strSomeTextWithLinks];
Run Code Online (Sandbox Code Playgroud)

然后按如下方式设置UITextView文本

NSDictionary *linkAttributes = @{NSForegroundColorAttributeName: [UIColor redColor],

                                 NSUnderlineColorAttributeName: [UIColor blueColor],

                                 NSUnderlineStyleAttributeName: @(NSUnderlinePatternSolid)};

customTextView.linkTextAttributes = linkAttributes; // customizes the appearance of links
textView.attributedText = attributedString;
Run Code Online (Sandbox Code Playgroud)

确保在XIB中启用UITextView的"可选"行为.

  • 我认为这是最好的解决方案!**关于启用"可选择"的说明非常重要!** (15认同)

Dun*_*n C 13

我的问题的核心是我希望能够在文本视图/字段/标签中创建可点击链接,而无需编写自定义代码来操作文本和添加链接.我希望它是数据驱动的.

我终于想出了怎么做.问题是IB不尊重嵌入式链接.

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

NSAttributedString 符合NSCoding协议,因此您可以将其转换为NSData或从NSData转换

我创建了一个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
}
Run Code Online (Sandbox Code Playgroud)

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

我写了一篇博文,链接到展示该技术的示例(Swift)项目.你可以在这里看到它:

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


Dod*_*ono 12

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

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

截屏

截屏

我使用NSMutableAttributedStringfrom html string 的解决方案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;
Run Code Online (Sandbox Code Playgroud)


Aki*_*ala 10

Swift 3示例,用于检测属性文本抽头上的操作

/sf/answers/3095854401/

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
}
Run Code Online (Sandbox Code Playgroud)

同样,你可以使用shouldInteractWith URLUITextFieldDelegate方法添加任何你想要的动作.

干杯!!


wzb*_*zon 5

我已经编写了一种方法,该方法将link(linkString)添加到具有特定url(urlString)的字符串(fullString):

- (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;
}
Run Code Online (Sandbox Code Playgroud)

您应该这样称呼它:

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];
Run Code Online (Sandbox Code Playgroud)


mas*_*sty 5

我需要继续使用纯 UILabel,因此从我的点击识别器中调用它(这是基于在这里的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];
                                          }
                                      }];
Run Code Online (Sandbox Code Playgroud)


Ada*_*G J 5

使用 UITextView 并设置 Link 的 dataDetectorTypes。

像这样:

testTextView.editable = false 
testTextView.dataDetectorTypes = .link
Run Code Online (Sandbox Code Playgroud)

如果你想检测链接、电话号码、地址等...那么

testTextView.dataDetectorTypes = .all
Run Code Online (Sandbox Code Playgroud)

  • 不可以。这只能让您使链接可点击。我的问题是让任意文本(如“单击此处”)可点击,而不是像“http://somedomain/someurl?param=value”这样的 URL (4认同)