169 lines
4.8 KiB
Swift
169 lines
4.8 KiB
Swift
//
|
|
// BaseViewController.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import SnapKit
|
|
import UIKit
|
|
|
|
/// 业务 ViewController 基类,提供统一的 UI 搭建流程与通用 UI 能力。
|
|
class BaseViewController: UIViewController {
|
|
|
|
// MARK: - Lifecycle
|
|
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
view.backgroundColor = AppColor.pageBackground
|
|
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
|
|
|
|
@MainActor
|
|
func showLoading() {
|
|
GlobalLoadingManager.shared.show()
|
|
}
|
|
|
|
@MainActor
|
|
func hideLoading() {
|
|
GlobalLoadingManager.shared.hide()
|
|
}
|
|
|
|
// 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 = AppRadius.sm
|
|
container.clipsToBounds = true
|
|
container.alpha = 0
|
|
|
|
let label = UILabel()
|
|
label.text = message
|
|
label.textColor = .white
|
|
label.font = .app(.body)
|
|
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.centerY.equalToSuperview()
|
|
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)
|
|
}
|
|
|
|
/// 沿 presentation 链找到当前最顶层控制器,避免 Sheet 已展示时 present 冲突。
|
|
func topMostPresentedViewController() -> UIViewController {
|
|
var top: UIViewController = self
|
|
while let presented = top.presentedViewController {
|
|
top = presented
|
|
}
|
|
return top
|
|
}
|
|
|
|
/// 在当前可见 presentation 链顶端弹出页面。
|
|
func presentOnTop(
|
|
_ viewController: UIViewController,
|
|
animated: Bool = true,
|
|
completion: (() -> Void)? = nil
|
|
) {
|
|
topMostPresentedViewController().present(viewController, animated: animated, completion: completion)
|
|
}
|
|
|
|
/// 全屏预览图片与视频媒体。
|
|
func presentMediaPreview(items: [MediaPreviewItem], startIndex: Int) {
|
|
guard !items.isEmpty else { return }
|
|
presentOnTop(MediaPreviewViewController(items: items, startIndex: startIndex))
|
|
}
|
|
|
|
/// 全屏预览网络图片。
|
|
func presentImagePreview(imageURLs: [String], startIndex: Int) {
|
|
guard !imageURLs.isEmpty else { return }
|
|
let items = imageURLs.compactMap { MediaPreviewItem.remoteImage($0) }
|
|
presentMediaPreview(items: items, startIndex: startIndex)
|
|
}
|
|
|
|
// MARK: - Notifications
|
|
|
|
private func registerBaseNotifications() {
|
|
NotificationCenter.default.addObserver(
|
|
self,
|
|
selector: #selector(handleSessionDidExpireNotification),
|
|
name: NotificationName.sessionDidExpire,
|
|
object: nil
|
|
)
|
|
}
|
|
|
|
@objc private func handleSessionDidExpireNotification() {
|
|
GlobalLoadingManager.shared.hideAll()
|
|
onSessionDidExpire()
|
|
}
|
|
}
|