【问题标题】:Parsing HTML into NSAttributedText - how to set font?将 HTML 解析为 NSAttributedText - 如何设置字体?
【发布时间】:2013-11-24 04:14:25
【问题描述】:

我正在尝试获取 html 格式的文本 sn-p,以便在 iPhone 上的 UITableViewCell 中很好地显示。

到目前为止,我有这个:

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 中设置一些东西,但是,我在任何地方都找不到任何示例。

【问题讨论】:

标签: html ios nsattributedstring


【解决方案1】:

想通了。有点熊,也许不是最好的答案。

此代码将完成所有字体更改。我知道它使用“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];
    }];

【讨论】:

  • 在字体名称中寻找“BOLD”这个词是一件可怕的事情!这也会破坏其他字体属性,例如斜体。
  • 一种更通用的方法是在枚举时查看字体特征,并创建具有相同特征的字体。我将在下面发布我的代码。
【解决方案2】:

是的,有一个更简单的解决方案。在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];

希望这会有所帮助。

【讨论】:

    【解决方案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)];
    

    【讨论】:

    • 这行得通,但是你会失去粗体和斜体 因为它们被字体覆盖了。
    【解决方案4】:

    我实际上找到了解决这个问题的有效方法:

    在解析之前更改 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"]];
    

    Swift 版本:

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

    【讨论】:

    • 最简单的解决方案 .. 其他答案可能正确,但以更难的方式做事并不聪明 .. :)
    • 最佳智能答案
    • 最聪明的答案,同意!干杯
    • 你好,实际上这很好用,但是如果我将此属性文本转换回 html,则该 html 中的字体大小会增加
    • 实际上是在stackoverflow上其他帖子的帮助下..我能够将属性文本转换为html,除了字体大小之外一切正常,几乎翻了一番
    【解决方案5】:
    #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
    

    这样你不需要指定你想要的字体,它会带标签字体和大小。

    【讨论】:

    • 这很优雅!
    • 不错。虽然它作为 NSAttributedString imo 上的一个类别更有意义。
    • @Javier Querol 那么如何处理链接碰撞?
    • 您使用NSUnicodeStringEncoding 将字符串编码为数据,然后使用NSUTF8StringEncoding 将数据编码回字符。好吗?
    • 抱歉 - 此解决方案对我不起作用,字体未设置为所需的字体。 - 而不是使用 self.font.fontName 而是使用 self.font.familyName 确实设置了所需的字体,但不保留 HTML 标签。请参阅下面的解决方案,该解决方案确实有效,并且不依赖于使用任何类型的 HTML 样式。 -rrh
    【解决方案6】:

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

    【讨论】:

    • 不改变当前字体,这就是我要找的,谢谢!
    • 这行得通。您可以立即将修改后的字符串设置为 String 并省略 NSString 初始化,即 "(文本)"
    • 为了完成这项工作,(效果非常好)我必须在 font-family 值周围添加单引号,所以
    • 我认为,从 iOS9 开始,最好使用font-family: '-apple-system', 'HelveticaNeue';(它可以工作,并且向后兼容)。如果只支持iOS9font-family: -apple-system;可以使用
    • 设置文本颜色也很方便,只需将颜色添加到样式属性,其值为十六进制字符串格式color: #000000。请参阅此链接将 UIColor 转换为十六进制字符串:gist.github.com/yannickl/16f0ed38f0698d9a8ae7
    【解决方案7】:

    如果您在创建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+

    【讨论】:

    • 到处搜索这个...!!谢谢..!
    【解决方案8】:

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

    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 的问题更稳定。
    【解决方案9】:

    改进 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

    【讨论】:

      【解决方案10】:

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

      【讨论】:

        【解决方案11】:

        使用 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

        【讨论】:

          【解决方案12】:

          感谢您的回答,我真的很喜欢这个扩展,但我还没有转换成 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];
          
          }];    } 
          

          编码愉快! --格雷格框架

          【讨论】:

          • 感谢老天爷! :-)
          【解决方案13】:

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

          【讨论】:

            【解决方案14】:

            结合大家的答案,我做了两个扩展,允许使用 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 https://stackoverflow.com/questions/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 https://stackoverflow.com/questions/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)
                    }
                }
            }
            

            【讨论】:

            • 唯一适用于UILabelUITextView 的完整解决方案。谢谢!
            【解决方案15】:

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

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

            最重要的是它不依赖任何 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
            

            【讨论】:

              【解决方案16】:

              Swift 5 UILabel 和 UITextView 的解决方案

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

              }

              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
              }
              

              }

              UILabel 的用法

              self.label.setHTMLFromString(htmlText: htmlString) 
              

              UITextView 的用法

              self.textView.setHTMLFromString(htmlText: htmlString) 
              

              输出

              【讨论】:

                【解决方案17】:
                • 快速解决方案

                以下方法有效。您可以通过这种方法很好地提供字体系列、字体大小和颜色。随意建议更改或任何更好的方法。

                extension UILabel {
                
                func setHTMLFromString(htmlText: String,fontFamily:String,fontColor:String) {
                    let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', \(fontFamily); font-size: \(self.font!.pointSize); color: \(fontColor) ; \">%@</span>", htmlText)
                    do{
                        if let valData = modifiedFont.data(using: .utf8){
                            let attrStr = try NSAttributedString(data: valData, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html.rawValue], documentAttributes: nil)
                            self.attributedText = attrStr
                        }
                    }
                    catch{
                        print("Conversion failed with \(error)")
                        self.attributedText = nil
                        }
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  相关资源
                  最近更新 更多
                  热门标签