iOS-小说阅读器功能拆分之笔记划线

划线要点

笔记划线在小说阅读器中是非常常见和重要的功能点,那么笔记划线的两要素就必须要知道

  • 起始位置
  • 结束位置

在添加笔记的时候,需要将划线文本的起始和结束位置传递给后端,此外就是笔记内容,有一个特殊情况也需要考虑,如果是对文中的图片添加笔记以及图片的划线处理,这一点需要额外做些处理,笔者尝试,直接对图片的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 按理说,对富文本的划线,只需要设置好 underLineStyleunderLineColor 即可,但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?
}
相关推荐
SunshineBrother5 小时前
shell脚本,怎么查找项目中的重复图片
ios
月未央5 小时前
HarmonyOS Next 开发系列:Provider和Consumer状态修饰器实践
ios·harmonyos
北京自在科技1 天前
【Find My功能科普】防盗黑科技如何改变生活?
科技·ios·生活·findmy
水木姚姚2 天前
图形界面控件编程(iOS)
人工智能·python·macos·ios·xcode
书弋江山2 天前
Flutter 调用原生IOS接口
flutter·ios·cocoa
q567315232 天前
使用ASIWebPageRequest库编写Objective-C下载器程序
android·开发语言·macos·ios·objective-c·iphone
晨枫阳2 天前
不同开发语言之for循环的用法、区别总结
开发语言·python·objective-c·swift·js
星海拾遗2 天前
debug_unpack_ios failed: Exception: Failed to codesign 解决方案(亲测有效)
flutter·ios
水木姚姚2 天前
视频软件编程(iOS)
macos·ios·objective-c·音视频·xcode
L_Jason先生2 天前
iOS 聊天 IM 消息收发管理工具
前端·ios·设计模式·cocoa·责任链模式·适配器模式