iOS에서 HTML을 NSAttributedString으로 변환


151

UIWebView일부 텍스트를 처리하고 올바르게 색상을 지정 하는 인스턴스를 사용 하고 있습니다. 결과는 HTML로 표시되지만에 표시하지 않고 HTML UIWebView로 표시하고 싶습니다 Core Text.NSAttributedString .

나는 만들 수 있고 NSAttributedString 있지만 HTML을 중요한 문자열로 변환하고 매핑하는 방법을 잘 모르겠습니다.

Mac OS X NSAttributedString에서는initWithHTML: 방법이 있지만 Mac 전용이며 iOS에서는 사용할 수 없다는 있습니다.

나는 이것과 비슷한 질문이 있다는 것을 알고 있지만 대답이 없었습니다. 나는 다시 시도하고 누군가가 이것을 할 수있는 방법을 만들 었는지, 만약 그렇다면 공유 할 수 있는지 확인했습니다.


2
NSAttributedString-Add-for-HTML 라이브러리는 동일한 작성자에 의해 이름이 바뀌고 프레임 워크로 롤백되었습니다. 이제 DTCoreText라고하며 많은 핵심 텍스트 레이아웃 클래스가 포함되어 있습니다. 여기에서
Brian Douglas Moakley

답변:


290

iOS 7에서 UIKit은 HTML을 사용하여 initWithData:options:documentAttributes:error:초기화 할 수 있는 방법을 추가했습니다 NSAttributedString.

[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] 
                                 options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                           NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} 
                      documentAttributes:nil error:nil];

스위프트에서 :

let htmlData = NSString(string: details).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType:
        NSAttributedString.DocumentType.html]
let attributedString = try? NSMutableAttributedString(data: htmlData ?? Data(),
                                                          options: options,
                                                          documentAttributes: nil)

28
어떤 이유로, NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType 옵션을 사용하면 인코딩 시간이 정말 오래 걸립니다. (
Arie Litovsky

14
NSHTMLTextDocumentType이 너무 나쁜 경우 NSRange를 사용하여 속성을 설정하는 것보다 문자 그대로 ~ 1000x 느립니다. (굵은 태그가 하나있는 짧은 레이블이 있습니다.)
Jason Moore

6
백그라운드 스레드에서 NSHTMLTextDocumentType을 사용할 수없는 경우이 메소드로 NSHTMLTextDocumentType을 사용할 수없는 경우에 유의하십시오. iOS 7에서도 HTML 렌더링에 TextKit을 사용하지 않습니다. Ingve가 권장하는 DTCoreText 라이브러리를 살펴보십시오.
TJez

2
대박. 그냥 생각한다면, 아마도 [NSNumber numberWithInt : NSUTF8StringEncoding]을 @ (NSUTF8StringEncoding)로 할 수 있습니까?
Jarsen

15
나는 이것을하고 있었지만 iOS 8에서는 조심해야한다. 고통스럽게 느리다. 몇 백 자 동안 1 초에 가깝다. (iOS 7에서는 거의 즉각적이었습니다.)
Norman

43

Github의 Oliver Drobnik 가 NSAttributedString 에 진행중인 오픈 소스 추가 작업이 있습니다 . HTML 구문 분석을 위해 NSScanner를 사용합니다.


iOS 4.3의 최소 배포 필요 :( 아직도, 매우 인상적입니다.
Oh Danny Boy

3
@Lirik Overkill은 다른 사람에게는 적합하지만 다른 사람에게는 완벽합니다. 즉, 귀하의 의견은 도움이되지 않습니다.
wuf810

3
이 프로젝트는 오픈 소스이며 표준 2 절 BSD 라이센스가 적용됩니다. 즉, Cocoanetics를이 코드의 원래 작성자로 언급하고 앱 내에서 LICENSE 텍스트를 재현해야합니다.
dulgan

28

HTML에서 NSAttributedString을 생성하는 것은 메인 스레드에서 수행되어야합니다!

업데이트 : NSAttributedString HTML 렌더링은 후드 아래의 WebKit에 의존 하며 기본 스레드에서 실행해야 하거나 때로는 SIGTRAP으로 앱이 충돌합니다 .

새로운 유물 충돌 로그 :

여기에 이미지 설명을 입력하십시오

아래는 업데이트 된 스레드 안전 Swift 2 문자열 확장입니다.

extension String {
    func attributedStringFromHTML(completionBlock:NSAttributedString? ->()) {
        guard let data = dataUsingEncoding(NSUTF8StringEncoding) else {
            print("Unable to decode data from html string: \(self)")
            return completionBlock(nil)
        }

        let options = [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType,
                   NSCharacterEncodingDocumentAttribute: NSNumber(unsignedInteger:NSUTF8StringEncoding)]

        dispatch_async(dispatch_get_main_queue()) {
            if let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) {
                completionBlock(attributedString)
            } else {
                print("Unable to create attributed string from html string: \(self)")
                completionBlock(nil)
            }
        }
    }
}

용법:

let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
    self.bodyLabel.attributedText = attString
}

산출:

여기에 이미지 설명을 입력하십시오


앤드류 이것은 잘 작동합니다. 이 접근법을 사용한다면 UITextView에서 처리해야 할 모든 짧은 이벤트를 알고 싶었습니다. HTML로 제공되는 캘린더 일정, 전화, 이메일, 웹 사이트 링크 등을 처리 할 수 ​​있습니까? UITextView가 UILabel과 비교하여 이벤트를 처리 할 수 ​​있기를 바랍니다.
harshit2811

위의 방법은 서식에만 적합합니다. 이벤트 처리가 필요한 경우 TTTAttributedLabel을 사용하는 것이 좋습니다 .
Andrew Schreiber

NSAttributedString이 사용하는 기본 인코딩은 NSUTF16StringEncoding (UTF8 아님)입니다. 이것이 작동하지 않는 이유입니다. 적어도 내 경우에는!
Umit Kaya

이것이 허용되는 솔루션이어야합니다. 백그라운드 스레드에서 HTML 문자열 대화를 수행하면 됩니다 테스트를 실행하는 동안 결국 충돌, 꽤 자주.
ratsimihah

21

NSAttributedString의 신속한 이니셜 라이저 확장

내 성향은 이것을에 확장으로 추가하는 NSAttributedString것이 었 습니다 String. 정적 확장 및 초기화 프로그램으로 시도했습니다. 아래에 포함 된 이니셜 라이저를 선호합니다.

스위프트 4

internal convenience init?(html: String) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }

    guard let attributedString = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
        return nil
    }

    self.init(attributedString: attributedString)
}

스위프트 3

extension NSAttributedString {

internal convenience init?(html: String) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }

    guard let attributedString = try? NSMutableAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) else {
        return nil
    }

    self.init(attributedString: attributedString)
}
}

let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)

안녕하세요 세계가 이처럼되기를 원합니다 <p> <b> <i> hello </ i> </ b> <i> world </ i> </ p>
Uma Madhavi 2016

일부 LOC 저장하는 대신 guard ... NSMutableAttributedString(data:...에 의해 try self.init(data:...(및 추가 throws초기화에)
NYG

그리고 마침내 작동하지 않습니다-텍스트는 임의의 글꼴 크기를 얻습니다
Vyachaslav Gerchicov

2
UTF-8로 데이터를 디코딩하고 있지만 UTF-16으로 인코딩했습니다.
Shyam Bhat

11

이것은 StringHTML 문자열을로 반환하기 위해 Swift로 작성된 확장입니다 NSAttributedString.

extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.dataUsingEncoding(NSUTF16StringEncoding, allowLossyConversion: false) else { return nil }
        guard let html = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) else { return nil }
        return html
    }
}

쓰다,

label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString()

위에서 유니 코드를 올바르게 렌더링한다는 것을 보여주기 위해 의도적으로 유니 코드 \ u2022를 추가했습니다.

사소한 : 사용하는 기본 인코딩 NSAttributedStringNSUTF16StringEncoding(UTF8 아님!)입니다.


UTF16은 하루를 절약했습니다. 감사합니다.
Yueyu

UTF16은 하루를 절약했습니다. 감사합니다.
Yueyu

6

Andrew를 약간 수정했습니다. 의 솔루션을 하고 코드를 Swift 3으로 업데이트하십시오.

이 코드는 이제 UITextView를 사용 self하며 원래 글꼴, 글꼴 크기 및 텍스트 색상을 상속 할 수 있습니다.

참고 : 여기toHexString() 에서 확장

extension UITextView {
    func setAttributedStringFromHTML(_ htmlCode: String, completionBlock: @escaping (NSAttributedString?) ->()) {
        let inputText = "\(htmlCode)<style>body { font-family: '\((self.font?.fontName)!)'; font-size:\((self.font?.pointSize)!)px; color: \((self.textColor)!.toHexString()); }</style>"

        guard let data = inputText.data(using: String.Encoding.utf16) else {
            print("Unable to decode data from html string: \(self)")
            return completionBlock(nil)
        }

        DispatchQueue.main.async {
            if let attributedString = try? NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) {
                self.attributedText = attributedString
                completionBlock(attributedString)
            } else {
                print("Unable to create attributed string from html string: \(self)")
                completionBlock(nil)
            }
        }
    }
}

사용법 예 :

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }

5

스위프트 3.0 Xcode 8 버전

func htmlAttributedString() -> NSAttributedString? {
    guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
    guard let html = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) else { return nil }
    return html
}

5

스위프트 4


  • NSAttributedString 편의 이니셜 라이저
  • 여분의 경비원없이
  • 오류를 던진다

extension NSAttributedString {

    convenience init(htmlString html: String) throws {
        try self.init(data: Data(html.utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ], documentAttributes: nil)
    }

}

용법

UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")

당신은 내 하루를 저장합니다. 감사합니다.
pkc456

@ pkc456 meta.stackexchange.com/questions/5234/... , upvote에 할 :) 감사합니다!
AamirR

글꼴 크기와 글꼴 모음을 어떻게 설정합니까?
kirqe

self.init (attributedString : trustedString)
cyanide

4

당신이 지금 가지고있는 유일한 해결책은 HTML을 구문 분석하고 주어진 point / font / etc 속성으로 일부 노드를 작성한 다음 NSAttributedString으로 결합하는 것입니다. 많은 작업이지만 올바르게 수행하면 나중에 다시 사용할 수 있습니다.


1
HTML이 XHTML-Strict 인 경우 NSXMLDOcument 및 friends를 사용하여 구문 분석을 도울 수 있습니다.
딜런 루크 스

주어진 속성으로 노드를 구축하는 방법에 대해 어떻게 제안 하시겠습니까?
Joshua

2
그것은 구현 세부 사항입니다. 그러나 HTML을 구문 분석하면 글꼴 이름, 크기 등과 같은 항목을 지정하는 각 태그의 각 속성에 액세스 할 수 있습니다.이 정보를 사용하여 속성이있는 텍스트에 속성으로 추가해야하는 관련 세부 사항을 저장할 수 있습니다. . 일반적으로 이러한 작업을 수행하기 전에 먼저 구문 분석에 익숙해 져야합니다.
jer

2

위의 해결책이 맞습니다.

[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] 
                                 options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                           NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} 
                      documentAttributes:nil error:nil];

그러나 iOS 8.1, 2 또는 3에서 실행하면 앱이 충돌합니다.

충돌을 피하려면 다음을 수행하십시오. 큐에서 실행하십시오. 항상 메인 스레드에 있도록하십시오.


@alecex 나는 같은 문제를 만났다! iOS 8.1, 2, 3에서는 앱이 다운되지만 iOS 8.4 이상에서는 문제가 없습니다. 피하는 방법을 자세히 설명해 주시겠습니까? 또는 해결 방법이 있습니까, 아니면 방법을 대신 사용할 수 있습니까?
강력한

AppKit의 메소드를 복사하는 빠른 카테고리를 작성하여 매우 쉽고 직관적 인 방법을 사용했습니다. Apple이 추가하지 않은 이유는 다음과 같습니다
CGuess

2

NSHTMLTextDocumentType의 사용이 느리고 스타일을 제어하기가 어렵습니다. Atributika라는 내 라이브러리를 사용해 보는 것이 좋습니다. 그것은 매우 빠른 HTML 파서를 가지고 있습니다. 또한 태그 이름을 지정하고 스타일을 정의 할 수 있습니다.

예:

let str = "<strong>Hello</strong> World!".style(tags:
    Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString

label.attributedText = str

https://github.com/psharanda/Atributika에서 찾을 수 있습니다


2

스위프트 3 :
이것을보십시오 :

extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
        guard let html = try? NSMutableAttributedString(
            data: data,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil) else { return nil }
        return html
    }
}  

그리고 사용하기 위해 :

let str = "<h1>Hello bro</h1><h2>Come On</h2><h3>Go sis</h3><ul><li>ME 1</li><li>ME 2</li></ul> <p>It is me bro , remember please</p>"

self.contentLabel.attributedText = str.htmlAttributedString()

0

유용한 확장

이 스레드, 포드, 및 iOS 음식 요리 책의 P.80의 에리카 Sadun의 ObjC 예에서 영감을, 나는에 확장을 작성 String하고에 NSAttributedStringGitHub의에 - 반대 앞뒤로 HTML 일반 문자열과 NSAttributedStrings 그리고 그 사이의 이동 여기에 , 어떤 도움이되었습니다.

서명 (요지에 다시, 전체 코드, 위의 링크)입니다 :

extension NSAttributedString {
    func encodedString(ext: DocEXT) -> String?
    static func fromEncodedString(_ eString: String, ext: DocEXT) -> NSAttributedString? 
    static func fromHTML(_ html: String) -> NSAttributedString? // same as above, where ext = .html
}

extension String {
    func attributedString(ext: DocEXT) -> NSAttributedString?
}

enum DocEXT: String { case rtfd, rtf, htm, html, txt }

0

글꼴로

extension NSAttributedString
{
internal convenience init?(html: String, font: UIFont? = nil) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }
    assert(Thread.isMainThread)
    guard let attributedString = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
        return nil
    }
    let mutable = NSMutableAttributedString(attributedString: attributedString)
    if let font = font {
        mutable.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutable.length))
    }
    self.init(attributedString: mutable)
}
}

또는이 속성에서 파생 된 버전을 사용하고 attributedString을 설정 한 후 UILabel에서 글꼴을 설정할 수 있습니다


0

내장 변환은 .forgroundColor를 다른 것으로 설정 한 속성 사전을 전달하더라도 텍스트 색상을 항상 UIColor.black으로 설정합니다. iOS 13에서 DARK 모드를 지원하려면 NSAttributedString에서이 확장 버전을 사용해보십시오.

extension NSAttributedString {
    internal convenience init?(html: String)                    {
        guard 
            let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }

        let options : [DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        guard
            let string = try? NSMutableAttributedString(data: data, options: options,
                                                 documentAttributes: nil) else { return nil }

        if #available(iOS 13, *) {
            let colour = [NSAttributedString.Key.foregroundColor: UIColor.label]
            string.addAttributes(colour, range: NSRange(location: 0, length: string.length))
        }

        self.init(attributedString: string)
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.