将HTML解析为NSAttributedText-如何设置字体?


132

我正在尝试获取以html格式格式化的文本片段,以在UITableViewCell中的iPhone上很好地显示。

到目前为止,我有这个:

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
                                                              documentAttributes:nil error:&error];

这种作品。我得到一些加粗的“ Nice”文字!但是...它还将字体设置为Times Roman!这不是我想要的字体。我想我需要在documentAttributes中设置一些内容,但是,我在任何地方都找不到任何示例。


1
注意:NSHTMLTextDocumentType可能很慢。参见stackoverflow.com/questions/21166752/…–
finneycanhelp

重要说明:如果您使用的是自定义字体,则需要查看此答案stackoverflow.com/a/60786178/1223897
Yuvrajsinh

Answers:


117

Swift 2版本,基于Javier Querol给出的答案

extension UILabel {
    func setHTMLFromString(text: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String

        let attrStr = try! NSAttributedString(
            data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

Swift 3.0和iOS 9+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

Swift 5和iOS 11+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

1
不更改当前字体,谢谢,这是我一直在寻找的字体!
Mohammad Zaid Pathan 2015年

2
这可行。您可以立即将修改后的字符串设置为String,并省略NSString初始化,即“ <span style = \” font-family:(self.font!.fontName); font-size:(self.font!.pointSize)\“>(text)</ span>”
Matthew Korporaal

2
为了使这项工作(确实非常有效),我必须在font-family值周围添加单引号,因此<div style = \“ font-family:'(self.font!.fontName)';...。
geraldcor '16

4
我认为,自iOS9以来,最好使用font-family: '-apple-system', 'HelveticaNeue';(既可以工作,又可以向后兼容)。如果您仅支持iOS9,font-family: -apple-system;则可以使用
丹尼尔(Daniel)

1
设置文本颜色的功能也很方便,只需将颜色添加到具有十六进制字符串格式的值的样式属性中即可color: #000000。请参见以下将UIColor转换为十六进制字符串的链接:gist.github.com/yannickl/16f0ed38f0698d9a8ae7
Miroslav Hrivik

114
#import "UILabel+HTML.h"

@implementation UILabel (HTML)

- (void)jaq_setHTMLFromString:(NSString *)string {

    string = [string stringByAppendingString:[NSString stringWithFormat:@"<style>body{font-family: '%@'; font-size:%fpx;}</style>",
                                              self.font.fontName,
                                              self.font.pointSize]];
    self.attributedText = [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                                                documentAttributes:nil
                                                             error:nil];
}


@end

这样,您无需指定所需的字体,它将带有标签的字体和大小。


2
这非常优雅!
Merlevede '16

2
真好 尽管将其作为NSAttributedString imo上的类别更有意义。
Dimitris

@Javier Querol然后如何处理链接?
卡伦·安妮(KarenAnne)'16

您可以使用编码字符串为数据,NSUnicodeStringEncoding然后使用编码数据为字符NSUTF8StringEncoding。可以吗
Timur Bernikovich

1
抱歉,该解决方案不适用于我,字体未设置为所需的字体。-不是使用self.font.fontName而是使用self.font.familyName设置了所需的字体,但是HTML标记未保留。请参阅下面的解决方案,该解决方案有效且不依赖于使用任何类型的HTML样式。-rrh
Richie Hyatt

49

我实际上找到了解决此问题的可行方法:

在解析HTML响应字符串之前更改字体。

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">%@</span>", htmlResponse];

例:

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: HelveticaNeue-Thin; font-size: 17\">%@</span>", [response objectForKey:@"content"]];

迅捷版:

let aux = "<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">\(htmlResponse)</span>"

4
最简单的解决方案..其他答案可能正确,但以困难的方式做事并不聪明.. :)
Sameera Chathuranga 2015年

2
最聪明的答案
塔里克

最聪明的答案,表示同意!干杯
Jim Tierney

您好,实际上,这很好,但是如果我将属性文本转换回html,则该html中的字体大小会增加
Mehul Thakkar

1
实际上是从stackoverflow上其他帖子的帮助下获得的。.我能够将原始文本转换为html,并且除字体大小外,其他所有东西都可以正常工作,几乎翻了一倍
Mehul Thakkar

41

弄清楚了。有点负担,也许不是最佳答案。

此代码将经历所有字体更改。我知道该字体使用的是“ Times New Roman”和“ Times New Roman BoldMT”。但是无论如何,这都会找到粗体并让我重置它们。我也可以在设置时重置大小。

老实说,我希望/认为有一种方法可以在解析时进行设置,但如果有的话,我找不到它。

    NSRange range = (NSRange){0,[str length]};
    [str enumerateAttribute:NSFontAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
        UIFont* currentFont = value;
        UIFont *replacementFont = nil;

        if ([currentFont.fontName rangeOfString:@"bold" options:NSCaseInsensitiveSearch].location != NSNotFound) {
            replacementFont = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:25.0f];
        } else {
            replacementFont = [UIFont fontWithName:@"HelveticaNeue-Thin" size:25.0f];
        }

        [str addAttribute:NSFontAttributeName value:replacementFont range:range];
    }];

2
在字体名称中寻找单词“ BOLD”真是太可怕了!这也会破坏其他字体属性,例如斜体。
HughHughTeotl

1
一种更通用的方法是在枚举时查看字体特征,并创建具有相同特征的字体。我将在下面发布我的代码。
markiv 2015年

33

一种更通用的方法是在枚举时查看字体特征,并创建具有相同特征(粗体,斜体等)的字体:

extension NSMutableAttributedString {

    /// Replaces the base font (typically Times) with the given font, while preserving traits like bold and italic
    func setBaseFont(baseFont: UIFont, preserveFontSizes: Bool = false) {
        let baseDescriptor = baseFont.fontDescriptor
        let wholeRange = NSRange(location: 0, length: length)
        beginEditing()
        enumerateAttribute(.font, in: wholeRange, options: []) { object, range, _ in
            guard let font = object as? UIFont else { return }
            // Instantiate a font with our base font's family, but with the current range's traits
            let traits = font.fontDescriptor.symbolicTraits
            guard let descriptor = baseDescriptor.withSymbolicTraits(traits) else { return }
            let newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
            let newFont = UIFont(descriptor: descriptor, size: newSize)
            self.removeAttribute(.font, range: range)
            self.addAttribute(.font, value: newFont, range: range)
        }
        endEditing()
    }
}

即使这不是很简洁,但似乎比用更多的html包装html来解决问题更稳定。
syvex

23

是的,有一个更简单的解决方案。在html源代码中设置字体!

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
source = [source stringByAppendingString:@"<style>strong{font-family: 'Avenir-Roman';font-size: 14px;}</style>"];
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
                                                              documentAttributes:nil error:&error];

希望这可以帮助。


23

Swift 4+更新UILabel扩展

extension UILabel {
    func setHTMLFromString(text: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>" as NSString, text)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!,
            options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

iOS 9+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>" as NSString, htmlText) as String


        //process collection values
        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
            documentAttributes: nil)


        self.attributedText = attrStr
    }
}

8

如果您在创建转换的同时进行转换,那么上述所有答案都可以NSAttributedString。但是,我认为以下类别是一种更好的解决方案,它可以对字符串本身起作用,因此不需要访问输入:

extension NSMutableAttributedString
{
    func convertFontTo(font: UIFont)
    {
        var range = NSMakeRange(0, 0)

        while (NSMaxRange(range) < length)
        {
            let attributes = attributesAtIndex(NSMaxRange(range), effectiveRange: &range)
            if let oldFont = attributes[NSFontAttributeName]
            {
                let newFont = UIFont(descriptor: font.fontDescriptor().fontDescriptorWithSymbolicTraits(oldFont.fontDescriptor().symbolicTraits), size: font.pointSize)
                addAttribute(NSFontAttributeName, value: newFont, range: range)
            }
        }
    }
}

用于:

let desc = NSMutableAttributedString(attributedString: *someNSAttributedString*)
desc.convertFontTo(UIFont.systemFontOfSize(16))

适用于iOS 7+


在每个地方搜索...!谢谢..!
Irshad Qureshi

5

改进Victor的解决方案,包括颜色:

extension UILabel {
      func setHTMLFromString(text: String) {
          let modifiedFont = NSString(format:"<span style=\"color:\(self.textColor.toHexString());font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String

          let attrStr = try! NSAttributedString(
              data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
              options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
              documentAttributes: nil)

          self.attributedText = attrStr
      }
  }

为此,您还需要将uicolor的YLColor.swift转换为十六进制https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7


4

使用NSHTMLTextDocumentType速度慢且难以控制样式。我建议您尝试一下我的名为Atributika的库。它有自己的非常快的解析器。您也可以使用任何标签名称,并为其定义任何样式。

例:

let str = "<strong>Nice</strong> try, Phil".style(tags:
    Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString

label.attributedText = str

您可以在这里找到它https://github.com/psharanda/Atributika


4

将每个人的答案结合在一起,我进行了两个扩展,可以使用html文本设置标签。上面的某些答案不能正确解释属性字符串中的字体系列。其他人不能满足我的需求,或者以其他方式失败。让我知道您是否需要我改进。

我希望这可以帮助别人。

extension UILabel {
    /// Sets the label using the supplied html, using the label's font and font size as a basis.
    /// For predictable results, using only simple html without style sheets.
    /// See /programming/19921972/parsing-html-into-nsattributedtext-how-to-set-font
    ///
    /// - Returns: Whether the text could be converted.
    @discardableResult func setAttributedText(fromHtml html: String) -> Bool {
        guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
            print(">>> Could not create UTF8 formatted data from \(html)")
            return false
        }

        do {
            let mutableText = try NSMutableAttributedString(
                data: data,
                options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
                documentAttributes: nil)
            mutableText.replaceFonts(with: font)
            self.attributedText = mutableText
            return true
        } catch (let error) {
            print(">>> Could not create attributed text from \(html)\nError: \(error)")
            return false
        }
    }
}

extension NSMutableAttributedString {

    /// Replace any font with the specified font (including its pointSize) while still keeping
    /// all other attributes like bold, italics, spacing, etc.
    /// See /programming/19921972/parsing-html-into-nsattributedtext-how-to-set-font
    func replaceFonts(with font: UIFont) {
        let baseFontDescriptor = font.fontDescriptor
        var changes = [NSRange: UIFont]()
        enumerateAttribute(.font, in: NSMakeRange(0, length), options: []) { foundFont, range, _ in
            if let htmlTraits = (foundFont as? UIFont)?.fontDescriptor.symbolicTraits,
                let adjustedDescriptor = baseFontDescriptor.withSymbolicTraits(htmlTraits) {
                let newFont = UIFont(descriptor: adjustedDescriptor, size: font.pointSize)
                changes[range] = newFont
            }
        }
        changes.forEach { range, newFont in
            removeAttribute(.font, range: range)
            addAttribute(.font, value: newFont, range: range)
        }
    }
}

唯一适用于UILabel和的完整解决方案UITextView。谢谢!
Radu Ursache

3

感谢您的回答,我真的很喜欢扩展程序,但是我还没有转换为Swift。对于那些仍在Objective-C中的老学生,这应该有所帮助:D

-(void) setBaseFont:(UIFont*)font preserveSize:(BOOL) bPreserve {

UIFontDescriptor *baseDescriptor = font.fontDescriptor;

[self enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {

    UIFont *font = (UIFont*)value;
    UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
    UIFontDescriptor *descriptor = [baseDescriptor fontDescriptorWithSymbolicTraits:traits];
    UIFont *newFont = [UIFont fontWithDescriptor:descriptor size:bPreserve?baseDescriptor.pointSize:descriptor.pointSize];

    [self removeAttribute:NSFontAttributeName range:range];
    [self addAttribute:NSFontAttributeName value:newFont range:range];

}];    } 

编码愉快!-格雷格框架


1
感谢老同学们的上帝!:-)
Josef Rysanek '17

1

Swift 3字符串扩展,包括nil字体。没有字体的属性取自其他SO问题,不记得是哪个:(

extension String {
    var html2AttributedString: NSAttributedString? {
        guard let data = data(using: .utf8) else {
            return nil
        }

        do {
            return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
        }
        catch {
            print(error.localizedDescription)
            return nil
        }
    }

    public func getHtml2AttributedString(font: UIFont?) -> NSAttributedString? {
        guard let font = font else {
            return html2AttributedString
        }

        let modifiedString = "<style>body{font-family: '\(font.fontName)'; font-size:\(font.pointSize)px;}</style>\(self)";

        guard let data = modifiedString.data(using: .utf8) else {
            return nil
        }

        do {
            return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
        }
        catch {
            print(error)
            return nil
        }
    }
}

0

这是NSString的扩展,该扩展使用Objective-C返回NSAttributedString。

它可以正确处理带有HTML标签的字符串,并设置所需的字体和字体颜色,同时保留HTML标签,包括BOLD,ITALICS ...

最重要的是,它不依赖任何HTML标记来设置字体属性。

@implementation NSString (AUIViewFactory)

- (NSAttributedString*)attributedStringFromHtmlUsingFont:(UIFont*)font fontColor:(UIColor*)fontColor
{
    NSMutableAttributedString* mutableAttributedString = [[[NSAttributedString alloc] initWithData:[self dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding)} documentAttributes:nil error:nil] mutableCopy]; // parse text with html tags into a mutable attributed string
    [mutableAttributedString beginEditing];
    // html tags cause font ranges to be created, for example "This text is <b>bold</b> now." creates three font ranges: "This text is " , "bold" , " now."
    [mutableAttributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableAttributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL* stop)
    { // iterate every font range, change every font to new font but preserve symbolic traits such as bold and italic (underline and strikethorugh are preserved automatically), set font color
        if (value)
        {
            UIFont* oldFont = (UIFont*)value;
            UIFontDescriptor* fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
            UIFont* newFont = [UIFont fontWithDescriptor:fontDescriptor size:font.pointSize];
            [mutableAttributedString removeAttribute:NSFontAttributeName range:range]; // remove the old font attribute from this range
            [mutableAttributedString addAttribute:NSFontAttributeName value:newFont range:range]; // add the new font attribute to this range
            [mutableAttributedString addAttribute:NSForegroundColorAttributeName value:fontColor range:range]; // set the font color for this range
        }
    }];
    [mutableAttributedString endEditing];
    return mutableAttributedString;
}

@end

-3

实际上,存在一种更简单,更干净的方法。只需在解析HTML之后设置字体即可:

 NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
                                                                     options:@{
                                                                               NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                               NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                                                          documentAttributes:nil error:nil];
    [text addAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Lato-Regular" size:20]} range:NSMakeRange(0, text.length)];

14
可以,但是您会丢失粗体和斜体<b>和<u>,因为这些字体会被字体覆盖。
Zystem先生2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.