- 新增字体选择(系统/宋体/圆体/等宽)与暗色图片柔化配置 - 文本选择改为自定义手势+操作栏(拷贝/高亮/批注) - 添加 accessibilityIdentifier 支持自动化 UI 测试 - 新增 UITests 覆盖阅读器打开/关闭、工具栏、设置面板、批注等 - 添加 Demo 测试用 EPUB 书源(宝山辽墓材料与释读) - 新增文档:UI 自动化测试、功能开发计划、阅读器规划
161 lines
6.1 KiB
Swift
161 lines
6.1 KiB
Swift
import UIKit
|
||
|
||
// MARK: - 底部工具栏
|
||
|
||
/// 阅读器底部工具栏
|
||
/// 提供目录、书签、高亮管理、新建标注和设置五个功能入口
|
||
/// 按钮可见性可根据 configuration 动态调整
|
||
final class RDEPUBReaderBottomToolView: RDEPUBReaderToolView {
|
||
// MARK: 回调闭包
|
||
|
||
/// 点击目录按钮回调
|
||
var onShowTableOfContents: (() -> Void)?
|
||
/// 点击书签列表按钮回调
|
||
var onShowBookmarks: (() -> Void)?
|
||
/// 点击高亮列表按钮回调
|
||
var onShowHighlights: (() -> Void)?
|
||
/// 点击新建标注按钮回调
|
||
var onAddHighlight: (() -> Void)?
|
||
/// 点击设置按钮回调
|
||
var onShowSettings: (() -> Void)?
|
||
|
||
// MARK: UI 组件
|
||
|
||
/// 水平等分布局的按钮容器
|
||
private let stackView: UIStackView = {
|
||
let view = UIStackView()
|
||
view.axis = .horizontal
|
||
view.distribution = .fillEqually
|
||
view.alignment = .fill
|
||
view.spacing = 16
|
||
return view
|
||
}()
|
||
|
||
/// 目录按钮
|
||
private let chapterButton = RDEPUBReaderTintButton(type: .system)
|
||
/// 书签列表按钮
|
||
private let bookmarksButton = RDEPUBReaderTintButton(type: .system)
|
||
/// 高亮列表按钮
|
||
private let highlightsButton = RDEPUBReaderTintButton(type: .system)
|
||
/// 新建标注按钮
|
||
private let addHighlightButton = RDEPUBReaderTintButton(type: .system)
|
||
/// 设置按钮
|
||
private let settingsButton = RDEPUBReaderTintButton(type: .system)
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
accessibilityIdentifier = "epub.reader.bottomToolbar"
|
||
|
||
addSubview(stackView)
|
||
stackView.translatesAutoresizingMaskIntoConstraints = false
|
||
stackView.addArrangedSubview(chapterButton)
|
||
stackView.addArrangedSubview(bookmarksButton)
|
||
stackView.addArrangedSubview(highlightsButton)
|
||
stackView.addArrangedSubview(addHighlightButton)
|
||
stackView.addArrangedSubview(settingsButton)
|
||
|
||
NSLayoutConstraint.activate([
|
||
stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
|
||
stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
|
||
stackView.topAnchor.constraint(equalTo: topAnchor),
|
||
stackView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor)
|
||
])
|
||
|
||
configureButton(chapterButton, systemName: "list.bullet", fallbackTitle: "目录")
|
||
configureButton(bookmarksButton, systemName: "bookmark", fallbackTitle: "书签")
|
||
configureButton(highlightsButton, systemName: "note.text", fallbackTitle: "批注")
|
||
configureButton(addHighlightButton, systemName: "highlighter", fallbackTitle: "标注")
|
||
configureButton(settingsButton, systemName: "textformat.size", fallbackTitle: "设置")
|
||
chapterButton.accessibilityIdentifier = "epub.reader.toc"
|
||
bookmarksButton.accessibilityIdentifier = "epub.reader.bookmarks"
|
||
highlightsButton.accessibilityIdentifier = "epub.reader.highlights"
|
||
addHighlightButton.accessibilityIdentifier = "epub.reader.add-highlight"
|
||
settingsButton.accessibilityIdentifier = "epub.reader.settings"
|
||
|
||
[chapterButton, bookmarksButton, highlightsButton, addHighlightButton, settingsButton].forEach { button in
|
||
button.heightAnchor.constraint(greaterThanOrEqualToConstant: 44).isActive = true
|
||
}
|
||
|
||
chapterButton.addTarget(self, action: #selector(chapterAction), for: .touchUpInside)
|
||
bookmarksButton.addTarget(self, action: #selector(bookmarksAction), for: .touchUpInside)
|
||
highlightsButton.addTarget(self, action: #selector(highlightsAction), for: .touchUpInside)
|
||
addHighlightButton.addTarget(self, action: #selector(highlightAction), for: .touchUpInside)
|
||
settingsButton.addTarget(self, action: #selector(settingsAction), for: .touchUpInside)
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func lineFrame(in bounds: CGRect) -> CGRect {
|
||
CGRect(x: 0, y: 0, width: bounds.width, height: 0.5)
|
||
}
|
||
|
||
override func apply(theme: RDEPUBReaderTheme) {
|
||
super.apply(theme: theme)
|
||
[chapterButton, bookmarksButton, highlightsButton, addHighlightButton, settingsButton].forEach { button in
|
||
button.tintColor = .black
|
||
button.setTitleColor(.black, for: .normal)
|
||
}
|
||
}
|
||
|
||
// MARK: 按钮状态控制
|
||
|
||
/// 设置新建标注按钮是否可用(有选中文本时可用)
|
||
func setAddHighlightEnabled(_ isEnabled: Bool) {
|
||
addHighlightButton.isEnabled = isEnabled
|
||
addHighlightButton.alpha = isEnabled ? 1 : 0.45
|
||
}
|
||
|
||
func setHighlightsEnabled(_ isEnabled: Bool) {
|
||
highlightsButton.isEnabled = isEnabled
|
||
highlightsButton.alpha = isEnabled ? 1 : 0.45
|
||
}
|
||
|
||
func setBookmarksEnabled(_ isEnabled: Bool) {
|
||
bookmarksButton.isEnabled = isEnabled
|
||
bookmarksButton.alpha = isEnabled ? 1 : 0.45
|
||
}
|
||
|
||
func updateVisibility(
|
||
showsTableOfContents: Bool,
|
||
allowsHighlights: Bool,
|
||
showsSettingsPanel: Bool
|
||
) {
|
||
chapterButton.isHidden = !showsTableOfContents
|
||
bookmarksButton.isHidden = false
|
||
highlightsButton.isHidden = !allowsHighlights
|
||
addHighlightButton.isHidden = !allowsHighlights
|
||
settingsButton.isHidden = !showsSettingsPanel
|
||
}
|
||
|
||
private func configureButton(_ button: UIButton, systemName: String, fallbackTitle: String) {
|
||
button.titleLabel?.font = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||
if #available(iOS 13.0, *), let image = UIImage(systemName: systemName) {
|
||
button.setImage(image.withRenderingMode(.alwaysTemplate), for: .normal)
|
||
} else {
|
||
button.setTitle(fallbackTitle, for: .normal)
|
||
}
|
||
}
|
||
|
||
@objc private func chapterAction() {
|
||
onShowTableOfContents?()
|
||
}
|
||
|
||
@objc private func bookmarksAction() {
|
||
onShowBookmarks?()
|
||
}
|
||
|
||
@objc private func highlightsAction() {
|
||
onShowHighlights?()
|
||
}
|
||
|
||
@objc private func highlightAction() {
|
||
onAddHighlight?()
|
||
}
|
||
|
||
@objc private func settingsAction() {
|
||
onShowSettings?()
|
||
}
|
||
}
|