How can I make a clickable link in an NSAttributed

2019-01-01 06:50发布

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.

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.

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.

Is there a way to make user-readable text clickable in a UITextView, UILabel or UITextField?

The markup is different on SO, but here is the general idea. What I want is text like this:

This morph was generated with Face Dancer, Click to view in the app store.

The only thing I can get is this:

This morph was generated with Face Dancer, Click on http://example.com/facedancer to view in the app store.

21条回答
ら面具成の殇う
2楼-- · 2019-01-01 06:55

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:

Swift 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

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

I've just hit a requirement to do the same in a pure Objective-C project, so here's the Objective-C category.

@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
}
查看更多
人气声优
3楼-- · 2019-01-01 06:56

I needed to keep using a pure UILabel, so called this from my tap recognizer (this is based on malex's response here: Character index at touch point for 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];
                                          }
                                      }];
查看更多
临风纵饮
4楼-- · 2019-01-01 06:56

The excellent library from @AliSoftware OHAttributedStringAdditions makes it easy to add links in UILabel here is the documentation: https://github.com/AliSoftware/OHAttributedStringAdditions/wiki/link-in-UILabel

查看更多
流年柔荑漫光年
5楼-- · 2019-01-01 06:56

Swift 4:

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

yourTextView.attributedText = attributedString

Swift 3.1:

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

yourTextView.attributedText = attributedString
查看更多
刘海飞了
6楼-- · 2019-01-01 06:56

I have written a method, that adds a link(linkString) to a string (fullString) with a certain 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;
}

You should call it like this:

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];
查看更多
美炸的是我
7楼-- · 2019-01-01 06:57

if you want active substring in your UITextView then you can use my extended TextView... its short and simple. You can edit it as you want.

result: enter image description here

code: https://github.com/marekmand/ActiveSubstringTextView

查看更多
登录 后发表回答