660 lines
25 KiB
Swift
660 lines
25 KiB
Swift
//
|
||
// CloudDriveActionSheetViewController.swift
|
||
// suixinkan
|
||
//
|
||
|
||
import SnapKit
|
||
import UIKit
|
||
|
||
/// 云盘底部操作弹层,严格同步 Android 上传与文件操作菜单。
|
||
final class CloudDriveActionSheetViewController: UIViewController {
|
||
/// 云盘弹层内容布局。
|
||
enum Layout {
|
||
case upload
|
||
case controls
|
||
}
|
||
|
||
/// 云盘底部弹层操作项。
|
||
struct Item {
|
||
/// 操作文案。
|
||
let title: String
|
||
/// 操作图标。
|
||
let image: UIImage?
|
||
/// 是否展示为危险操作。
|
||
let isDestructive: Bool
|
||
/// 点击后的业务动作。
|
||
let handler: () -> Void
|
||
}
|
||
|
||
private let sheetTitle: String
|
||
private let layout: Layout
|
||
private let items: [Item]
|
||
private let dimView = UIView()
|
||
private let panelView = UIView()
|
||
private let contentStack = UIStackView()
|
||
|
||
/// 初始化 Android 风格云盘底部弹层。
|
||
init(title: String, layout: Layout, items: [Item]) {
|
||
sheetTitle = title
|
||
self.layout = layout
|
||
self.items = items
|
||
super.init(nibName: nil, bundle: nil)
|
||
modalPresentationStyle = .overFullScreen
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
setupUI()
|
||
setupConstraints()
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
dimView.alpha = 0
|
||
panelView.transform = CGAffineTransform(translationX: 0, y: 420)
|
||
}
|
||
|
||
override func viewDidAppear(_ animated: Bool) {
|
||
super.viewDidAppear(animated)
|
||
UIView.animate(withDuration: 0.24, delay: 0, options: [.curveEaseOut]) {
|
||
self.dimView.alpha = 1
|
||
self.panelView.transform = .identity
|
||
}
|
||
}
|
||
|
||
private func setupUI() {
|
||
view.backgroundColor = .clear
|
||
dimView.backgroundColor = UIColor.black.withAlphaComponent(0.35)
|
||
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelTapped)))
|
||
|
||
panelView.backgroundColor = .white
|
||
panelView.layer.cornerRadius = 16
|
||
panelView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||
panelView.clipsToBounds = true
|
||
|
||
contentStack.axis = .vertical
|
||
contentStack.spacing = 0
|
||
|
||
view.addSubview(dimView)
|
||
view.addSubview(panelView)
|
||
panelView.addSubview(contentStack)
|
||
|
||
let titleContainer = UIView()
|
||
let titleLabel = UILabel()
|
||
titleLabel.text = sheetTitle
|
||
titleLabel.font = .systemFont(ofSize: 18, weight: .semibold)
|
||
titleLabel.textColor = .black
|
||
titleLabel.textAlignment = .left
|
||
titleContainer.addSubview(titleLabel)
|
||
titleContainer.snp.makeConstraints { make in make.height.equalTo(52) }
|
||
titleLabel.snp.makeConstraints { make in
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.centerY.equalToSuperview()
|
||
}
|
||
contentStack.addArrangedSubview(titleContainer)
|
||
|
||
let titleDivider = UIView()
|
||
titleDivider.backgroundColor = UIColor(hex: 0xEEEEEE)
|
||
titleDivider.snp.makeConstraints { make in make.height.equalTo(0.5) }
|
||
contentStack.addArrangedSubview(titleDivider)
|
||
|
||
switch layout {
|
||
case .upload:
|
||
contentStack.addArrangedSubview(makeUploadContent())
|
||
case .controls:
|
||
contentStack.addArrangedSubview(makeControlsContent())
|
||
}
|
||
|
||
let divider = UIView()
|
||
divider.backgroundColor = UIColor(hex: 0xEEEEEE)
|
||
divider.snp.makeConstraints { make in make.height.equalTo(0.5) }
|
||
contentStack.addArrangedSubview(divider)
|
||
contentStack.addArrangedSubview(makeCancelArea())
|
||
let bottomSpacer = UIView()
|
||
bottomSpacer.snp.makeConstraints { make in make.height.equalTo(12) }
|
||
contentStack.addArrangedSubview(bottomSpacer)
|
||
}
|
||
|
||
private func setupConstraints() {
|
||
dimView.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||
panelView.snp.makeConstraints { make in
|
||
make.leading.trailing.bottom.equalToSuperview()
|
||
make.top.greaterThanOrEqualTo(view.safeAreaLayoutGuide).offset(20)
|
||
}
|
||
contentStack.snp.makeConstraints { make in
|
||
make.top.leading.trailing.equalToSuperview()
|
||
make.bottom.equalTo(view.safeAreaLayoutGuide)
|
||
}
|
||
}
|
||
|
||
private func makeUploadContent() -> UIView {
|
||
let container = UIView()
|
||
let stack = UIStackView()
|
||
stack.axis = .vertical
|
||
stack.spacing = 12
|
||
container.addSubview(stack)
|
||
stack.snp.makeConstraints { make in make.edges.equalToSuperview().inset(16) }
|
||
|
||
items.enumerated().forEach { index, item in
|
||
let row = UIView()
|
||
row.layer.cornerRadius = 8
|
||
row.layer.borderWidth = 1
|
||
row.layer.borderColor = UIColor(hex: 0xF4F4F4).cgColor
|
||
|
||
let iconView = UIImageView(image: item.image?.withRenderingMode(.alwaysOriginal))
|
||
iconView.contentMode = .scaleAspectFit
|
||
let label = UILabel()
|
||
label.text = item.title
|
||
label.font = .systemFont(ofSize: 14, weight: .bold)
|
||
label.textColor = .black
|
||
let button = UIButton(type: .custom)
|
||
button.tag = index
|
||
button.accessibilityLabel = item.title
|
||
button.addTarget(self, action: #selector(itemTapped(_:)), for: .touchUpInside)
|
||
|
||
row.addSubview(iconView)
|
||
row.addSubview(label)
|
||
row.addSubview(button)
|
||
iconView.snp.makeConstraints { make in
|
||
make.leading.equalToSuperview().offset(16)
|
||
make.centerY.equalToSuperview()
|
||
make.width.height.equalTo(32)
|
||
}
|
||
label.snp.makeConstraints { make in
|
||
make.leading.equalTo(iconView.snp.trailing).offset(12)
|
||
make.centerY.equalToSuperview()
|
||
make.trailing.lessThanOrEqualToSuperview().inset(16)
|
||
}
|
||
button.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||
row.snp.makeConstraints { make in make.height.equalTo(56) }
|
||
stack.addArrangedSubview(row)
|
||
}
|
||
return container
|
||
}
|
||
|
||
private func makeControlsContent() -> UIView {
|
||
let row = UIStackView()
|
||
row.axis = .horizontal
|
||
row.distribution = .fillEqually
|
||
|
||
items.enumerated().forEach { index, item in
|
||
let container = UIView()
|
||
let iconView = UIImageView(image: item.image?.withRenderingMode(.alwaysOriginal))
|
||
iconView.contentMode = .scaleAspectFit
|
||
let label = UILabel()
|
||
label.text = item.title
|
||
label.font = .systemFont(ofSize: 14)
|
||
label.textColor = UIColor(hex: 0x4B5563)
|
||
label.textAlignment = .center
|
||
let button = UIButton(type: .custom)
|
||
button.tag = index
|
||
button.accessibilityLabel = item.title
|
||
button.addTarget(self, action: #selector(itemTapped(_:)), for: .touchUpInside)
|
||
|
||
container.addSubview(iconView)
|
||
container.addSubview(label)
|
||
container.addSubview(button)
|
||
iconView.snp.makeConstraints { make in
|
||
make.top.equalToSuperview().offset(28)
|
||
make.centerX.equalToSuperview()
|
||
make.width.height.equalTo(24)
|
||
}
|
||
label.snp.makeConstraints { make in
|
||
make.top.equalTo(iconView.snp.bottom).offset(8)
|
||
make.leading.trailing.equalToSuperview().inset(4)
|
||
}
|
||
button.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||
container.snp.makeConstraints { make in make.height.equalTo(105) }
|
||
row.addArrangedSubview(container)
|
||
}
|
||
return row
|
||
}
|
||
|
||
private func makeCancelArea() -> UIView {
|
||
let container = UIView()
|
||
let button = UIButton(type: .system)
|
||
button.setTitle("取消", for: .normal)
|
||
button.setTitleColor(UIColor(hex: 0x4B5563), for: .normal)
|
||
button.titleLabel?.font = .systemFont(ofSize: 16, weight: .medium)
|
||
button.backgroundColor = UIColor(hex: 0xF4F4F4)
|
||
button.layer.cornerRadius = 12
|
||
button.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
|
||
container.addSubview(button)
|
||
button.snp.makeConstraints { make in
|
||
make.edges.equalToSuperview().inset(16)
|
||
make.height.equalTo(48)
|
||
}
|
||
return container
|
||
}
|
||
|
||
private func close(completion: (() -> Void)? = nil) {
|
||
UIView.animate(withDuration: 0.2, delay: 0, options: [.curveEaseIn]) {
|
||
self.dimView.alpha = 0
|
||
self.panelView.transform = CGAffineTransform(translationX: 0, y: self.panelView.bounds.height + 40)
|
||
} completion: { _ in
|
||
self.dismiss(animated: false, completion: completion)
|
||
}
|
||
}
|
||
|
||
@objc private func itemTapped(_ sender: UIButton) {
|
||
guard items.indices.contains(sender.tag) else { return }
|
||
let handler = items[sender.tag].handler
|
||
close(completion: handler)
|
||
}
|
||
|
||
@objc private func cancelTapped() {
|
||
close()
|
||
}
|
||
}
|
||
|
||
/// 云盘居中对话框基类,提供 Android 风格遮罩、圆角和转场。
|
||
class CloudDriveOverlayDialogViewController: UIViewController {
|
||
let dimView = UIView()
|
||
let cardView = UIView()
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
modalPresentationStyle = .overFullScreen
|
||
view.backgroundColor = .clear
|
||
dimView.backgroundColor = UIColor.black.withAlphaComponent(0.35)
|
||
cardView.backgroundColor = .white
|
||
cardView.layer.cornerRadius = 12
|
||
cardView.clipsToBounds = true
|
||
view.addSubview(dimView)
|
||
view.addSubview(cardView)
|
||
dimView.snp.makeConstraints { make in make.edges.equalToSuperview() }
|
||
dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(cancelOverlay)))
|
||
}
|
||
|
||
override func viewWillAppear(_ animated: Bool) {
|
||
super.viewWillAppear(animated)
|
||
dimView.alpha = 0
|
||
cardView.alpha = 0
|
||
cardView.transform = CGAffineTransform(scaleX: 0.94, y: 0.94)
|
||
}
|
||
|
||
override func viewDidAppear(_ animated: Bool) {
|
||
super.viewDidAppear(animated)
|
||
UIView.animate(withDuration: 0.2) {
|
||
self.dimView.alpha = 1
|
||
self.cardView.alpha = 1
|
||
self.cardView.transform = .identity
|
||
}
|
||
}
|
||
|
||
func dismissOverlay(completion: (() -> Void)? = nil) {
|
||
UIView.animate(withDuration: 0.16) {
|
||
self.dimView.alpha = 0
|
||
self.cardView.alpha = 0
|
||
self.cardView.transform = CGAffineTransform(scaleX: 0.96, y: 0.96)
|
||
} completion: { _ in
|
||
self.dismiss(animated: false, completion: completion)
|
||
}
|
||
}
|
||
|
||
@objc private func cancelOverlay() {
|
||
dismissOverlay()
|
||
}
|
||
}
|
||
|
||
/// Android `WeDialog` 对齐的云盘删除确认对话框。
|
||
final class CloudDriveConfirmationDialogViewController: CloudDriveOverlayDialogViewController {
|
||
private let dialogTitle: String
|
||
private let message: String
|
||
private let onConfirm: () -> Void
|
||
|
||
/// 创建删除确认对话框。
|
||
init(title: String, message: String, onConfirm: @escaping () -> Void) {
|
||
dialogTitle = title
|
||
self.message = message
|
||
self.onConfirm = onConfirm
|
||
super.init(nibName: nil, bundle: nil)
|
||
modalPresentationStyle = .overFullScreen
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
|
||
let titleLabel = UILabel()
|
||
titleLabel.text = dialogTitle
|
||
titleLabel.font = .systemFont(ofSize: 17, weight: .bold)
|
||
titleLabel.textColor = .black
|
||
titleLabel.textAlignment = .center
|
||
let messageLabel = UILabel()
|
||
messageLabel.text = message
|
||
messageLabel.font = .systemFont(ofSize: 17)
|
||
messageLabel.textColor = .black
|
||
messageLabel.textAlignment = .center
|
||
messageLabel.numberOfLines = 0
|
||
let divider = UIView()
|
||
divider.backgroundColor = UIColor(hex: 0xD1D5DB)
|
||
let verticalDivider = UIView()
|
||
verticalDivider.backgroundColor = UIColor(hex: 0xD1D5DB)
|
||
let cancelButton = makeDialogButton(title: "取消", color: .black, action: #selector(cancelTapped))
|
||
let confirmButton = makeDialogButton(title: "确定", color: AppColor.primary, action: #selector(confirmTapped))
|
||
|
||
[titleLabel, messageLabel, divider, cancelButton, verticalDivider, confirmButton].forEach(cardView.addSubview)
|
||
cardView.snp.makeConstraints { make in
|
||
make.center.equalToSuperview()
|
||
make.width.equalToSuperview().multipliedBy(0.8)
|
||
}
|
||
titleLabel.snp.makeConstraints { make in
|
||
make.top.equalToSuperview().offset(32)
|
||
make.leading.trailing.equalToSuperview().inset(24)
|
||
}
|
||
messageLabel.snp.makeConstraints { make in
|
||
make.top.equalTo(titleLabel.snp.bottom).offset(16)
|
||
make.leading.trailing.equalToSuperview().inset(24)
|
||
}
|
||
divider.snp.makeConstraints { make in
|
||
make.top.equalTo(messageLabel.snp.bottom).offset(32)
|
||
make.leading.trailing.equalToSuperview()
|
||
make.height.equalTo(0.5)
|
||
}
|
||
cancelButton.snp.makeConstraints { make in
|
||
make.top.equalTo(divider.snp.bottom)
|
||
make.leading.bottom.equalToSuperview()
|
||
make.height.equalTo(56)
|
||
}
|
||
confirmButton.snp.makeConstraints { make in
|
||
make.top.bottom.width.equalTo(cancelButton)
|
||
make.leading.equalTo(cancelButton.snp.trailing)
|
||
make.trailing.equalToSuperview()
|
||
}
|
||
verticalDivider.snp.makeConstraints { make in
|
||
make.top.equalTo(divider.snp.bottom)
|
||
make.bottom.equalToSuperview()
|
||
make.centerX.equalToSuperview()
|
||
make.width.equalTo(0.5)
|
||
}
|
||
}
|
||
|
||
private func makeDialogButton(title: String, color: UIColor, action: Selector) -> UIButton {
|
||
let button = UIButton(type: .system)
|
||
button.setTitle(title, for: .normal)
|
||
button.setTitleColor(color, for: .normal)
|
||
button.titleLabel?.font = .systemFont(ofSize: 17, weight: .bold)
|
||
button.addTarget(self, action: action, for: .touchUpInside)
|
||
return button
|
||
}
|
||
|
||
@objc private func cancelTapped() {
|
||
dismissOverlay()
|
||
}
|
||
|
||
@objc private func confirmTapped() {
|
||
dismissOverlay(completion: onConfirm)
|
||
}
|
||
}
|
||
|
||
/// Android `AddFolderDialog` 对齐的新建文件夹输入对话框。
|
||
final class CloudDriveTextInputDialogViewController: CloudDriveOverlayDialogViewController {
|
||
private let dialogTitle: String
|
||
private let placeholder: String
|
||
private let onConfirm: (String) -> Void
|
||
private let textField = UITextField()
|
||
private let confirmButton = UIButton(type: .system)
|
||
|
||
/// 创建文本输入对话框。
|
||
init(title: String, placeholder: String, onConfirm: @escaping (String) -> Void) {
|
||
dialogTitle = title
|
||
self.placeholder = placeholder
|
||
self.onConfirm = onConfirm
|
||
super.init(nibName: nil, bundle: nil)
|
||
modalPresentationStyle = .overFullScreen
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
cardView.layer.cornerRadius = 16
|
||
let titleLabel = UILabel()
|
||
titleLabel.text = dialogTitle
|
||
titleLabel.font = .systemFont(ofSize: 18, weight: .bold)
|
||
titleLabel.textColor = .black
|
||
let divider = UIView()
|
||
divider.backgroundColor = UIColor(hex: 0xEEEEEE)
|
||
|
||
textField.placeholder = placeholder
|
||
textField.font = .systemFont(ofSize: 14)
|
||
textField.textColor = UIColor(hex: 0x333333)
|
||
textField.backgroundColor = UIColor(hex: 0xF4F4F4)
|
||
textField.layer.cornerRadius = 8
|
||
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 1))
|
||
textField.leftViewMode = .always
|
||
textField.clearButtonMode = .whileEditing
|
||
textField.returnKeyType = .done
|
||
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
|
||
textField.addTarget(self, action: #selector(confirmTapped), for: .editingDidEndOnExit)
|
||
|
||
let cancelButton = UIButton(type: .system)
|
||
cancelButton.setTitle("取消", for: .normal)
|
||
cancelButton.setTitleColor(AppColor.primary, for: .normal)
|
||
cancelButton.titleLabel?.font = .systemFont(ofSize: 14)
|
||
cancelButton.backgroundColor = UIColor(hex: 0xEFF6FF)
|
||
cancelButton.layer.cornerRadius = 8
|
||
cancelButton.addTarget(self, action: #selector(cancelTapped), for: .touchUpInside)
|
||
|
||
confirmButton.setTitle("确定", for: .normal)
|
||
confirmButton.setTitleColor(.white, for: .normal)
|
||
confirmButton.titleLabel?.font = .systemFont(ofSize: 14)
|
||
confirmButton.layer.cornerRadius = 8
|
||
confirmButton.addTarget(self, action: #selector(confirmTapped), for: .touchUpInside)
|
||
confirmButton.isEnabled = false
|
||
updateConfirmAppearance()
|
||
|
||
[titleLabel, divider, textField, cancelButton, confirmButton].forEach(cardView.addSubview)
|
||
cardView.snp.makeConstraints { make in
|
||
make.center.equalToSuperview()
|
||
make.leading.trailing.equalToSuperview().inset(28)
|
||
}
|
||
titleLabel.snp.makeConstraints { make in
|
||
make.top.equalToSuperview()
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.equalTo(52)
|
||
}
|
||
divider.snp.makeConstraints { make in
|
||
make.top.equalTo(titleLabel.snp.bottom)
|
||
make.leading.trailing.equalToSuperview()
|
||
make.height.equalTo(1)
|
||
}
|
||
textField.snp.makeConstraints { make in
|
||
make.top.equalTo(divider.snp.bottom).offset(16)
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.equalTo(48)
|
||
}
|
||
confirmButton.snp.makeConstraints { make in
|
||
make.top.equalTo(textField.snp.bottom).offset(20)
|
||
make.trailing.bottom.equalToSuperview().inset(16)
|
||
make.width.equalTo(72)
|
||
make.height.equalTo(38)
|
||
}
|
||
cancelButton.snp.makeConstraints { make in
|
||
make.trailing.equalTo(confirmButton.snp.leading).offset(-8)
|
||
make.centerY.width.height.equalTo(confirmButton)
|
||
}
|
||
|
||
NotificationCenter.default.addObserver(
|
||
self,
|
||
selector: #selector(keyboardFrameChanged(_:)),
|
||
name: UIResponder.keyboardWillChangeFrameNotification,
|
||
object: nil
|
||
)
|
||
}
|
||
|
||
deinit {
|
||
NotificationCenter.default.removeObserver(self)
|
||
}
|
||
|
||
private func updateConfirmAppearance() {
|
||
confirmButton.backgroundColor = confirmButton.isEnabled ? AppColor.primary : AppColor.buttonDisabled
|
||
}
|
||
|
||
@objc private func textChanged() {
|
||
confirmButton.isEnabled = !(textField.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||
updateConfirmAppearance()
|
||
}
|
||
|
||
@objc private func cancelTapped() {
|
||
view.endEditing(true)
|
||
dismissOverlay()
|
||
}
|
||
|
||
@objc private func confirmTapped() {
|
||
let value = (textField.text ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
|
||
guard !value.isEmpty else { return }
|
||
view.endEditing(true)
|
||
dismissOverlay { [onConfirm] in onConfirm(value) }
|
||
}
|
||
|
||
@objc private func keyboardFrameChanged(_ notification: Notification) {
|
||
guard let frame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return }
|
||
let frameInView = view.convert(frame, from: nil)
|
||
let overlap = max(0, cardView.frame.maxY + 16 - frameInView.minY)
|
||
UIView.animate(withDuration: 0.2) {
|
||
self.cardView.transform = CGAffineTransform(translationX: 0, y: -overlap)
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Android `MoveFileDialog` 对齐的云盘移动目标对话框。
|
||
final class CloudDriveMoveDialogViewController: CloudDriveOverlayDialogViewController, UITableViewDataSource, UITableViewDelegate {
|
||
private let folders: [CloudFile]
|
||
private let onConfirm: (Int) -> Void
|
||
private let tableView = UITableView(frame: .zero, style: .plain)
|
||
private var selectedID: Int
|
||
|
||
/// 创建移动目标选择对话框。
|
||
init(folders: [CloudFile], onConfirm: @escaping (Int) -> Void) {
|
||
self.folders = folders
|
||
self.onConfirm = onConfirm
|
||
selectedID = folders.first?.id ?? 0
|
||
super.init(nibName: nil, bundle: nil)
|
||
modalPresentationStyle = .overFullScreen
|
||
}
|
||
|
||
@available(*, unavailable)
|
||
required init?(coder: NSCoder) {
|
||
fatalError("init(coder:) has not been implemented")
|
||
}
|
||
|
||
override func viewDidLoad() {
|
||
super.viewDidLoad()
|
||
cardView.layer.cornerRadius = 28
|
||
let titleLabel = UILabel()
|
||
titleLabel.text = "移动到"
|
||
titleLabel.font = .systemFont(ofSize: 16, weight: .semibold)
|
||
titleLabel.textColor = UIColor(hex: 0x333333)
|
||
let messageLabel = UILabel()
|
||
messageLabel.text = "请选择目标文件夹"
|
||
messageLabel.font = .systemFont(ofSize: 14)
|
||
messageLabel.textColor = UIColor(hex: 0x666666)
|
||
|
||
tableView.backgroundColor = .white
|
||
tableView.separatorStyle = .none
|
||
tableView.rowHeight = 44
|
||
tableView.dataSource = self
|
||
tableView.delegate = self
|
||
|
||
let cancelButton = makeTextButton(title: "取消", color: UIColor(hex: 0x666666), action: #selector(cancelTapped))
|
||
let confirmButton = makeTextButton(title: "确定", color: AppColor.primary, action: #selector(confirmTapped))
|
||
|
||
[titleLabel, messageLabel, tableView, cancelButton, confirmButton].forEach(cardView.addSubview)
|
||
cardView.snp.makeConstraints { make in
|
||
make.center.equalToSuperview()
|
||
make.leading.trailing.equalToSuperview().inset(28)
|
||
}
|
||
titleLabel.snp.makeConstraints { make in
|
||
make.top.equalToSuperview().offset(24)
|
||
make.leading.trailing.equalToSuperview().inset(24)
|
||
}
|
||
messageLabel.snp.makeConstraints { make in
|
||
make.top.equalTo(titleLabel.snp.bottom).offset(12)
|
||
make.leading.trailing.equalTo(titleLabel)
|
||
}
|
||
tableView.snp.makeConstraints { make in
|
||
make.top.equalTo(messageLabel.snp.bottom).offset(12)
|
||
make.leading.trailing.equalToSuperview().inset(16)
|
||
make.height.equalTo(min(CGFloat(max(folders.count, 1)) * 44, UIScreen.main.bounds.height * 0.6))
|
||
}
|
||
confirmButton.snp.makeConstraints { make in
|
||
make.top.equalTo(tableView.snp.bottom).offset(8)
|
||
make.trailing.bottom.equalToSuperview().inset(12)
|
||
make.width.equalTo(64)
|
||
make.height.equalTo(44)
|
||
}
|
||
cancelButton.snp.makeConstraints { make in
|
||
make.trailing.equalTo(confirmButton.snp.leading)
|
||
make.centerY.width.height.equalTo(confirmButton)
|
||
}
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||
folders.count
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||
let reuseIdentifier = "CloudDriveMoveFolderCell"
|
||
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier)
|
||
?? UITableViewCell(style: .default, reuseIdentifier: reuseIdentifier)
|
||
configure(cell, folder: folders[indexPath.row])
|
||
return cell
|
||
}
|
||
|
||
private func configure(_ cell: UITableViewCell, folder: CloudFile) {
|
||
cell.textLabel?.text = folder.name
|
||
cell.textLabel?.font = .systemFont(ofSize: 14)
|
||
cell.textLabel?.textColor = UIColor(hex: 0x333333)
|
||
cell.imageView?.image = UIImage(
|
||
systemName: selectedID == folder.id ? "circle.inset.filled" : "circle",
|
||
withConfiguration: UIImage.SymbolConfiguration(pointSize: 20)
|
||
)
|
||
cell.imageView?.tintColor = AppColor.primary
|
||
cell.selectionStyle = .none
|
||
}
|
||
|
||
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||
let previousID = selectedID
|
||
selectedID = folders[indexPath.row].id
|
||
if let previousIndex = folders.firstIndex(where: { $0.id == previousID }),
|
||
let previousCell = tableView.cellForRow(at: IndexPath(row: previousIndex, section: 0)) {
|
||
configure(previousCell, folder: folders[previousIndex])
|
||
}
|
||
if let selectedCell = tableView.cellForRow(at: indexPath) {
|
||
configure(selectedCell, folder: folders[indexPath.row])
|
||
}
|
||
}
|
||
|
||
private func makeTextButton(title: String, color: UIColor, action: Selector) -> UIButton {
|
||
let button = UIButton(type: .system)
|
||
button.setTitle(title, for: .normal)
|
||
button.setTitleColor(color, for: .normal)
|
||
button.titleLabel?.font = .systemFont(ofSize: 14, weight: .medium)
|
||
button.addTarget(self, action: action, for: .touchUpInside)
|
||
return button
|
||
}
|
||
|
||
@objc private func cancelTapped() {
|
||
dismissOverlay()
|
||
}
|
||
|
||
@objc private func confirmTapped() {
|
||
let selectedID = selectedID
|
||
dismissOverlay { [onConfirm] in onConfirm(selectedID) }
|
||
}
|
||
}
|