Files
suixinkan_uikit/suixinkan/Base/BaseViewController.swift
汉秋 e290656322 Add base infrastructure and SPM dependencies for iOS development.
Integrate SnapKit and Kingfisher, add BaseViewController and NotificationName, and streamline AGENTS.md with Android-aligned conventions.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 14:59:55 +08:00

163 lines
4.4 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.

//
// 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-actiondelegate
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()
}
}