NSAttributedText에 HTML 파싱-글꼴을 설정하는 방법?


133

UITableViewCell의 iPhone에 잘 표시되도록 html 형식으로 된 텍스트 조각을 얻으려고합니다.

지금까지 나는 이것을 가지고있다 :

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

답변:


118

Javier Querol 의 답변을 기반으로 한 Swift 2 버전

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

스위프트 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
    }
}

스위프트 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

2
작동합니다. 수정 된 문자열을 즉시 문자열로 설정하고 NSString 초기화를 생략 할 수 있습니다. 즉 "<span style = \"font-family : (self.font! .fontName); font-size : (self.font! .pointSize) \ "> (텍스트) </ span>"
Matthew Korporaal

2
이 작업을 수행하려면 (정말 잘 작동합니다) font-family 값 주위에 작은 따옴표를 추가해야하므로 <div style = \ "font-family : '(self.font! .fontName)'; ....
geraldcor

4
나는 iOS9부터 사용하는 것이 가장 좋습니다 font-family: '-apple-system', 'HelveticaNeue';(작동하며 이전 버전과 호환됩니다). 당신은 단지 iOS9가 지원하는 경우 font-family: -apple-system;사용할 수 있습니다
다니엘

1
또한 텍스트 색상을 설정하고 16 진 문자열 형식의 값으로 스타일 속성에 색상을 추가하는 기능도 편리합니다 color: #000000. 16 진수 문자열로 UIColor 변환이 링크를 참조하십시오 : gist.github.com/yannickl/16f0ed38f0698d9a8ae7
미로슬라프 Hrivik에게

115
#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

2
좋은. NSAttributedString imo의 카테고리로 더 의미가 있지만.
Dimitris

@Javier Querol 그렇다면 링크 링크를 처리하는 방법은 무엇입니까?
KarenAnne

을 사용하여 문자열을 데이터로 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

2
최상의 답변
Tariq

똑똑한 대답, 동의했다! 건배
Jim Tierney

안녕하세요, 실제로 이것은 훌륭하게 작동하지만이 중요한 텍스트를 html로 다시 변환하면 해당 HTML에서 글꼴 크기가 커집니다.
Mehul Thakkar

1
실제로 유래를 통해 다른 게시물의 도움로부터 .. 나는 HTML로 attriubuted 텍스트를 변환 할 수 있어요 모든 것을 거의 두 배로지고 그, 글꼴 크기에서 떨어져 잘 작동
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

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

UILabel 확장 의 스위프트 4+ 업데이트

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에서 16 진수로 변환하는 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)
        }
    }
}

작동 유일한 완벽한 솔루션 UILabelUITextView. 감사!
Radu Ursache

3

답변 주셔서 감사합니다, 나는 확장을 정말 좋아하지만 아직 신속으로 변환하지 않았습니다. Objective-C에 아직있는 고학년 학생들에게는 약간 도움이 될 것입니다.

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

1

nil 글꼴을 포함한 Swift 3 String 확장. 글꼴이없는 속성은 다른 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

다음은 Objective-C를 사용하여 NSAttributedString을 반환하는 NSString의 확장입니다.

그것은 BOLD, ITALICS를 포함한 HTML 태그를 유지하면서 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

-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>는 글꼴로 덮어 쓰기 때문에 잃게됩니다.
Mr. Zystem
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.