ReadViewSDK/Sources/RDReaderView/EPUBUI/RDEPUBReaderTopToolView.swift
shenlei 0e7c0577e3 feat: add in-reader search and restructure documentation
- Add RDEPUBReaderSearchBarView with animated show/hide, keyword
  navigation, and match counting integrated into the reader controller
- Restructure docs: replace scattered design docs with consolidated
  BUSINESS_LOGIC.md and UML_CLASS_DIAGRAMS.md; update ARCHITECTURE.md
- Add SearchTests and FanrenParseTimeTest; enhance LargeBookOnDemandTests
- Add scripts/run_ui_regression.sh and summarize_ui_results.py for
  automated UI test execution and reporting

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 17:34:50 +08:00

141 lines
5.9 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}
}
}