- 新增 RDEPUBReaderSearchCoordinator 与 RDEPUBSelectionState 管理搜索和选中状态 - 新增 BookmarkChromeStateTests、NavigationBackwardTests、SelectionAnnotateTests 等 UI 测试 - 新增多个边界测试 epub 样本(损坏结构、空归档、缺失文件、流式外链验证) - 重构阅读器 chrome 状态管理,统一 tool bar 与 search bar 交互 - 优化大书分页缓存策略(RDEPUBChapterSummaryDiskCache、RDEPUBPageCountCache) - 移除废弃的 RDEPUBLocationConverter 和 RDEPUBPageBreakPolicy - 更新 epub-bridge.js 与 JS bridge 通信协议 - 全面更新现有 UI 测试以适配新的 helper 和状态管理
142 lines
6.0 KiB
Swift
142 lines
6.0 KiB
Swift
import UIKit
|
||
|
||
// MARK: - 顶部工具栏
|
||
|
||
/// 阅读器顶部工具栏
|
||
/// 显示书名、返回按钮和书签按钮
|
||
/// 位于阅读内容上方,支持 iOS 13+ SF Symbols 和低版本文字回退
|
||
public final class RDEPUBReaderTopToolView: RDEPUBReaderToolView {
|
||
/// 返回按钮点击回调
|
||
var onBack: (() -> Void)?
|
||
/// 书签按钮点击回调
|
||
var onToggleBookmark: (() -> Void)?
|
||
/// 搜索按钮点击回调
|
||
var onSearch: (() -> Void)?
|
||
|
||
private let backButton = RDEPUBReaderTintButton(type: .system)
|
||
private let searchButton = RDEPUBReaderTintButton(type: .system)
|
||
private let bookmarkButton = RDEPUBReaderTintButton(type: .system)
|
||
private let titleLabel: UILabel = {
|
||
let label = UILabel()
|
||
label.textAlignment = .center
|
||
label.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
|
||
label.numberOfLines = 1
|
||
return label
|
||
}()
|
||
private var isBookmarked = false
|
||
|
||
override init(frame: CGRect) {
|
||
super.init(frame: frame)
|
||
accessibilityIdentifier = "epub.reader.topToolbar"
|
||
self.backgroundColor = .white
|
||
addSubview(backButton)
|
||
addSubview(searchButton)
|
||
addSubview(bookmarkButton)
|
||
addSubview(titleLabel)
|
||
|
||
backButton.translatesAutoresizingMaskIntoConstraints = false
|
||
searchButton.translatesAutoresizingMaskIntoConstraints = false
|
||
bookmarkButton.translatesAutoresizingMaskIntoConstraints = false
|
||
titleLabel.translatesAutoresizingMaskIntoConstraints = false
|
||
|
||
backButton.addTarget(self, action: #selector(backAction), for: .touchUpInside)
|
||
searchButton.addTarget(self, action: #selector(searchAction), for: .touchUpInside)
|
||
bookmarkButton.addTarget(self, action: #selector(bookmarkAction), for: .touchUpInside)
|
||
|
||
NSLayoutConstraint.activate([
|
||
backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
|
||
backButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 4),
|
||
backButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
|
||
backButton.widthAnchor.constraint(equalToConstant: 44),
|
||
backButton.heightAnchor.constraint(equalToConstant: 44),
|
||
|
||
bookmarkButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
|
||
bookmarkButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 4),
|
||
bookmarkButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
|
||
bookmarkButton.widthAnchor.constraint(equalToConstant: 44),
|
||
bookmarkButton.heightAnchor.constraint(equalToConstant: 44),
|
||
|
||
searchButton.trailingAnchor.constraint(equalTo: bookmarkButton.leadingAnchor, constant: -4),
|
||
searchButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 4),
|
||
searchButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -4),
|
||
searchButton.widthAnchor.constraint(equalToConstant: 44),
|
||
searchButton.heightAnchor.constraint(equalToConstant: 44),
|
||
|
||
titleLabel.leadingAnchor.constraint(equalTo: backButton.trailingAnchor, constant: 8),
|
||
titleLabel.trailingAnchor.constraint(equalTo: searchButton.leadingAnchor, constant: -8),
|
||
titleLabel.centerYAnchor.constraint(equalTo: backButton.centerYAnchor)
|
||
])
|
||
|
||
if #available(iOS 13.0, *) {
|
||
backButton.setImage(UIImage(systemName: "chevron.left")?.withRenderingMode(.alwaysTemplate), for: .normal)
|
||
searchButton.setImage(UIImage(systemName: "magnifyingglass")?.withRenderingMode(.alwaysTemplate), for: .normal)
|
||
} else {
|
||
backButton.setTitle("返回", for: .normal)
|
||
searchButton.setTitle("搜索", for: .normal)
|
||
}
|
||
backButton.accessibilityIdentifier = "epub.reader.back"
|
||
searchButton.accessibilityIdentifier = "epub.reader.search"
|
||
bookmarkButton.accessibilityIdentifier = "epub.reader.bookmark"
|
||
titleLabel.accessibilityIdentifier = "epub.reader.title"
|
||
updateBookmarkButtonAppearance()
|
||
}
|
||
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override public func lineFrame(in bounds: CGRect) -> CGRect {
|
||
CGRect(x: 0, y: bounds.height - 0.5, width: bounds.width, height: 0.5)
|
||
}
|
||
|
||
override public func apply(theme: RDEPUBReaderTheme) {
|
||
super.apply(theme: theme)
|
||
titleLabel.textColor = theme.toolControlTextColor
|
||
backButton.tintColor = theme.toolControlTextColor
|
||
searchButton.tintColor = theme.toolControlTextColor
|
||
bookmarkButton.tintColor = theme.toolControlTextColor
|
||
if #unavailable(iOS 13.0) {
|
||
backButton.setTitleColor(theme.toolControlTextColor, for: .normal)
|
||
searchButton.setTitleColor(theme.toolControlTextColor, for: .normal)
|
||
bookmarkButton.setTitleColor(theme.toolControlTextColor, for: .normal)
|
||
}
|
||
updateBookmarkButtonAppearance()
|
||
}
|
||
|
||
func setTitle(_ title: String?) {
|
||
titleLabel.text = title
|
||
}
|
||
|
||
func setBookmarkSelected(_ isSelected: Bool) {
|
||
isBookmarked = isSelected
|
||
updateBookmarkButtonAppearance()
|
||
}
|
||
|
||
func setBookmarkEnabled(_ isEnabled: Bool) {
|
||
bookmarkButton.isEnabled = isEnabled
|
||
bookmarkButton.alpha = isEnabled ? 1 : 0.45
|
||
}
|
||
|
||
@objc private func backAction() {
|
||
onBack?()
|
||
}
|
||
|
||
@objc private func searchAction() {
|
||
onSearch?()
|
||
}
|
||
|
||
@objc private func bookmarkAction() {
|
||
onToggleBookmark?()
|
||
}
|
||
|
||
private func updateBookmarkButtonAppearance() {
|
||
if #available(iOS 13.0, *) {
|
||
let imageName = isBookmarked ? "bookmark.fill" : "bookmark"
|
||
bookmarkButton.setImage(UIImage(systemName: imageName)?.withRenderingMode(.alwaysTemplate), for: .normal)
|
||
} else {
|
||
bookmarkButton.setTitle(isBookmarked ? "已签" : "书签", for: .normal)
|
||
}
|
||
bookmarkButton.accessibilityValue = isBookmarked ? "selected" : "unselected"
|
||
}
|
||
}
|