UITextView linkTextAttributes font attribute not applied to NSAttributedString

后端 未结 8 640
暖寄归人
暖寄归人 2021-02-01 20:18

I have an NSAttributedString generated from HTML which includes some links. The attributed string is shown in a UITextView. I wish to apply a different font style f

相关标签:
8条回答
  • 2021-02-01 20:58

    There's also an easy way to apply style for the text if you use html - you can just add the style within the html code. Then you wouldn't need to worry about setting attributes for the text. For example:

    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];
    
    0 讨论(0)
  • 2021-02-01 21:01

    for simple cases: (without horrible HTML use):

        let linkTextAttributes : [String : Any] = [
            NSForegroundColorAttributeName: UIColor.red,
            NSUnderlineColorAttributeName: UIColor.magenta,
            NSUnderlineStyleAttributeName: NSUnderlineStyle.patternSolid.rawValue
        ]
    
        self.infoText.linkTextAttributes = linkTextAttributes
    
    0 讨论(0)
  • 2021-02-01 21:02

    Not sure why linkTextAttributes doesn't work for the font name. But we can achieve this by updating the link attributes of the NSAttributedString. Check the code below.

            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 {
        }
    

    This is the objective-C code for this:

    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;
    

    0 讨论(0)
  • 2021-02-01 21:04

    Swift 5 version of Ryan Heitner's awesome answer:

    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
    
    0 讨论(0)
  • 2021-02-01 21:05

    Since attributed strings are generally a pain, I find it is better to avoid the range APIs, and to keep things as immutable as possible. Set the attributes when you create the attributed string rather than going back and trying to set a range. This will also help with localization because figuring out ranges for different languages is quite tricky (the sample below does not show localization to keep things illustrative). It makes things cleaner and easier to follow. When all strings are constructed, assemble the whole thing from the pieces.

    // 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
    
    0 讨论(0)
  • 2021-02-01 21:13

    This is a swift 3 update of answer above from @Arun Ammannaya

    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]
    

    This is a Swift 3 solution to @CTiPKA which I prefer since it avoids 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
    
    0 讨论(0)
提交回复
热议问题