223 lines
8.7 KiB
Swift
223 lines
8.7 KiB
Swift
//
|
|
// CloudDriveFileCell.swift
|
|
// suixinkan
|
|
//
|
|
|
|
import Kingfisher
|
|
import SnapKit
|
|
import UIKit
|
|
|
|
/// 云盘文件 Cell,严格对齐 Android 宫格与列表两种样式。
|
|
final class CloudDriveFileCell: UICollectionViewCell {
|
|
static let reuseIdentifier = "CloudDriveFileCell"
|
|
|
|
/// 点击文件操作入口时回调。
|
|
var onDetails: (() -> Void)?
|
|
|
|
private let thumbnailView = UIImageView()
|
|
private let folderIconView = UIImageView(
|
|
image: CloudDriveAsset.image(named: "icon_cloud_storage_folder_type", fallbackSystemName: "folder.fill")
|
|
)
|
|
private let playIconView = UIImageView(
|
|
image: CloudDriveAsset.image(named: "icon_cloud_storage_file_play", fallbackSystemName: "play.circle.fill")
|
|
)
|
|
private let extensionLabel = CloudDrivePaddingLabel()
|
|
private let nameLabel = UILabel()
|
|
private let detailLabel = UILabel()
|
|
private let moreButton = UIButton(type: .system)
|
|
|
|
private var gridConstraints: [Constraint] = []
|
|
private var listConstraints: [Constraint] = []
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
setupUI()
|
|
setupConstraints()
|
|
}
|
|
|
|
@available(*, unavailable)
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
override func prepareForReuse() {
|
|
super.prepareForReuse()
|
|
thumbnailView.kf.cancelDownloadTask()
|
|
thumbnailView.image = nil
|
|
folderIconView.isHidden = true
|
|
playIconView.isHidden = true
|
|
extensionLabel.isHidden = true
|
|
onDetails = nil
|
|
}
|
|
|
|
/// 应用云盘文件数据。
|
|
func apply(file: CloudFile, mode: CloudDriveDisplayMode) {
|
|
let isGrid = mode == .grid
|
|
gridConstraints.forEach { isGrid ? $0.activate() : $0.deactivate() }
|
|
listConstraints.forEach { isGrid ? $0.deactivate() : $0.activate() }
|
|
|
|
nameLabel.text = file.name
|
|
nameLabel.font = .systemFont(ofSize: 15, weight: isGrid ? .semibold : .regular)
|
|
nameLabel.lineBreakMode = isGrid ? .byTruncatingTail : .byTruncatingMiddle
|
|
detailLabel.text = isGrid ? CloudDriveFormatter.typeName(file.type) : CloudDriveFormatter.fileDescription(file)
|
|
detailLabel.textColor = isGrid ? UIColor(hex: 0x4B5563) : UIColor(hex: 0x7B8EAA)
|
|
|
|
contentView.layer.cornerRadius = isGrid ? 12 : 0
|
|
contentView.layer.borderWidth = isGrid ? 1 : 0
|
|
contentView.backgroundColor = isGrid ? .white : .clear
|
|
thumbnailView.layer.cornerRadius = isGrid ? 12 : 0
|
|
thumbnailView.clipsToBounds = isGrid
|
|
|
|
configureThumbnail(file, mode: mode)
|
|
}
|
|
|
|
private func setupUI() {
|
|
backgroundColor = .clear
|
|
contentView.clipsToBounds = true
|
|
contentView.layer.borderColor = UIColor(hex: 0xF4F4F4).cgColor
|
|
|
|
thumbnailView.contentMode = .scaleAspectFill
|
|
thumbnailView.clipsToBounds = true
|
|
|
|
folderIconView.contentMode = .scaleAspectFit
|
|
folderIconView.isHidden = true
|
|
|
|
playIconView.contentMode = .scaleAspectFit
|
|
playIconView.isHidden = true
|
|
|
|
extensionLabel.font = .systemFont(ofSize: 12, weight: .bold)
|
|
extensionLabel.textColor = .white
|
|
extensionLabel.backgroundColor = UIColor.black.withAlphaComponent(0.5)
|
|
extensionLabel.layer.cornerRadius = 4
|
|
extensionLabel.clipsToBounds = true
|
|
extensionLabel.isHidden = true
|
|
|
|
nameLabel.textColor = UIColor(hex: 0x333333)
|
|
nameLabel.numberOfLines = 1
|
|
|
|
detailLabel.font = .systemFont(ofSize: 12)
|
|
detailLabel.numberOfLines = 1
|
|
|
|
let detailsImage = CloudDriveAsset.resizedImage(
|
|
named: "icon_cloud_storage_file_item_details",
|
|
fallbackSystemName: "ellipsis",
|
|
size: CGSize(width: 16, height: 16)
|
|
)
|
|
moreButton.setImage(detailsImage, for: .normal)
|
|
moreButton.imageView?.contentMode = .scaleAspectFit
|
|
moreButton.addTarget(self, action: #selector(moreTapped), for: .touchUpInside)
|
|
moreButton.accessibilityLabel = "文件操作"
|
|
|
|
[thumbnailView, folderIconView, playIconView, extensionLabel, nameLabel, detailLabel, moreButton]
|
|
.forEach(contentView.addSubview)
|
|
}
|
|
|
|
private func setupConstraints() {
|
|
thumbnailView.snp.makeConstraints { make in
|
|
gridConstraints.append(make.top.leading.trailing.equalToSuperview().constraint)
|
|
gridConstraints.append(make.height.equalTo(thumbnailView.snp.width).dividedBy(1.77).constraint)
|
|
listConstraints.append(make.leading.centerY.equalToSuperview().constraint)
|
|
listConstraints.append(make.width.height.equalTo(48).constraint)
|
|
}
|
|
folderIconView.snp.makeConstraints { make in
|
|
make.center.equalTo(thumbnailView)
|
|
make.width.height.equalTo(48)
|
|
}
|
|
playIconView.snp.makeConstraints { make in
|
|
make.center.equalTo(thumbnailView)
|
|
make.width.height.equalTo(24)
|
|
}
|
|
extensionLabel.snp.makeConstraints { make in
|
|
make.top.leading.equalTo(thumbnailView).offset(8)
|
|
}
|
|
nameLabel.snp.makeConstraints { make in
|
|
gridConstraints.append(make.top.equalTo(thumbnailView.snp.bottom).offset(8).constraint)
|
|
gridConstraints.append(make.leading.equalToSuperview().offset(12).constraint)
|
|
listConstraints.append(make.top.equalToSuperview().offset(12).constraint)
|
|
listConstraints.append(make.leading.equalTo(thumbnailView.snp.trailing).offset(12).constraint)
|
|
make.trailing.equalTo(moreButton.snp.leading).offset(-4)
|
|
}
|
|
detailLabel.snp.makeConstraints { make in
|
|
make.top.equalTo(nameLabel.snp.bottom).offset(2)
|
|
make.leading.trailing.equalTo(nameLabel)
|
|
}
|
|
moreButton.snp.makeConstraints { make in
|
|
gridConstraints.append(make.trailing.equalToSuperview().inset(10).constraint)
|
|
listConstraints.append(make.trailing.equalToSuperview().constraint)
|
|
make.centerY.equalTo(nameLabel.snp.bottom).offset(4)
|
|
make.width.height.equalTo(28)
|
|
}
|
|
listConstraints.forEach { $0.deactivate() }
|
|
}
|
|
|
|
private func configureThumbnail(_ file: CloudFile, mode: CloudDriveDisplayMode) {
|
|
thumbnailView.kf.cancelDownloadTask()
|
|
folderIconView.isHidden = true
|
|
playIconView.isHidden = true
|
|
extensionLabel.isHidden = true
|
|
|
|
if mode == .list {
|
|
thumbnailView.backgroundColor = .clear
|
|
thumbnailView.contentMode = .scaleAspectFit
|
|
let assetName: String
|
|
let fallbackName: String
|
|
if file.isFolder {
|
|
assetName = "icon_cloud_storage_file_type_folder"
|
|
fallbackName = "folder.fill"
|
|
} else if file.isVideo {
|
|
assetName = "icon_cloud_storage_file_type_video"
|
|
fallbackName = "video.fill"
|
|
} else {
|
|
assetName = "icon_cloud_storage_file_type_photo"
|
|
fallbackName = "photo.fill"
|
|
}
|
|
thumbnailView.image = CloudDriveAsset.image(named: assetName, fallbackSystemName: fallbackName)
|
|
return
|
|
}
|
|
|
|
thumbnailView.contentMode = .scaleAspectFill
|
|
if file.isFolder {
|
|
thumbnailView.image = nil
|
|
thumbnailView.backgroundColor = UIColor(hex: 0xFFF0E2)
|
|
folderIconView.isHidden = false
|
|
return
|
|
}
|
|
|
|
thumbnailView.backgroundColor = UIColor(hex: 0xF4F4F4)
|
|
let previewURL = file.isVideo ? file.coverUrl : file.fileUrl
|
|
let placeholder = CloudDriveAsset.image(
|
|
named: file.isVideo ? "icon_cloud_storage_file_type_video" : "icon_cloud_storage_file_type_photo",
|
|
fallbackSystemName: file.isVideo ? "video" : "photo"
|
|
)
|
|
if let url = URL(string: previewURL), !previewURL.isEmpty {
|
|
thumbnailView.kf.setImage(with: url, placeholder: placeholder)
|
|
} else {
|
|
thumbnailView.image = placeholder
|
|
}
|
|
playIconView.isHidden = !file.isVideo
|
|
extensionLabel.text = CloudDriveFormatter.fileExtension(file.fileUrl.isEmpty ? file.name : file.fileUrl)
|
|
extensionLabel.isHidden = extensionLabel.text?.isEmpty ?? true
|
|
}
|
|
|
|
@objc private func moreTapped() {
|
|
onDetails?()
|
|
}
|
|
}
|
|
|
|
/// 云盘文件扩展名标签,提供 Android 对齐的四边内边距。
|
|
private final class CloudDrivePaddingLabel: UILabel {
|
|
private let insets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
|
|
|
|
override func drawText(in rect: CGRect) {
|
|
super.drawText(in: rect.inset(by: insets))
|
|
}
|
|
|
|
override var intrinsicContentSize: CGSize {
|
|
let size = super.intrinsicContentSize
|
|
return CGSize(
|
|
width: size.width + insets.left + insets.right,
|
|
height: size.height + insets.top + insets.bottom
|
|
)
|
|
}
|
|
}
|