将 NSAttributedString 转换成 Data 进行存储
Convert NSAttributedString into Data for storage
我有一个带有属性文本的 UITextView
,allowsEditingTextAttributes
设置为 true
。
我正在尝试使用以下代码将属性字符串转换为数据对象:
let text = self.textView.attributedText
let data = try text.data(from: NSMakeRange(0, text.length), documentAttributes: [:])
但是,这引发了以下错误:
Error Domain=NSCocoaErrorDomain Code=66062 "(null)"
知道此错误的含义或可能导致此错误的原因吗?我在最新的 Xcode 和 iOS。谢谢。
您需要指定要将属性字符串转换为哪种文档数据:
.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)
更新 Xcode 10.2 • Swift 5 或更高版本
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)
}
}
对此进行扩展:
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) }
}
用法:
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" ...
}
这将打印
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>
*/
一旦对我有用,请检查一下。
您还可以使用以下功能更新字体样式和系列。
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)
}
}
我有一个带有属性文本的 UITextView
,allowsEditingTextAttributes
设置为 true
。
我正在尝试使用以下代码将属性字符串转换为数据对象:
let text = self.textView.attributedText
let data = try text.data(from: NSMakeRange(0, text.length), documentAttributes: [:])
但是,这引发了以下错误:
Error Domain=NSCocoaErrorDomain Code=66062 "(null)"
知道此错误的含义或可能导致此错误的原因吗?我在最新的 Xcode 和 iOS。谢谢。
您需要指定要将属性字符串转换为哪种文档数据:
.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)
更新 Xcode 10.2 • Swift 5 或更高版本
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)
}
}
对此进行扩展:
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) }
}
用法:
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" ...
}
这将打印
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>
*/
一旦对我有用,请检查一下。 您还可以使用以下功能更新字体样式和系列。
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)
}
}