// // BaseViewController.swift // suixinkan // import SnapKit import UIKit /// 业务 ViewController 基类,提供统一的 UI 搭建流程与通用 UI 能力。 class BaseViewController: UIViewController { private var loadingOverlay: UIView? private var loadingIndicator: UIActivityIndicatorView? // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .systemBackground setupNavigationBar() setupUI() setupConstraints() bindActions() registerBaseNotifications() } deinit { NotificationCenter.default.removeObserver(self) } // MARK: - Template Methods /// 配置导航栏,子类按需重写。 func setupNavigationBar() {} /// 创建并添加子视图。 func setupUI() {} /// 使用 SnapKit 设置约束。 func setupConstraints() {} /// 绑定 target-action、delegate 等交互。 func bindActions() {} /// Token 失效时回调,子类可重写处理跳转登录等逻辑。 func onSessionDidExpire() {} // MARK: - Loading func showLoading() { if loadingOverlay == nil { let overlay = UIView() overlay.backgroundColor = UIColor.black.withAlphaComponent(0.1) let indicator = UIActivityIndicatorView(style: .large) indicator.hidesWhenStopped = true view.addSubview(overlay) overlay.addSubview(indicator) overlay.snp.makeConstraints { make in make.edges.equalToSuperview() } indicator.snp.makeConstraints { make in make.center.equalToSuperview() } loadingOverlay = overlay loadingIndicator = indicator } loadingOverlay?.isHidden = false loadingIndicator?.startAnimating() view.bringSubviewToFront(loadingOverlay!) } func hideLoading() { loadingIndicator?.stopAnimating() loadingOverlay?.isHidden = true } // MARK: - Toast / Alert func showToast(_ message: String, duration: TimeInterval = 2.0) { guard !message.isEmpty else { return } let container = UIView() container.backgroundColor = UIColor.black.withAlphaComponent(0.8) container.layer.cornerRadius = 8 container.clipsToBounds = true container.alpha = 0 let label = UILabel() label.text = message label.textColor = .white label.font = .systemFont(ofSize: 14) label.textAlignment = .center label.numberOfLines = 0 container.addSubview(label) view.addSubview(container) label.snp.makeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)) } container.snp.makeConstraints { make in make.centerX.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-32) make.leading.greaterThanOrEqualToSuperview().offset(32) make.trailing.lessThanOrEqualToSuperview().offset(-32) } UIView.animate(withDuration: 0.25) { container.alpha = 1 } completion: { _ in UIView.animate(withDuration: 0.25, delay: duration) { container.alpha = 0 } completion: { _ in container.removeFromSuperview() } } } func showError(_ message: String) { showToast(message) } func showAlert( title: String?, message: String?, confirmTitle: String = "确定", confirmHandler: (() -> Void)? = nil ) { let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: confirmTitle, style: .default) { _ in confirmHandler?() }) present(alert, animated: true) } // MARK: - Navigation func popViewController() { navigationController?.popViewController(animated: true) } // MARK: - Notifications private func registerBaseNotifications() { NotificationCenter.default.addObserver( self, selector: #selector(handleSessionDidExpireNotification), name: NotificationName.sessionDidExpire, object: nil ) } @objc private func handleSessionDidExpireNotification() { hideLoading() onSessionDidExpire() } }