划线要点
笔记划线在小说阅读器中是非常常见和重要的功能点,那么笔记划线的两要素就必须要知道
- 起始位置
- 结束位置
在添加笔记的时候,需要将划线文本的起始和结束位置传递给后端,此外就是笔记内容,有一个特殊情况也需要考虑,如果是对文中的图片添加笔记以及图片的划线处理,这一点需要额外做些处理,笔者尝试,直接对图片的range
进行下划线添加属性配置,并不能实现图片划线
阅读文章的笔记要从后端获取,那么这个就需要事先设置笔记获取的规则,是整本书全部获取,还是单章或者单页获取,如果一本书笔记比较多,那整本获取的体验感就要稍差,笔者这边考虑的是以章节形式去获取笔记,然后对章节内容进行划线设置
服务端获取笔记与阅读器的交互
swift
// MARK - 请求当前章节的笔记数据
@objc public func fetchNotesData() {
// 章节每一次请求之前,都需要将旧的数据清空,重新将笔记进行临时存储
noteViewModel.noteList(bookId: self.bookModel.bookId, chapterNumber: self.bookModel.chapterIndex, success: nil)
}
提供一个获取笔记数据的方法,这个是在每次进入新章节的时候都会获取,所以在翻页的时候,需要判断当前章节和即将出现的章节是否为同一章节,如果不是,则需要请求章节的笔记
ini
if WLNoteConfig.shared.currentChapterModel == nil || chapterModel.chapterIndex != WLNoteConfig.shared.currentChapterModel.chapterIndex {
WLNoteConfig.shared.currentChapterModel = chapterModel
fetchNotesData()
}
文本内容的划线
笔者渲染采用的是 DTAttributedLabel
按理说,对富文本的划线,只需要设置好 underLineStyle
和 underLineColor
即可,但DTCoreText
的绘制做了一些其他操作,导致直接设置underLineColor
并未生效,需要针对每一个 element
做特殊处理
在章节列表和分页处理的时候,有一个构建html
富文本的操作
ini
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)
针对 element
配置的方法中,我们可以做一些配置
ini
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)
}
}
element.underlineColor = WL_READER_CURSOR_COLOR
这个就是配置下划线颜色
接下来就是下划线的style
和添加对应的link
属性,方便处理点击划线笔记事件
php
// MARK - 设置笔记对应的link
func addLinkToAttributeContent(with notes:[WLBookNoteModel]?) {
guard let notes = notes else { return }
let attr = NSMutableAttributedString(attributedString: self.attributedString)
// 先清除所有的划线
attr.enumerateAttribute(.link, in: NSRange(location: 0, length: attributedString.length)) { value, range, _ in
if value != nil {
attr.removeAttribute(.link, range: range)
}
}
// 再清除所有的下划线
attr.enumerateAttribute(.underlineStyle, in: NSRange(location: 0, length: attributedString.length)) { value, range, _ in
if value != nil {
attr.removeAttribute(.underlineStyle, range: range)
}
}
attr.enumerateAttribute(.underlineColor, in: NSRange(location: 0, length: attributedString.length)) { value, range, _ in
if value != nil {
attr.removeAttribute(.underlineColor, range: range)
}
}
for note in notes {
// 只有划线和笔记需要添加事件
if note.noteType == .line || note.noteType == .note {
if let sourceContent = note.sourceContent, sourceContent.type == 0 {
// 根据文本计算相对位置
let range = (self.attributedString.string as NSString).range(of: sourceContent.text!)
attr.addAttribute(.link, value: URL(string: note.noteIdStr!)!, range: range)
attr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue | NSUnderlineStyle.patternDot.rawValue, range: range)
}
// 如果当前是滚动模式
// if WLBookConfig.shared.effetType == .scroll {
//
// }else {
// attr.addAttribute(.link, value: URL(string: note.noteIdStr!)!, range: NSMakeRange(note.startLocation!, note.endLocation! - note.startLocation!))
// attr.addAttribute(.underlineStyle, value: NSUnderlineStyle.single.rawValue | NSUnderlineStyle.patternDot.rawValue, range: NSMakeRange(note.startLocation!, note.endLocation! - note.startLocation!))
// }
}
}
self.attributedString = attr
var rect = self.bounds
let insets = self.edgeInsets
rect.origin.x += insets.left;
rect.origin.y += insets.top;
rect.size.width -= (insets.left + insets.right);
rect.size.height -= (insets.top + insets.bottom);
let layoutFrame = self.layouter.layoutFrame(with: rect, range: self.contentRange)
self.layoutFrame = layoutFrame
}
划线笔记点击
swift
// MARK - 生成链接视图的代理
func attributedTextContentView(_ attributedTextContentView: DTAttributedTextContentView!, viewForLink url: URL!, identifier: String!, frame: CGRect) -> UIView! {
let btn = DTLinkButton(frame: frame)
btn.url = url
btn.alpha = 0.5
btn.addTarget(self, action: #selector(_onTapBtn(btn:)), for: .touchUpInside)
return btn
}
DTCoreText
中的 DTAttributedTextContentViewDelegate
提供了针对link
设置的代理,如上所示,可以通过这个添加点击事件
图片划线
关于图片划线,也是利用了 DTCoreText
的特点, DTAttributedTextContentViewDelegate
也提供了针对图片处理的代理
func attributedTextContentView(_ attributedTextContentView: DTAttributedTextContentView!, viewFor attachment: DTTextAttachment!, frame: CGRect) -> UIView!
那么该如何做呢?在这里可以拿到对应的 attachment
图片,然后通过笔记数据的特定标识判断当前图片是否做过笔记,如果做过,对图片控件做一些样式处理
ini
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
}
}
}
}
return imageView
}
return nil
}
图片的点击事件也是在这里配置的,具体点击后做什么操作,可以根据具体业务做处理
WLCustomLazyImageView
是继承 DTLazyImageView
,添加了一个图片标识
kotlin
import DTCoreText
class WLCustomLazyImageView: DTLazyImageView {
// 图片标识
public var imageIdentifier:String?
}