新增ai修图
This commit is contained in:
@ -11,6 +11,9 @@ import UIKit
|
||||
final class TravelAlbumDetailViewController: BaseViewController {
|
||||
private let viewModel: TravelAlbumDetailViewModel
|
||||
private let api: any TravelAlbumServing
|
||||
private let aiEditResultStore = TravelAlbumAIEditResultStore.shared
|
||||
private var pendingAIEditMaterialIDs: Set<Int> = []
|
||||
private var failedAIEditCount = 0
|
||||
|
||||
private let scrollContainer = UIView()
|
||||
private let infoCard = TravelAlbumInfoCard()
|
||||
@ -24,9 +27,9 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
private var dataSource: UICollectionViewDiffableDataSource<Int, TravelAlbumMaterial>!
|
||||
private let bottomBar = UIView()
|
||||
private let bottomActionStack = UIStackView()
|
||||
private let aiEditSelectedButton = UIButton(type: .system)
|
||||
private let deleteSelectedButton = UIButton(type: .system)
|
||||
private let uploadButton = UIButton(type: .system)
|
||||
private var isDeleteSelectedButtonVisible = false
|
||||
|
||||
init(albumId: Int, api: (any TravelAlbumServing)? = nil) {
|
||||
viewModel = TravelAlbumDetailViewModel(albumId: albumId)
|
||||
@ -41,6 +44,7 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
|
||||
override func setupNavigationBar() {
|
||||
title = "相册管理"
|
||||
navigationItem.backButtonDisplayMode = .minimal
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(
|
||||
image: UIImage(systemName: "ellipsis"),
|
||||
menu: UIMenu(children: [
|
||||
@ -78,21 +82,39 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
withReuseIdentifier: TravelAlbumMaterialCell.reuseIdentifier,
|
||||
for: indexPath
|
||||
) as! TravelAlbumMaterialCell
|
||||
let aiEditState = self?.aiEditResultStore.state(for: material.id)
|
||||
?? TravelAlbumAIEditResultState()
|
||||
let resultImage = aiEditState.isCover
|
||||
? (self?.aiEditResultStore.coverImage(for: material.id)
|
||||
?? self?.aiEditResultStore.editedImage(for: material.id))
|
||||
: self?.aiEditResultStore.editedImage(for: material.id)
|
||||
cell.apply(
|
||||
material: material,
|
||||
selectionMode: self?.viewModel.isSelectionMode == true,
|
||||
selected: self?.viewModel.selectedMaterialIds.contains(material.id) == true
|
||||
selected: self?.viewModel.selectedMaterialIds.contains(material.id) == true,
|
||||
aiEditState: aiEditState,
|
||||
editedImage: resultImage
|
||||
)
|
||||
return cell
|
||||
}
|
||||
|
||||
bottomBar.backgroundColor = AppColor.pageBackground
|
||||
bottomActionStack.axis = .vertical
|
||||
bottomActionStack.spacing = 8
|
||||
configureBottomButton(deleteSelectedButton, title: "删除选中(0)", color: UIColor(hex: 0xE53935))
|
||||
bottomActionStack.axis = .horizontal
|
||||
bottomActionStack.spacing = 12
|
||||
bottomActionStack.distribution = .fillEqually
|
||||
configureSelectionActionButton(
|
||||
aiEditSelectedButton,
|
||||
title: "AI修图",
|
||||
image: UIImage(named: "travel_album_preview_ai_edit")?.withRenderingMode(.alwaysTemplate)
|
||||
)
|
||||
configureSelectionActionButton(
|
||||
deleteSelectedButton,
|
||||
title: "删除",
|
||||
image: UIImage(named: "travel_album_preview_delete")?.withRenderingMode(.alwaysTemplate)
|
||||
)
|
||||
configureBottomButton(uploadButton, title: "上传照片", color: AppColor.primary)
|
||||
aiEditSelectedButton.isHidden = true
|
||||
deleteSelectedButton.isHidden = true
|
||||
deleteSelectedButton.alpha = 0
|
||||
|
||||
view.addSubview(scrollContainer)
|
||||
scrollContainer.addSubview(infoCard)
|
||||
@ -105,6 +127,7 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
manageCard.addSubview(collectionView)
|
||||
view.addSubview(bottomBar)
|
||||
bottomBar.addSubview(bottomActionStack)
|
||||
bottomActionStack.addArrangedSubview(aiEditSelectedButton)
|
||||
bottomActionStack.addArrangedSubview(deleteSelectedButton)
|
||||
bottomActionStack.addArrangedSubview(uploadButton)
|
||||
}
|
||||
@ -118,11 +141,10 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
make.leading.trailing.equalToSuperview().inset(20)
|
||||
make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-12)
|
||||
}
|
||||
deleteSelectedButton.snp.makeConstraints { make in
|
||||
make.height.equalTo(48).priority(.high)
|
||||
}
|
||||
uploadButton.snp.makeConstraints { make in
|
||||
make.height.equalTo(48).priority(.high)
|
||||
[aiEditSelectedButton, deleteSelectedButton, uploadButton].forEach { button in
|
||||
button.snp.makeConstraints { make in
|
||||
make.height.equalTo(58).priority(.high)
|
||||
}
|
||||
}
|
||||
scrollContainer.snp.makeConstraints { make in
|
||||
make.top.equalTo(view.safeAreaLayoutGuide).offset(12)
|
||||
@ -163,8 +185,15 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
purchasedTabButton.addTarget(self, action: #selector(purchasedTabTapped), for: .touchUpInside)
|
||||
sortButton.addTarget(self, action: #selector(sortTapped), for: .touchUpInside)
|
||||
selectButton.addTarget(self, action: #selector(selectTapped), for: .touchUpInside)
|
||||
aiEditSelectedButton.addTarget(self, action: #selector(aiEditSelectedTapped), for: .touchUpInside)
|
||||
deleteSelectedButton.addTarget(self, action: #selector(deleteSelectedTapped), for: .touchUpInside)
|
||||
uploadButton.addTarget(self, action: #selector(uploadTapped), for: .touchUpInside)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(aiEditResultDidChange(_:)),
|
||||
name: TravelAlbumAIEditResultStore.didChangeNotification,
|
||||
object: aiEditResultStore
|
||||
)
|
||||
viewModel.onStateChange = { [weak self] in
|
||||
Task { @MainActor in self?.applyViewModel() }
|
||||
}
|
||||
@ -195,8 +224,7 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
selectButton.isHidden = viewModel.selectedTab != .all
|
||||
selectButton.setImage(UIImage(systemName: viewModel.isSelectionMode ? "checkmark.circle.fill" : "circle"), for: .normal)
|
||||
selectButton.tintColor = viewModel.isSelectionMode ? AppColor.primary : AppColor.textTertiary
|
||||
deleteSelectedButton.setTitle("删除选中(\(viewModel.selectedMaterialIds.count))", for: .normal)
|
||||
setDeleteSelectedButtonVisible(viewModel.isSelectionMode && !viewModel.selectedMaterialIds.isEmpty)
|
||||
updateBottomToolbar()
|
||||
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Int, TravelAlbumMaterial>()
|
||||
snapshot.appendSections([0])
|
||||
@ -213,6 +241,28 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据选择数量和修图任务刷新底部操作栏。
|
||||
private func updateBottomToolbar() {
|
||||
let toolbarState = TravelAlbumSelectionToolbarState(
|
||||
isSelectionMode: viewModel.isSelectionMode,
|
||||
selectedCount: viewModel.selectedMaterialIds.count
|
||||
)
|
||||
uploadButton.isHidden = !toolbarState.showsUpload
|
||||
aiEditSelectedButton.isHidden = !toolbarState.showsSelectionActions
|
||||
deleteSelectedButton.isHidden = !toolbarState.showsSelectionActions
|
||||
let selectedContainsProcessingPhoto = viewModel.selectedMaterialIds.contains { materialID in
|
||||
aiEditResultStore.state(for: materialID).isProcessing
|
||||
}
|
||||
let canStartAIEditing = toolbarState.selectionActionsEnabled
|
||||
&& pendingAIEditMaterialIDs.isEmpty
|
||||
&& !selectedContainsProcessingPhoto
|
||||
aiEditSelectedButton.isEnabled = canStartAIEditing
|
||||
deleteSelectedButton.isEnabled = toolbarState.selectionActionsEnabled
|
||||
aiEditSelectedButton.alpha = canStartAIEditing ? 1 : 0.4
|
||||
deleteSelectedButton.alpha = toolbarState.selectionActionsEnabled ? 1 : 0.4
|
||||
bottomBar.backgroundColor = toolbarState.showsSelectionActions ? .white : AppColor.pageBackground
|
||||
}
|
||||
|
||||
private func configurePillButton(_ button: UIButton) {
|
||||
button.titleLabel?.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
button.layer.cornerRadius = 16
|
||||
@ -242,27 +292,20 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
button.layer.cornerRadius = 10
|
||||
}
|
||||
|
||||
private func setDeleteSelectedButtonVisible(_ visible: Bool) {
|
||||
guard visible != isDeleteSelectedButtonVisible else { return }
|
||||
isDeleteSelectedButtonVisible = visible
|
||||
|
||||
let changes = {
|
||||
self.deleteSelectedButton.isHidden = !visible
|
||||
self.deleteSelectedButton.alpha = visible ? 1 : 0
|
||||
self.view.layoutIfNeeded()
|
||||
/// 配置多选模式底部的图标操作按钮,图标复用照片预览页的 Iconfont 资源。
|
||||
private func configureSelectionActionButton(_ button: UIButton, title: String, image: UIImage?) {
|
||||
var configuration = UIButton.Configuration.plain()
|
||||
configuration.title = title
|
||||
configuration.image = image
|
||||
configuration.imagePlacement = .top
|
||||
configuration.imagePadding = 5
|
||||
configuration.baseForegroundColor = AppColor.textSecondary
|
||||
configuration.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { attributes in
|
||||
var attributes = attributes
|
||||
attributes.font = .systemFont(ofSize: 13, weight: .medium)
|
||||
return attributes
|
||||
}
|
||||
guard view.window != nil else {
|
||||
changes()
|
||||
return
|
||||
}
|
||||
|
||||
view.layoutIfNeeded()
|
||||
UIView.animate(
|
||||
withDuration: 0.25,
|
||||
delay: 0,
|
||||
options: [.curveEaseInOut, .beginFromCurrentState],
|
||||
animations: changes
|
||||
)
|
||||
button.configuration = configuration
|
||||
}
|
||||
|
||||
private func makeLayout() -> UICollectionViewCompositionalLayout {
|
||||
@ -319,6 +362,157 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
viewModel.toggleSelectionMode()
|
||||
}
|
||||
|
||||
@objc private func aiEditSelectedTapped() {
|
||||
let selectedMaterials = viewModel.materials.filter { viewModel.selectedMaterialIds.contains($0.id) }
|
||||
guard let firstMaterial = selectedMaterials.first else {
|
||||
showToast("请选择要修图的照片")
|
||||
return
|
||||
}
|
||||
let imageURLString = firstMaterial.coverUrl.isEmpty ? firstMaterial.fileUrl : firstMaterial.coverUrl
|
||||
guard let imageURL = URL(string: imageURLString), !imageURLString.isEmpty else {
|
||||
presentAIEditSheet(sourceImage: nil, selectedMaterials: selectedMaterials)
|
||||
return
|
||||
}
|
||||
KingfisherManager.shared.retrieveImage(with: imageURL) { [weak self] result in
|
||||
guard let self else { return }
|
||||
Task { @MainActor in
|
||||
let sourceImage: UIImage?
|
||||
if case let .success(value) = result {
|
||||
sourceImage = value.image
|
||||
} else {
|
||||
sourceImage = nil
|
||||
}
|
||||
self.presentAIEditSheet(sourceImage: sourceImage, selectedMaterials: selectedMaterials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 复用单张照片预览页的 AI 修图预设弹窗完成批量选择演示。
|
||||
private func presentAIEditSheet(
|
||||
sourceImage: UIImage?,
|
||||
selectedMaterials: [TravelAlbumMaterial]
|
||||
) {
|
||||
let controller = TravelAlbumAIEditSheetViewController(
|
||||
sourceImage: sourceImage,
|
||||
selectedPhotoCount: selectedMaterials.count
|
||||
)
|
||||
controller.onConfirm = { [weak self] selection in
|
||||
self?.startBatchAIEditing(
|
||||
materials: selectedMaterials,
|
||||
selection: selection
|
||||
)
|
||||
}
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
/// 启动批量本地演示修图,并在全部照片完成前禁用 AI 修图入口。
|
||||
private func startBatchAIEditing(
|
||||
materials: [TravelAlbumMaterial],
|
||||
selection: TravelAlbumAIEditSelection
|
||||
) {
|
||||
guard pendingAIEditMaterialIDs.isEmpty, !materials.isEmpty else { return }
|
||||
let materialIDs = materials.map(\.id)
|
||||
let coverMaterialID = selection.coverTemplate == nil ? nil : materials.first?.id
|
||||
pendingAIEditMaterialIDs = Set(materialIDs)
|
||||
failedAIEditCount = 0
|
||||
aiEditResultStore.startProcessing(materialIDs: materialIDs)
|
||||
applyViewModel()
|
||||
|
||||
materials.forEach { material in
|
||||
let imageURLString = material.fileUrl.isEmpty ? material.coverUrl : material.fileUrl
|
||||
guard let imageURL = URL(string: imageURLString), !imageURLString.isEmpty else {
|
||||
finishAIEditing(
|
||||
materialID: material.id,
|
||||
selection: selection,
|
||||
appliedCoverTemplate: material.id == coverMaterialID ? selection.coverTemplate : nil,
|
||||
sourceImage: nil
|
||||
)
|
||||
return
|
||||
}
|
||||
KingfisherManager.shared.retrieveImage(with: imageURL) { [weak self] result in
|
||||
guard let self else { return }
|
||||
Task { @MainActor in
|
||||
let sourceImage: UIImage?
|
||||
if case let .success(value) = result {
|
||||
sourceImage = value.image
|
||||
} else {
|
||||
sourceImage = nil
|
||||
}
|
||||
self.finishAIEditing(
|
||||
materialID: material.id,
|
||||
selection: selection,
|
||||
appliedCoverTemplate: material.id == coverMaterialID ? selection.coverTemplate : nil,
|
||||
sourceImage: sourceImage
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 完成单张批量任务;最后一张结束后恢复入口并反馈整体结果。
|
||||
private func finishAIEditing(
|
||||
materialID: Int,
|
||||
selection: TravelAlbumAIEditSelection,
|
||||
appliedCoverTemplate: TravelAlbumCoverTemplate?,
|
||||
sourceImage: UIImage?
|
||||
) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { [weak self] in
|
||||
guard let self else { return }
|
||||
if let sourceImage {
|
||||
let refinedImage = TravelAlbumAIEditImageProcessor.render(
|
||||
effect: selection.refinedPreset.effect,
|
||||
source: sourceImage
|
||||
)
|
||||
let atmosphereImage = selection.atmosphereOption.map { option in
|
||||
TravelAlbumAIEditImageProcessor.render(effect: option.effect, source: sourceImage)
|
||||
}
|
||||
let coverImage = appliedCoverTemplate.map { template in
|
||||
TravelAlbumCoverTemplateImageProcessor.render(
|
||||
template: template,
|
||||
source: refinedImage
|
||||
)
|
||||
}
|
||||
if selection.refinedPreset.effect == .original,
|
||||
selection.atmosphereOption == nil,
|
||||
appliedCoverTemplate == nil {
|
||||
self.aiEditResultStore.restoreOriginal(materialID: materialID)
|
||||
} else {
|
||||
self.aiEditResultStore.complete(
|
||||
materialID: materialID,
|
||||
presetID: selection.refinedPreset.id,
|
||||
atmosphereOptionID: selection.atmosphereOption?.id,
|
||||
coverTemplateID: appliedCoverTemplate?.id,
|
||||
editedImage: refinedImage,
|
||||
atmosphereImage: atmosphereImage,
|
||||
coverImage: coverImage
|
||||
)
|
||||
}
|
||||
} else {
|
||||
self.aiEditResultStore.failProcessing(materialID: materialID)
|
||||
self.failedAIEditCount += 1
|
||||
}
|
||||
self.pendingAIEditMaterialIDs.remove(materialID)
|
||||
self.applyViewModel()
|
||||
if self.pendingAIEditMaterialIDs.isEmpty {
|
||||
let successMessage: String
|
||||
if let coverTemplate = selection.coverTemplate,
|
||||
selection.atmosphereOption != nil {
|
||||
successMessage = "AI修图完成,每张生成 2 个结果,并生成「\(coverTemplate.title)」封面"
|
||||
} else if let coverTemplate = selection.coverTemplate {
|
||||
successMessage = "AI修图完成,已生成「\(coverTemplate.title)」封面"
|
||||
} else if selection.atmosphereOption != nil {
|
||||
successMessage = "AI修图完成,每张已生成 2 个结果"
|
||||
} else {
|
||||
successMessage = "AI修图完成"
|
||||
}
|
||||
let message = self.failedAIEditCount == 0
|
||||
? successMessage
|
||||
: "有 \(self.failedAIEditCount) 张照片修图失败,请重试"
|
||||
self.showToast(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func deleteSelectedTapped() {
|
||||
let count = viewModel.selectedMaterialIds.count
|
||||
let alert = UIAlertController(title: "删除素材", message: "确定删除选中的 \(count) 张素材吗?", preferredStyle: .alert)
|
||||
@ -331,22 +525,60 @@ final class TravelAlbumDetailViewController: BaseViewController {
|
||||
}
|
||||
|
||||
@objc private func uploadTapped() {
|
||||
let controller = TravelAlbumTransferModeSheetViewController()
|
||||
controller.onModeSelected = { [weak self] mode in
|
||||
self?.openWiredTransfer(mode: mode)
|
||||
}
|
||||
present(controller, animated: true)
|
||||
}
|
||||
|
||||
/// 根据本次选择进入对应传输模式,避免被历史缓存模式覆盖。
|
||||
private func openWiredTransfer(mode: TravelAlbumOTGTransferMode) {
|
||||
let album = viewModel.album
|
||||
let controller = WiredCameraTransferViewController(
|
||||
viewModel: WiredCameraTransferViewModel(
|
||||
albumId: album?.id ?? viewModel.albumId,
|
||||
albumTitle: album?.name ?? "",
|
||||
headerPhone: album?.displayPhone ?? ""
|
||||
headerPhone: album?.displayPhone ?? "",
|
||||
initialTransferMode: mode
|
||||
)
|
||||
)
|
||||
navigationController?.pushViewController(controller, animated: true)
|
||||
}
|
||||
|
||||
/// 仅刷新状态发生变化的缩略图,及时呈现修图结果和任务角标。
|
||||
@objc private func aiEditResultDidChange(_ notification: Notification) {
|
||||
guard let materialID = notification.userInfo?[TravelAlbumAIEditResultStore.materialIDUserInfoKey] as? Int,
|
||||
let material = dataSource.snapshot().itemIdentifiers.first(where: { $0.id == materialID }) else {
|
||||
return
|
||||
}
|
||||
var snapshot = dataSource.snapshot()
|
||||
snapshot.reconfigureItems([material])
|
||||
dataSource.apply(snapshot, animatingDifferences: true)
|
||||
updateBottomToolbar()
|
||||
}
|
||||
}
|
||||
|
||||
extension TravelAlbumDetailViewController: UICollectionViewDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
guard let material = dataSource.itemIdentifier(for: indexPath) else { return }
|
||||
viewModel.toggleMaterialSelection(material)
|
||||
if viewModel.isSelectionMode {
|
||||
viewModel.toggleMaterialSelection(material)
|
||||
} else {
|
||||
// 非多选状态进入预览,避免影响原有批量删除流程。
|
||||
let initialIndex = viewModel.materials.firstIndex(where: { $0.id == material.id }) ?? 0
|
||||
let controller = TravelAlbumPhotoPreviewViewController(
|
||||
materials: viewModel.materials,
|
||||
initialIndex: initialIndex,
|
||||
api: api
|
||||
)
|
||||
controller.onDeleted = { [weak self] in
|
||||
guard let self else { return }
|
||||
Task { await self.viewModel.refreshAll(api: self.api) }
|
||||
}
|
||||
// 照片与修图样式、封面模板统一走全屏预览,避免系统导航栏重复出现。
|
||||
present(controller, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
@ -499,19 +731,70 @@ private final class TravelAlbumMaterialCell: UICollectionViewCell {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func apply(material: TravelAlbumMaterial, selectionMode: Bool, selected: Bool) {
|
||||
let urlString = material.coverUrl.isEmpty ? material.fileUrl : material.coverUrl
|
||||
if let url = URL(string: urlString), !urlString.isEmpty {
|
||||
imageView.kf.setImage(with: url, placeholder: UIImage(systemName: "photo"))
|
||||
override func prepareForReuse() {
|
||||
super.prepareForReuse()
|
||||
imageView.kf.cancelDownloadTask()
|
||||
imageView.image = nil
|
||||
}
|
||||
|
||||
func apply(
|
||||
material: TravelAlbumMaterial,
|
||||
selectionMode: Bool,
|
||||
selected: Bool,
|
||||
aiEditState: TravelAlbumAIEditResultState,
|
||||
editedImage: UIImage?
|
||||
) {
|
||||
imageView.backgroundColor = .clear
|
||||
imageView.tintColor = nil
|
||||
if aiEditState.hasEditedResult, let editedImage {
|
||||
imageView.kf.cancelDownloadTask()
|
||||
imageView.image = editedImage
|
||||
} else if let assetName = material.bundledImageAssetName,
|
||||
let bundledImage = UIImage(named: assetName) {
|
||||
imageView.kf.cancelDownloadTask()
|
||||
imageView.image = bundledImage
|
||||
} else {
|
||||
imageView.image = UIImage(systemName: "photo")
|
||||
imageView.tintColor = AppColor.primary
|
||||
imageView.backgroundColor = UIColor(hex: 0xEAF2FF)
|
||||
let urlString = material.coverUrl.isEmpty ? material.fileUrl : material.coverUrl
|
||||
if let url = URL(string: urlString), !urlString.isEmpty {
|
||||
imageView.kf.setImage(with: url, placeholder: UIImage(systemName: "photo"))
|
||||
} else {
|
||||
imageView.image = UIImage(systemName: "photo")
|
||||
imageView.tintColor = AppColor.primary
|
||||
imageView.backgroundColor = UIColor(hex: 0xEAF2FF)
|
||||
}
|
||||
}
|
||||
applyAIEditStatus(aiEditState.thumbnailStatus, isPurchased: material.isPurchased)
|
||||
checkImageView.isHidden = !selectionMode
|
||||
checkImageView.image = UIImage(systemName: selected ? "checkmark.circle.fill" : "circle")
|
||||
checkImageView.tintColor = selected ? AppColor.primary : .white
|
||||
nameLabel.text = material.fileName
|
||||
sizeLabel.text = TravelAlbumDisplayFormatter.fileSizeText(material.fileSize)
|
||||
}
|
||||
|
||||
/// 复用左上角状态位,让用户无需进入预览即可识别照片是否完成 AI 修图。
|
||||
private func applyAIEditStatus(
|
||||
_ status: TravelAlbumAIEditResultState.ThumbnailStatus,
|
||||
isPurchased: Bool
|
||||
) {
|
||||
let appearance: (title: String, color: UIColor, width: CGFloat)
|
||||
switch status {
|
||||
case .none:
|
||||
appearance = isPurchased
|
||||
? ("已购", AppColor.primary, 48)
|
||||
: ("已上传", UIColor(hex: 0x34C759), 48)
|
||||
case .processing:
|
||||
appearance = ("修图中", AppColor.primary, 48)
|
||||
case .edited:
|
||||
appearance = ("AI已修", UIColor(hex: 0x6750A4), 48)
|
||||
case .cover:
|
||||
appearance = ("AI封面", UIColor(hex: 0xF59E0B), 56)
|
||||
case .failed:
|
||||
appearance = ("修图失败", UIColor(hex: 0xE53935), 56)
|
||||
}
|
||||
statusLabel.text = appearance.title
|
||||
statusLabel.backgroundColor = appearance.color
|
||||
statusLabel.snp.updateConstraints { make in
|
||||
make.width.equalTo(appearance.width)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user