UITextView linkTextAttributes字体属性未应用于NSAttributedString

时间:2016-01-28 05:46:55

标签: swift uitextview nsattributedstring

我有一个由HTML生成的sum += i;,其中包含一些链接。属性字符串显示在UITextView中。我希望为链接应用不同的字体样式,并为此设置NSAttributedString。我已添加linkTextAttributesNSForegroundColorAttributeNameNSFontAttributeName。由于某种原因,应用了前景色,但其余属性不是。

NSUnderlineStyleAttributeName

是否有其他人遇到此问题,如何更改链接的字体样式而无需将原生HTML应用于原始HTML?感谢。

8 个答案:

答案 0 :(得分:18)

不确定为什么linkTextAttributes不适用于字体名称。但是我们可以通过更新NSAttributedString的链接属性来实现这一点。请检查以下代码。

        do {
        let htmlStringCode = "For more info <a href=\"http://www.samplelink.com/subpage.php?id=8\">Click here</a>"

        let string = try NSAttributedString(data: htmlStringCode.dataUsingEncoding(NSUTF8StringEncoding)!, options: [NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding], documentAttributes: nil)

        let newString = NSMutableAttributedString(attributedString: string)
        string.enumerateAttributesInRange(NSRange.init(location: 0, length: string.length), options: .Reverse) { (attributes : [String : AnyObject], range:NSRange, _) -> Void in
            if let _ = attributes[NSLinkAttributeName] {
                newString.removeAttribute(NSFontAttributeName, range: range)
                newString.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(30), range: range)
            }
        }
        textField.attributedText = newString
        textField.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.redColor(), NSUnderlineStyleAttributeName : NSUnderlineStyle.StyleNone.rawValue]

    }catch {
    }

这是 objective-C 代码:

NSDictionary *options = @{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType};
NSData *data = [html dataUsingEncoding:NSUnicodeStringEncoding allowLossyConversion:NO];

NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil];
NSMutableAttributedString *attributedStringWithBoldLinks = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];

[attributedString enumerateAttributesInRange:NSMakeRange(0, attributedString.string.length) options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary<NSString *,id> * _Nonnull attrs, NSRange range, BOOL * _Nonnull stop) {

    if ([attrs objectForKey:NSLinkAttributeName]) {
        [attributedStringWithBoldLinks removeAttribute:NSFontAttributeName range:range];
        [attributedStringWithBoldLinks addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"YourFont-Bold" size:16.0] range:range];
    }
}];

self.linkTextAttributes = @{NSForegroundColorAttributeName : [UIColor redColor]};

self.attributedText = attributedStringWithBoldLinks;

Screenshot

答案 1 :(得分:3)

出于某种原因,使用enumerateAttributesInRange:进行后处理归因字符串对我来说不起作用。

所以我使用NSDataDetector检测链接,enumerateMatchesInString:options:range:usingBlock:将我的样式用于字符串中的所有链接。 这是我的处理功能:

+ (void) postProcessTextViewLinksStyle:(UITextView *) textView {
   NSAttributedString *attributedString = textView.attributedText;
   NSMutableAttributedString *attributedStringWithItalicLinks = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];

   NSError *error = nil;
   NSDataDetector *detector = [NSDataDetector dataDetectorWithTypes:NSTextCheckingTypeLink
                                                           error:&error];

   [detector enumerateMatchesInString:[attributedString string]
                           options:0
                             range:NSMakeRange(0, [attributedString length])
                        usingBlock:^(NSTextCheckingResult *match, NSMatchingFlags flags, BOOL *stop){
                            NSRange matchRange = [match range];
                            NSLog(@"Links style postprocessing. Range (from: %lu, length: %lu )", (unsigned long)matchRange.location, (unsigned long)matchRange.length);
                            if ([match resultType] == NSTextCheckingTypeLink) {                                    
                                [attributedStringWithItalicLinks removeAttribute:NSFontAttributeName range:matchRange];
                                [attributedStringWithItalicLinks addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"YourFont-Italic" size:14.0f] range:matchRange];
                            }
                        }];

   textView.attributedText = attributedStringWithItalicLinks;
}

答案 2 :(得分:3)

这是@Arun Ammannaya

的快速3更新答案
guard let font = UIFont.init(name: "Roboto-Regular", size: 15) else {
    return
}
let newString = NSMutableAttributedString(attributedString: string)
let range = NSRange(location:0,length: string.length)
string.enumerateAttributes(in: range, options: .reverse, using: { (attributes : [String : Any], range : NSRange, _) -> Void in
    if let _ = attributes[NSLinkAttributeName] {
        newString.removeAttribute(NSFontAttributeName, range: range)
        newString.addAttribute(NSFontAttributeName, value: font, range: range)
    }
})
errorTextView.attributedText = newString
errorTextView.linkTextAttributes = [NSForegroundColorAttributeName : UIColor.green, NSUnderlineStyleAttributeName : NSUnderlineStyle.styleSingle.rawValue]

这是@CTiPKA的Swift 3解决方案,我更喜欢它,因为它避免了HTML

guard let attributedString = errorTextView.attributedText else {
    return
}
guard let font = UIFont.init(name: "Roboto-Regular", size: 15) else {
   return
}
let newString = NSMutableAttributedString(attributedString: attributedString)

let types: NSTextCheckingResult.CheckingType = [.link, .phoneNumber]

guard let linkDetector = try? NSDataDetector(types: types.rawValue) else { return  }
let range = NSRange(location:0,length: attributedString.length)

linkDetector.enumerateMatches(in: attributedString.string, options: [], range: range, using: { (match : NSTextCheckingResult?,
    flags : NSRegularExpression.MatchingFlags, stop) in

    if let matchRange = match?.range {
        newString.removeAttribute(NSFontAttributeName, range: matchRange)
        newString.addAttribute(NSFontAttributeName, value: font, range: matchRange)
    }
})
errorTextView.attributedText = newString

答案 3 :(得分:3)

针对Swift 4进行了更新:

let originalText = NSMutableAttributedString(attributedString: textView.attributedText)
var newString = NSMutableAttributedString(attributedString: textView.attributedText)

originalText.enumerateAttributes(in: NSRange(0..<originalText.length), options: .reverse) { (attributes, range, pointer) in
    if let _ = attributes[NSAttributedString.Key.link] {
        newString.removeAttribute(NSAttributedString.Key.font, range: range)
        newString.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 30), range: range)
    }
}

self.textView.attributedText = newString // updates the text view on the vc

答案 4 :(得分:2)

对于简单的情况:(没有可怕的HTML使用):

    let linkTextAttributes : [String : Any] = [
        NSForegroundColorAttributeName: UIColor.red,
        NSUnderlineColorAttributeName: UIColor.magenta,
        NSUnderlineStyleAttributeName: NSUnderlineStyle.patternSolid.rawValue
    ]

    self.infoText.linkTextAttributes = linkTextAttributes

答案 5 :(得分:0)

如果使用html,还有一种简单的方法可以为文本应用样式 - 您只需在html代码中添加样式即可。然后您不必担心为文本设置属性。例如:

NSString *html = [NSString stringWithFormat:@"<p style=\"font-family: Your-Font-Name; color: #344052; font-size: 15px\"><a style=\"color: #0A9FD2\" href=\"https://examplelink.com\">%@</a> %@ on %@</p>", name, taskName, timeString];
NSDictionary *options = @{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType};
NSData *data = [html dataUsingEncoding:NSUTF8StringEncoding];

NSAttributedString *attributedString = [[NSAttributedString alloc] initWithData:data options:options documentAttributes:nil error:nil];

答案 6 :(得分:0)

Swift 5 版本的Ryan Heitner的出色答案:

guard let attributedString = textView.attributedText else { return }
guard let linkFont = UIFont(name: "HelveticaNeue-Bold", size: 20.0) else { return }

let newString = NSMutableAttributedString(attributedString: attributedString)
let types: NSTextCheckingResult.CheckingType = [.link, .phoneNumber]

guard let linkDetector = try? NSDataDetector(types: types.rawValue) else { return }
let range = NSRange(location: 0, length: attributedString.length)

linkDetector.enumerateMatches(in: attributedString.string, options: [], range: range, using: { (match: NSTextCheckingResult?, flags: NSRegularExpression.MatchingFlags, stop) in
    if let matchRange = match?.range {
        newString.removeAttribute(NSAttributedString.Key.font, range: matchRange)
        newString.addAttribute(NSAttributedString.Key.font, value: linkFont, range: matchRange)
    }
})

textView.attributedText = newString

答案 7 :(得分:0)

由于属性字符串通常很麻烦,我发现最好避免使用范围API,并尽可能使内容保持不变。在创建属性字符串时设置属性,而不要返回并尝试设置范围。这也将有助于本地化,因为弄清楚不同语言的范围非常棘手(下面的示例并未显示本地化以保持说明性)。它使事情更清洁,更容易遵循。构造完所有琴弦后,将所有东西组装成碎片。

// build string
let intro = NSAttributedString(string: "I agree that I have read and understood the ")
let terms = NSAttributedString(string: "Terms and Conditions ", attributes: [.link: "https://apple.com" as Any])
let middle = NSAttributedString(string: "and ")
let privacy = NSAttributedString(string: "Privacy Policy. ", attributes: [.link: "https://example.com" as Any])
let ending = NSAttributedString(string: "This application may send me SMS messages.")
let attrStr = NSMutableAttributedString()
attrStr.append(intro)
attrStr.append(terms)
attrStr.append(middle)
attrStr.append(privacy)
attrStr.append(ending)

// set the link color
let linkAttributes: [NSAttributedString.Key: AnyObject] = [.foregroundColor: UIColor(named: "Secondary")!]
textView.linkTextAttributes = linkAttributes
textView.attributedText = attrStr