235 lines
8.7 KiB
Swift
235 lines
8.7 KiB
Swift
//
|
||
// CloudDriveTransferViewController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
/// 云盘传输管理页,对齐 Android `CloudStorageTransitScreen`。
|
||
final class CloudDriveTransferViewController: BaseViewController {
|
||
private enum Tab: Int {
|
||
case upload
|
||
case download
|
||
}
|
||
|
||
private let manager: CloudTransferManager
|
||
private let segmentedControl = UISegmentedControl(items: ["上传", "下载"])
|
||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||
private var selectedTab: Tab = .upload
|
||
|
||
/// 初始化传输管理页。
|
||
init(manager: CloudTransferManager = .shared) {
|
||
self.manager = manager
|
||
super.init(nibName: nil, bundle: nil)
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func setupNavigationBar() {
|
||
title = "传输管理"
|
||
}
|
||
|
||
override func setupUI() {
|
||
view.backgroundColor = AppColor.pageBackground
|
||
segmentedControl.selectedSegmentIndex = selectedTab.rawValue
|
||
tableView.backgroundColor = AppColor.pageBackground
|
||
tableView.separatorStyle = .none
|
||
tableView.dataSource = self
|
||
tableView.delegate = self
|
||
tableView.register(CloudTransferCell.self, forCellReuseIdentifier: CloudTransferCell.reuseIdentifier)
|
||
|
||
view.addSubview(segmentedControl)
|
||
view.addSubview(tableView)
|
||
}
|
||
|
||
override func setupConstraints() {
|
||
segmentedControl.snp.makeConstraints { make in
|
||
make.top.equalTo(view.safeAreaLayoutGuide).offset(AppSpacing.sm)
|
||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||
make.height.equalTo(36)
|
||
}
|
||
tableView.snp.makeConstraints { make in
|
||
make.top.equalTo(segmentedControl.snp.bottom).offset(AppSpacing.sm)
|
||
make.leading.trailing.bottom.equalToSuperview()
|
||
}
|
||
}
|
||
|
||
override func bindActions() {
|
||
segmentedControl.addTarget(self, action: #selector(tabChanged), for: .valueChanged)
|
||
manager.onTasksChange = { [weak self] in
|
||
Task { @MainActor in self?.tableView.reloadData() }
|
||
}
|
||
}
|
||
|
||
private var visibleTasks: [CloudTransferTask] {
|
||
selectedTab == .upload ? manager.uploadTasks : manager.downloadTasks
|
||
}
|
||
|
||
@objc private func tabChanged() {
|
||
selectedTab = Tab(rawValue: segmentedControl.selectedSegmentIndex) ?? .upload
|
||
tableView.reloadData()
|
||
}
|
||
}
|
||
|
||
extension CloudDriveTransferViewController: UITableViewDataSource, UITableViewDelegate {
|
||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||
visibleTasks.count
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||
112
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: CloudTransferCell.reuseIdentifier, for: indexPath) as! CloudTransferCell
|
||
let task = visibleTasks[indexPath.row]
|
||
cell.apply(task: task)
|
||
cell.onPrimaryAction = { [weak self] in
|
||
guard let self else { return }
|
||
if task.kind == .upload {
|
||
task.status == .uploading ? self.manager.pauseUpload(id: task.id) : self.manager.resumeUpload(id: task.id)
|
||
} else {
|
||
task.status == .downloading ? self.manager.pauseDownload(id: task.id) : self.manager.resumeDownload(id: task.id)
|
||
}
|
||
}
|
||
cell.onCancel = { [weak self] in
|
||
guard let self else { return }
|
||
task.kind == .upload ? self.manager.cancelUpload(id: task.id) : self.manager.cancelDownload(id: task.id)
|
||
}
|
||
return cell
|
||
}
|
||
}
|
||
|
||
/// 云盘传输任务 Cell。
|
||
private final class CloudTransferCell: UITableViewCell {
|
||
static let reuseIdentifier = "CloudTransferCell"
|
||
|
||
var onPrimaryAction: (() -> Void)?
|
||
var onCancel: (() -> Void)?
|
||
|
||
private let cardView = UIView()
|
||
private let iconView = UIImageView()
|
||
private let titleLabel = UILabel()
|
||
private let statusLabel = UILabel()
|
||
private let primaryButton = UIButton(type: .system)
|
||
private let cancelButton = UIButton(type: .system)
|
||
private let progressView = UIProgressView(progressViewStyle: .default)
|
||
private let percentLabel = UILabel()
|
||
|
||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||
setupUI()
|
||
setupConstraints()
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func prepareForReuse() {
|
||
super.prepareForReuse()
|
||
onPrimaryAction = nil
|
||
onCancel = nil
|
||
}
|
||
|
||
func apply(task: CloudTransferTask) {
|
||
iconView.image = CloudDriveAsset.image(
|
||
named: task.fileType == 1 ? "icon_cloud_storage_file_type_video" : "icon_cloud_storage_file_type_photo",
|
||
fallbackSystemName: task.fileType == 1 ? "video.fill" : "photo.fill"
|
||
)
|
||
titleLabel.text = task.fileName
|
||
statusLabel.text = "\(task.status.title) \(CloudDriveFormatter.fileSize(task.fileSize))"
|
||
progressView.progress = Float(task.progress) / 100
|
||
percentLabel.text = "\(task.progress)%"
|
||
let isRunning = task.status == .uploading || task.status == .downloading
|
||
primaryButton.setImage(UIImage(systemName: isRunning ? "pause.fill" : "play.fill"), for: .normal)
|
||
primaryButton.isHidden = task.status == .completed
|
||
}
|
||
|
||
private func setupUI() {
|
||
backgroundColor = .clear
|
||
selectionStyle = .none
|
||
cardView.backgroundColor = .white
|
||
cardView.layer.cornerRadius = AppRadius.sm
|
||
cardView.clipsToBounds = true
|
||
|
||
iconView.tintColor = AppColor.primary
|
||
iconView.contentMode = .scaleAspectFit
|
||
titleLabel.font = .app(.body)
|
||
titleLabel.textColor = AppColor.textPrimary
|
||
titleLabel.lineBreakMode = .byTruncatingMiddle
|
||
statusLabel.font = .app(.caption)
|
||
statusLabel.textColor = AppColor.textTertiary
|
||
progressView.progressTintColor = AppColor.primary
|
||
percentLabel.font = .app(.caption)
|
||
percentLabel.textColor = AppColor.textSecondary
|
||
percentLabel.textAlignment = .right
|
||
|
||
primaryButton.tintColor = AppColor.primary
|
||
cancelButton.tintColor = AppColor.textSecondary
|
||
cancelButton.setImage(UIImage(systemName: "xmark"), for: .normal)
|
||
primaryButton.addTarget(self, action: #selector(primaryTapped), for: .touchUpInside)
|
||
cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
|
||
|
||
contentView.addSubview(cardView)
|
||
cardView.addSubview(iconView)
|
||
cardView.addSubview(titleLabel)
|
||
cardView.addSubview(statusLabel)
|
||
cardView.addSubview(primaryButton)
|
||
cardView.addSubview(cancelButton)
|
||
cardView.addSubview(progressView)
|
||
cardView.addSubview(percentLabel)
|
||
}
|
||
|
||
private func setupConstraints() {
|
||
cardView.snp.makeConstraints { make in
|
||
make.top.bottom.equalToSuperview().inset(AppSpacing.xs)
|
||
make.leading.trailing.equalToSuperview().inset(AppSpacing.md)
|
||
}
|
||
iconView.snp.makeConstraints { make in
|
||
make.leading.top.equalToSuperview().offset(AppSpacing.sm)
|
||
make.width.height.equalTo(48)
|
||
}
|
||
cancelButton.snp.makeConstraints { make in
|
||
make.trailing.top.equalToSuperview().inset(AppSpacing.xs)
|
||
make.width.height.equalTo(36)
|
||
}
|
||
primaryButton.snp.makeConstraints { make in
|
||
make.trailing.equalTo(cancelButton.snp.leading)
|
||
make.centerY.equalTo(cancelButton)
|
||
make.width.height.equalTo(36)
|
||
}
|
||
titleLabel.snp.makeConstraints { make in
|
||
make.leading.equalTo(iconView.snp.trailing).offset(AppSpacing.sm)
|
||
make.top.equalTo(iconView)
|
||
make.trailing.equalTo(primaryButton.snp.leading).offset(-AppSpacing.xs)
|
||
}
|
||
statusLabel.snp.makeConstraints { make in
|
||
make.leading.trailing.equalTo(titleLabel)
|
||
make.top.equalTo(titleLabel.snp.bottom).offset(AppSpacing.xxs)
|
||
}
|
||
progressView.snp.makeConstraints { make in
|
||
make.leading.equalTo(iconView)
|
||
make.trailing.equalToSuperview().inset(AppSpacing.sm)
|
||
make.top.equalTo(iconView.snp.bottom).offset(AppSpacing.sm)
|
||
}
|
||
percentLabel.snp.makeConstraints { make in
|
||
make.top.equalTo(progressView.snp.bottom).offset(AppSpacing.xxs)
|
||
make.trailing.equalTo(progressView)
|
||
}
|
||
}
|
||
|
||
@objc private func primaryTapped() {
|
||
onPrimaryAction?()
|
||
}
|
||
|
||
@objc private func cancelTapped() {
|
||
onCancel?()
|
||
}
|
||
}
|