在UITextView中使超链接可点击是很简单的。您只需在IB中的视图上设置“检测链接”复选框,它就会检测HTTP链接并将其转换为超链接。

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

将带属性的文本安装到文本视图(或UILabel或UITextField)是很容易的。但是,当带有属性的文本包含链接时,它是不可点击的。

是否有一种方法使用户可读的文本点击在UITextView, UILabel或UITextField?

在SO上的标记是不同的,但这里是大致的思想。我想要的是这样的文本:

这个变形是由Face Dancer生成的,点击在应用程序商店中查看。

我唯一能得到的是:

这个变形是由Face Dancer生成的,点击http://example.com/facedancer在应用商店中查看。


当前回答

更新:

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

如何创建一个链接,其中显示的可点击链接的文本与被调用的实际链接不同: 如何在不使用自定义代码设置文本属性的情况下设置链接。

iOS 7增加了从NSData中加载属性文本的功能。

我创建了一个UITextView的自定义子类,它利用了@IBInspectable属性,让你直接在IB中加载RTF文件中的内容。

以下是细节:

In iOS 7, NSAttributedString gained the method initWithData:options:documentAttributes:error:. That method lets you load an NSAttributedString from an NSData object. You can first load an RTF file into NSData, then use initWithData:options:documentAttributes:error: to load that NSData into your text view. (Note that there is also a method initWithFileURL:options:documentAttributes:error: that will load an attributed string directly from a file, but that method was deprecated in iOS 9. It's safer to use the method initWithData:options:documentAttributes:error:, which wasn't deprecated.

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

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

I also added an @IBDesignable attribute to my custom class. The @IBDesignable attribute tells Xcode that it should install a running copy of your custom view class into Interface builder so you can see it in the graphical display of your view hierarchy. ()Unfortunately, for this class, the @IBDesignable property seems to be flaky. It worked when I first added it, but then I deleted the plain text contents of my text view and the clickable links in my view went away and I have not been able to get them back.)

RTF_UITextView的代码非常简单。除了添加@IBDesignable属性和@IBInspectable属性的RTF_Filename属性外,我还添加了一个didSet()方法到RTF_Filename属性。当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属性不能可靠地让你在Interface builder中预览样式文本,那么将上述代码设置为UITextView的扩展而不是自定义子类可能会更好。这样,您就可以在任何文本视图中使用它,而不必将文本视图更改为自定义类。

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

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

Github上的DatesInSwift演示项目

其他回答

如果你有@Karl Nosworthy和@esilver上面提供的问题,我已经更新了NSMutableAttributedString扩展到它的Swift 4版本。

extension NSMutableAttributedString {

public func setAsLink(textToFind:String, linkURL:String) -> Bool {

    let foundRange = self.mutableString.range(of: textToFind)
    if foundRange.location != NSNotFound {
         _ = NSMutableAttributedString(string: textToFind)
        // Set Attribuets for Color, HyperLink and Font Size
        let attributes = [NSFontAttributeName: UIFont.bodyFont(.regular, shouldResize: true), NSLinkAttributeName:NSURL(string: linkURL)!, NSForegroundColorAttributeName: UIColor.blue]

        self.setAttributes(attributes, range: foundRange)
        return true
    }
    return false
  }
}

使用NSMutableAttributedString。

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

编辑:

这不是直接关于这个问题,只是澄清一下,UITextField和UILabel不支持打开url。如果你想使用UILabel链接,你可以检查TTTAttributedLabel。

此外,你应该设置你的UITextView的dataDetectorTypes值为UIDataDetectorTypeLink或UIDataDetectorTypeAll以在单击时打开url。或者你也可以按照评论中的建议使用委托方法。

我只是创建了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];
}];

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

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中使用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 })
    }