import UIKit final class RDEPUBAnnotationEditorViewController: UIViewController, UITextViewDelegate { private let quote: String private let initialNote: String? private let onSave: (String?) -> Void private let textView = UITextView() private let countLabel = UILabel() init(quote: String, initialNote: String?, onSave: @escaping (String?) -> Void) { self.quote = quote self.initialNote = initialNote 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 = UIColor(red: 0.975, green: 0.965, blue: 0.935, alpha: 1) 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 = UIColor(red: 0.10, green: 0.34, blue: 0.78, alpha: 1) navigationController?.navigationBar.titleTextAttributes = [ .foregroundColor: UIColor(red: 0.08, green: 0.10, blue: 0.14, alpha: 1), .font: UIFont.systemFont(ofSize: 17, weight: .semibold) ] } private func configureContent() { let quoteCard = UIView() let rail = UIView() let quoteLabel = UILabel() let inputCard = UIView() [quoteCard, rail, quoteLabel, inputCard, textView, countLabel].forEach { $0.translatesAutoresizingMaskIntoConstraints = false } view.addSubview(quoteCard) quoteCard.addSubview(rail) quoteCard.addSubview(quoteLabel) view.addSubview(inputCard) inputCard.addSubview(textView) inputCard.addSubview(countLabel) quoteCard.backgroundColor = UIColor.white.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 = UIColor(red: 0.19, green: 0.20, blue: 0.23, alpha: 1) inputCard.backgroundColor = .white 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 = UIColor(red: 0.08, green: 0.10, blue: 0.14, alpha: 1) textView.backgroundColor = .clear textView.accessibilityIdentifier = "epub.reader.annotation.editor" countLabel.font = UIFont.monospacedDigitSystemFont(ofSize: 12, weight: .regular) countLabel.textColor = .secondaryLabel countLabel.textAlignment = .right NSLayoutConstraint.activate([ quoteCard.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), quoteCard.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), quoteCard.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), rail.leadingAnchor.constraint(equalTo: quoteCard.leadingAnchor, constant: 16), rail.topAnchor.constraint(equalTo: quoteCard.topAnchor, constant: 16), rail.bottomAnchor.constraint(equalTo: quoteCard.bottomAnchor, constant: -16), rail.widthAnchor.constraint(equalToConstant: 4), quoteLabel.leadingAnchor.constraint(equalTo: rail.trailingAnchor, constant: 14), quoteLabel.trailingAnchor.constraint(equalTo: quoteCard.trailingAnchor, constant: -18), quoteLabel.topAnchor.constraint(equalTo: quoteCard.topAnchor, constant: 16), quoteLabel.bottomAnchor.constraint(equalTo: quoteCard.bottomAnchor, constant: -16), inputCard.topAnchor.constraint(equalTo: quoteCard.bottomAnchor, constant: 18), inputCard.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), inputCard.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20), inputCard.bottomAnchor.constraint(equalTo: view.keyboardLayoutGuide.topAnchor, constant: -16), inputCard.heightAnchor.constraint(greaterThanOrEqualToConstant: 210), textView.topAnchor.constraint(equalTo: inputCard.topAnchor, constant: 14), textView.leadingAnchor.constraint(equalTo: inputCard.leadingAnchor, constant: 14), textView.trailingAnchor.constraint(equalTo: inputCard.trailingAnchor, constant: -14), textView.bottomAnchor.constraint(equalTo: countLabel.topAnchor, constant: -8), countLabel.leadingAnchor.constraint(equalTo: inputCard.leadingAnchor, constant: 16), countLabel.trailingAnchor.constraint(equalTo: inputCard.trailingAnchor, constant: -16), countLabel.bottomAnchor.constraint(equalTo: inputCard.bottomAnchor, constant: -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) } }