I have a UITextView
with attributed text and allowsEditingTextAttributes
set to true
.
I'm trying to convert the attributed string into a Data object, using the following code:
let text = self.textView.attributedText
let data = try text.data(from: NSMakeRange(0, text.length), documentAttributes: [:])
However, this is throwing the following error:
Error Domain=NSCocoaErrorDomain Code=66062 "(null)"
Any ideas what this error means or what could cause this? I'm on the latest Xcode and iOS. Thanks.
You need to specify what kind of document data you would like to convert your attributed string to:
.txt // Plain Text Document Type (Simple Text)
.html // HTML Text Document Type (Hypertext Markup Language)
.rtf // RTF Text Document Type (Rich text format document)
.rtfd // RTFD Text Document Type (Rich text format document with attachment)
update Xcode 10.2 • Swift 5 or later
let textView = UITextView()
textView.attributedText = .init(string: "abc",
attributes: [.font: UIFont(name: "Helvetica", size: 16)!])
if let attributedText = textView.attributedText {
do {
let htmlData = try attributedText.data(from: .init(location: 0, length: attributedText.length),
documentAttributes: [.documentType: NSAttributedString.DocumentType.html])
let htmlString = String(data: htmlData, encoding: .utf8) ?? ""
print(htmlString)
} catch {
print(error)
}
}
Expanding on that:
extension NSAttributedString {
convenience init(data: Data, documentType: DocumentType, encoding: String.Encoding = .utf8) throws {
try self.init(attributedString: .init(data: data, options: [.documentType: documentType, .characterEncoding: encoding.rawValue], documentAttributes: nil))
}
func data(_ documentType: DocumentType) -> Data {
// Discussion
// Raises an rangeException if any part of range lies beyond the end of the receiver’s characters.
// Therefore passing a valid range allow us to force unwrap the result
try! data(from: .init(location: 0, length: length),
documentAttributes: [.documentType: documentType])
}
var text: Data { data(.plain) }
var html: Data { data(.html) }
var rtf: Data { data(.rtf) }
var rtfd: Data { data(.rtfd) }
}
Usage:
let textView = UITextView()
textView.attributedText = .init(string: "abc", attributes: [.font: UIFont(name: "Helvetica", size: 16)!])
if let textData = textView.attributedText?.text {
let text = String(data: textData, encoding: .utf8) ?? ""
print(text) // abc
}
if let htmlData = textView.attributedText?.html {
let html = String(data: htmlData, encoding: .utf8) ?? ""
print(html) // /* <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" ...
}
This will print
abc
/* <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="Content-Style-Type" content="text/css">
<title></title>
<meta name="Generator" content="Cocoa HTML Writer">
<style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 16.0px Helvetica}
span.s1 {font-family: 'Helvetica'; font-weight: normal; font-style: normal; font-size: 16.00pt}
</style>
</head>
<body>
<p class="p1"><span class="s1">abc</span></p>
</body>
</html>
*/
Please check it once this work for me. You can update font style and family also by using below functions.
var htmlStr = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n<meta http-equiv=\"Content-Style-Type\" content=\"text/css\">\n<title></title>\n<meta name=\"Generator\" content=\"Cocoa HTML Writer\">\n<style type=\"text/css\">\np.p1 {margin: 0.0px 0.0px 0.0px 0.0px; line-height: 22.0px; font: 17.0px \'Times New Roman\'; color: #000000; -webkit-text-stroke: #000000}\nspan.s1 {font-family: \'TimesNewRomanPS-BoldMT\'; font-weight: bold; font-style: normal; font-size: 17.00px; font-kerning: none}\nspan.s2 {font-family: \'Times New Roman\'; font-weight: normal; font-style: normal; font-size: 17.00px; text-decoration: underline ; font-kerning: none}\nspan.s3 {font-family: \'Times New Roman\'; font-weight: normal; font-style: normal; font-size: 17.00px; font-kerning: none}\n</style>\n</head>\n<body>\n<p class=\"p1\"><span class=\"s1\">This </span><span class=\"s2\">is</span><span class=\"s1\"> pikes</span><span class=\"s3\"> AsD this </span><span class=\"s1\">is</span><span class=\"s3\"> finding </span><span class=\"s2\">error</span></p>\n</body>\n</html>\n"
htmlStr = htmlStr.replacingOccurrences(of: "\n", with: "")
htmlStr = htmlStr.replacingOccurrences(of: "\\", with: "")
//Send htmlStr to server and when you will get back it from
// then convert html string to attributed string by below line
let str = Self.htmlToAttributedString(html: htmlStr,fontSize :17, fontName:"Times New Roman")
self.tfEmail.attributedText = str
//Function for attributed string from html string
func htmlToAttributedString(html:String,fontSize:CGFloat = 15.0, fontName : String = "NunitoSans-Regular",ignorFontBold: Bool = false) -> NSAttributedString {
let attr = (try? NSAttributedString(htmlString: html, font: UIFont(name: fontName, size: fontSize),ignorFontBold: ignorFontBold)) ?? NSAttributedString()
return attr
}
//Extension for NSAttribute string
extension NSAttributedString {
convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = false, ignorFontBold: Bool = false) throws {
let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let data = html.data(using: .utf8, allowLossyConversion: true)
guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else {
try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil)
return
}
let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
let range = NSRange(location: 0, length: attr.length)
attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
if let htmlFont = attrib as? UIFont {
let traits = htmlFont.fontDescriptor.symbolicTraits
var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)
if ignorFontBold == false{
if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitBold.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitBold)!
}
if (traits.rawValue & UIFontDescriptor.SymbolicTraits.traitItalic.rawValue) != 0 {
descrip = descrip.withSymbolicTraits(.traitItalic) ?? descrip
}
}
attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
}
}
self.init(attributedString: attr)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With