前言
阅读器内容渲染采用的是
DTCoreText框架,它对html格式的渲染支持非常友好,也提供了对标签中的图片,链接等的特殊处理方式,这一篇主要就是来介绍下如何处理图片标签
根据html构建富文本
let builder = DTHTMLAttributedStringBuilder(html: htmlData, options: options, documentAttributes: nil)
builder?.willFlushCallback = { element in
self.setEelementDisplay(element: element!)
}
guard let attributedString = builder?.generatedAttributedString() else { return }
chapterContentAttr = NSMutableAttributedString(attributedString: attributedString)
DTHTMLAttributedStringBuilder是DTCoreText提供的构建富文本的工具类,它里面有willFlushCallback回调,在这里可以拿到每一个element标签,可以对标签进行特殊处理
/// 这里之所以要这么写,因为对应的epub解析出来的html文件,对应的标签层级都不同,只不过对每一层都做一次过滤,保证对应的节点正常
public func setEelementDisplay(element:DTHTMLElement) {
guard let childNodes = element.childNodes else { return }
for node in childNodes {
let _node = node as! DTHTMLElement
configNoteDispaly(element: _node)
setNodeDisplay(element: _node)
}
}
private func setNodeDisplay(element:DTHTMLElement!) {
if (element.childNodes != nil) {
for node in element.childNodes {
configNoteDispaly(element: node as! DTHTMLElement)
setNodeDisplay(element: node as? DTHTMLElement)
}
}else {
configNoteDispaly(element: element)
}
}
private func configNoteDispaly(element:DTHTMLElement) {
// 设置下划线颜色
element.underlineColor = WL_READER_CURSOR_COLOR
// 设置段落样式
let paragraphStyle = element.paragraphStyle
paragraphStyle?.paragraphSpacing = WLBookConfig.shared.paragraphSpacing
element.paragraphStyle = paragraphStyle
if element.name == "img" || element.name == "image" {
setImageDisplay(element: element)
}else if element.name == "h1" || element.name == "h2" {
setHTitleDisplay(element: element)
}else if element.name == "figcaption" {
setFigcaptionDisplay(element: element)
}else if element.name == "blockquote" {
setBlockquoteDisplay(element: element)
}
}
如代码所示,这种处理方式应该能适应大部分的epub解析出来的文件格式了,具体的处理还需要看具体解析出来之后的html文本格式而定
处理图片
/// 更改图片的实际布局宽高
private func setImageDisplay(element:DTHTMLElement) {
if let childNodes = element.childNodes, childNodes.count > 0 {
for node in childNodes {
setImageDisplay(element: node as! DTHTMLElement)
}
}else {
if element.name == "img" || element.name == "image" {
var displayWidth: CGFloat? = nil
var displayHeight: CGFloat? = nil
if let width = element.attributes["width"] as? String {
displayWidth = CGFloat(Double(width) ?? 0)
}
if let height = element.attributes["height"] as? String {
displayHeight = CGFloat(Double(height) ?? 0)
}
guard let imageAttachment = element.textAttachment else { return }
let image = imageAttachment.image
guard let image = image else { return }
if displayWidth == nil || displayHeight == nil {
// 根据画布宽度和图片的实际尺寸调整显示尺寸
// 图片的宽高比
let aspectRatio = image.size.width / image.size.height
if displayWidth == nil, let height = displayHeight {
displayWidth = height * aspectRatio
} else if displayHeight == nil, let width = displayWidth {
displayHeight = width / aspectRatio
} else {
displayWidth = min(WLBookConfig.shared.readContentRect.width, image.size.width)
displayHeight = displayWidth! / aspectRatio
}
}
if displayHeight! > WLBookConfig.shared.readContentRect.height { // 如果图片高度比预定的显示高度要高,则给个默认的高度为最大高度值减去50,如果不这么做,在分页的时候,图片单独占一页会发现pageVisible的range对应的length 为0,就没法正确分页了,这个是比较坑的
displayHeight = WLBookConfig.shared.readContentRect.height - 50
}
// displayHeight = WLBookConfig.shared.readContentRect.height - 50
guard let finalWidth = displayWidth, let finalHeight = displayHeight else { return }
imageAttachment.displaySize = CGSize(width: finalWidth, height: finalHeight)
}
}
}
这里就是处理实际的图片标签的宽高,需要拿到富文本中的attachment,然后设置它的显示尺寸即可
除此之外,如果html文本中的 h1, figcaption, blockquote等标签需要有特定的样式,或者行间距,段间距等,都可以在这里进行特殊处理
图片显示
图片显示可以禁用掉DTAttributedLabel的shouldDrawImages,自定义图片显示
func attributedTextContentView(_ attributedTextContentView: DTAttributedTextContentView!, viewFor attachment: DTTextAttachment!, frame: CGRect) -> UIView! {
if attachment.isKind(of: DTImageTextAttachment.self) {
let imageView = WLCustomLazyImageView()
imageView.url = attachment.contentURL
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRectMake((attributedTextContentView.frame.width - frame.width) / 2.0, frame.origin.y, frame.width, frame.height)
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(_onTapImage(tap:)))
imageView.addGestureRecognizer(tap)
// 查看当前视图所在的位置,是否与笔记位置重合
guard let notes = WLNoteConfig.shared.readChapterNotes() else { return imageView }
for note in notes {
if note.noteType == .note || note.noteType == .line {
if note.sourceContent?.type == 1 {
if note.sourceContent?.imageSource == attachment.contentURL.relativePath {
imageView.imageIdentifier = note.noteIdStr
imageView.layer.borderColor = WL_READER_CURSOR_COLOR.cgColor
imageView.layer.borderWidth = 2.0
break
}
}
}
}
return imageView
}
return nil
}
这是 DTAttributedTextContentViewDelegate代理的方法,可以对具体的图片进行特殊显示,返回的是一个渲染图片的view,可以是UIImageView,也可以是DTCoreText提供的DTLazyImageView
还可以在这里对图片进行点击,长按等事件的处理,另外就是如果笔记引用内容是图片,在这里也可以如上述代码中所示的那样,对做过笔记的图片进行特殊样式处理,比如描边