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

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

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

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

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

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

我唯一能得到的是:

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


当前回答

我写了一个方法,添加一个链接(linkString)到一个字符串(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];

其他回答

只需为UITextView找到一个无代码的解决方案:

启用检测->链接选项,URL和电子邮件也将被检测和点击!

在Swift 5.5中

自从Swift 5.5以来,NSAttributedString是完全可本地化的,甚至不需要定义字符的数量就很容易使用。

func attributedStringBasics(important: Bool) {
    var buy = AttributedString("Buy a new iPhone!")
    buy.font = .body.bold()

    var website = AttributedString("Visit Apple")
    website.font = .body.italic()
    website.link = URL(string: "http://www.apple.com")

    var container = AttributeContainer()
    if important {
        container.foregroundColor = .red
        container.underlineColor = .primary
    } else {
        container.foregroundColor = .primary
    }

    buy.mergeAttributes(container)
    website.mergeAttributes(container)

    print(buy)
    print(website)
}

更新:

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

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

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演示项目

如果你想在你的UITextView活动子字符串,那么你可以使用我的扩展TextView…它简短而简单。你可以随意编辑。

结果:

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

我发现这真的很有用,但我需要在相当多的地方这样做,所以我已经包装了我的方法在一个简单的扩展到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
    }
}

斯威夫特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
   }
}

使用示例:

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
}

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