ReadViewSDK/Sources/RDEpubReaderView/EPUBUI/RDEPUBAnnotationEditorViewController.swift
shenlei 1422461224 将 PDF 成品阅读控制器下沉为 RDPDFReaderView SDK,并修复手机横屏单页连续竖滑
SDK 化收尾:
- 新增 RDPDFReaderView 本地 pod(阅读容器、成品控制器、划线/注释、画笔、
  目录/设置面板、主题与持久化契约),Demo 精简为 PDFKit 页面提供者 + 持久化宿主
- Podfile/工程接入 RDPDFReaderView 与 SnapKit,示例书改用 PDF 阅读器示例文件

手机横屏单页竖滑修复:
- cell 高度改为宽度适配后的纸张高度(sizeForItemAt + 宿主按页提供纵横比),
  纵向超出一屏的内容由外层 collectionView 连续滚动承接,不再被裁掉
- 页面内部不再开启同向嵌套的兜底竖向拖动,修复外层滚动被吞、无法滚到下一页
- 修复 applyContentSize 先评估拖动开关后写 contentSize 的顺序缺陷
- 页码按视口中心命中 cell 计算,补 didEndDragging 兜底;旋转重锚点与
  transitionToPage 改用真实布局位置
- 横竖屏同为竖滑时也重建 cell 并刷新全部可见页的适配配置
- 横屏单页点击不再跳页,只显隐工具栏;翻页交给连续竖滑

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 18:49:44 +09:00

143 lines
5.1 KiB
Swift

import UIKit
import SnapKit
final class RDEPUBAnnotationEditorViewController: UIViewController, UITextViewDelegate {
private let quote: String
private let initialNote: String?
private let theme: RDEPUBReaderTheme
private let onSave: (String?) -> Void
private let textView = UITextView()
private let countLabel = UILabel()
init(
quote: String,
initialNote: String?,
theme: RDEPUBReaderTheme,
onSave: @escaping (String?) -> Void
) {
self.quote = quote
self.initialNote = initialNote
self.theme = theme
self.onSave = onSave
super.init(nibName: nil, bundle: nil)
title = "写注释"
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = theme.toolBackgroundColor
configureNavigation()
configureContent()
updateCount()
DispatchQueue.main.async { [weak self] in self?.textView.becomeFirstResponder() }
}
private func configureNavigation() {
navigationItem.leftBarButtonItem = UIBarButtonItem(
title: "取消", style: .plain, target: self, action: #selector(cancelAction)
)
navigationItem.rightBarButtonItem = UIBarButtonItem(
title: "保存", style: .done, target: self, action: #selector(saveAction)
)
navigationController?.navigationBar.tintColor = .systemBlue
navigationController?.navigationBar.titleTextAttributes = [
.foregroundColor: theme.toolControlTextColor,
.font: UIFont.systemFont(ofSize: 17, weight: .semibold)
]
}
private func configureContent() {
let quoteCard = UIView()
let rail = UIView()
let quoteLabel = UILabel()
let inputCard = UIView()
view.addSubview(quoteCard)
quoteCard.addSubview(rail)
quoteCard.addSubview(quoteLabel)
view.addSubview(inputCard)
inputCard.addSubview(textView)
inputCard.addSubview(countLabel)
quoteCard.backgroundColor = theme.contentBackgroundColor.withAlphaComponent(0.78)
quoteCard.layer.cornerRadius = 16
rail.backgroundColor = UIColor(red: 0.94, green: 0.63, blue: 0.18, alpha: 1)
rail.layer.cornerRadius = 2
quoteLabel.text = quote
quoteLabel.numberOfLines = 5
quoteLabel.font = UIFont.systemFont(ofSize: 16)
quoteLabel.textColor = theme.contentTextColor.withAlphaComponent(0.85)
inputCard.backgroundColor = theme.contentBackgroundColor
inputCard.layer.cornerRadius = 18
inputCard.layer.shadowColor = UIColor.black.cgColor
inputCard.layer.shadowOpacity = 0.06
inputCard.layer.shadowRadius = 18
inputCard.layer.shadowOffset = CGSize(width: 0, height: 6)
textView.text = initialNote
textView.delegate = self
textView.font = UIFont.systemFont(ofSize: 17)
textView.textColor = theme.contentTextColor
textView.backgroundColor = .clear
textView.accessibilityIdentifier = "epub.reader.annotation.editor"
countLabel.font = UIFont.monospacedDigitSystemFont(ofSize: 12, weight: .regular)
countLabel.textColor = .secondaryLabel
countLabel.textAlignment = .right
quoteCard.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide).offset(20)
make.horizontalEdges.equalToSuperview().inset(20)
}
rail.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.verticalEdges.equalToSuperview().inset(16)
make.width.equalTo(4)
}
quoteLabel.snp.makeConstraints { make in
make.leading.equalTo(rail.snp.trailing).offset(14)
make.trailing.equalToSuperview().inset(18)
make.verticalEdges.equalToSuperview().inset(16)
}
inputCard.snp.makeConstraints { make in
make.top.equalTo(quoteCard.snp.bottom).offset(18)
make.horizontalEdges.equalToSuperview().inset(20)
make.bottom.equalTo(view.keyboardLayoutGuide.snp.top).offset(-16)
make.height.greaterThanOrEqualTo(210)
}
textView.snp.makeConstraints { make in
make.top.horizontalEdges.equalToSuperview().inset(14)
make.bottom.equalTo(countLabel.snp.top).offset(-8)
}
countLabel.snp.makeConstraints { make in
make.horizontalEdges.equalToSuperview().inset(16)
make.bottom.equalToSuperview().inset(12)
}
}
func textViewDidChange(_ textView: UITextView) {
if textView.text.count > 1000 {
textView.text = String(textView.text.prefix(1000))
}
updateCount()
}
private func updateCount() {
countLabel.text = "\(textView.text.count)/1000"
}
@objc private func cancelAction() {
dismiss(animated: true)
}
@objc private func saveAction() {
let note = textView.text.trimmingCharacters(in: .whitespacesAndNewlines)
onSave(note.isEmpty ? nil : note)
dismiss(animated: true)
}
}