feat: 优化 AI 修图模板与全屏预览

This commit is contained in:
2026-08-13 13:48:11 +08:00
parent 60502784db
commit f2a005e008
22 changed files with 410 additions and 116 deletions
@@ -39,7 +39,7 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
private let validationLabel = UILabel()
private let actionStack = UIStackView()
private let cancelButton = UIButton(type: .system)
private let confirmButton = UIButton(type: .system)
private let confirmButton = AIRetouchGradientButton(type: .system)
/// 创建 AI 修图模板选择 Sheet。
init(
@@ -110,7 +110,7 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
footerStack.alignment = .fill
actionStack.axis = .horizontal
actionStack.spacing = 12
actionStack.distribution = .fillEqually
actionStack.distribution = .fill
actionStack.alignment = .fill
configureCancelButton()
configureConfirmButton()
@@ -145,6 +145,9 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
actionStack.snp.makeConstraints { make in
make.height.equalTo(56)
}
cancelButton.snp.makeConstraints { make in
make.width.equalTo(confirmButton.snp.width).multipliedBy(0.78)
}
collectionView.snp.makeConstraints { make in
make.top.equalTo(view.safeAreaLayoutGuide)
make.leading.trailing.equalToSuperview()
@@ -202,37 +205,25 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
}
private func configureCancelButton() {
var configuration = UIButton.Configuration.filled()
configuration.title = "取消"
configuration.baseBackgroundColor = .white
configuration.baseForegroundColor = AIRetouchTemplateStyle.textPrimary
configuration.background.cornerRadius = 12
configuration.background.strokeColor = UIColor(hex: 0xD1D1D6)
configuration.background.strokeWidth = 1
configuration.titleTextAttributesTransformer = buttonTitleTransformer
cancelButton.configuration = configuration
cancelButton.setTitle("取消", for: .normal)
cancelButton.setTitleColor(AIRetouchTemplateStyle.primary, for: .normal)
cancelButton.setTitleColor(AIRetouchTemplateStyle.primary.withAlphaComponent(0.45), for: .disabled)
cancelButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
cancelButton.backgroundColor = AIRetouchTemplateStyle.cardBackground
cancelButton.layer.cornerRadius = 12
cancelButton.layer.borderColor = AIRetouchTemplateStyle.primary.cgColor
cancelButton.layer.borderWidth = 1
cancelButton.accessibilityIdentifier = "travelAlbum.aiRetouchCancelButton"
}
private func configureConfirmButton() {
var configuration = UIButton.Configuration.filled()
configuration.title = "确定"
configuration.baseBackgroundColor = AIRetouchTemplateStyle.primary
configuration.baseForegroundColor = .white
configuration.background.cornerRadius = 12
configuration.titleTextAttributesTransformer = buttonTitleTransformer
confirmButton.configuration = configuration
confirmButton.setTitle("确定", for: .normal)
confirmButton.setTitleColor(.white, for: .normal)
confirmButton.setTitleColor(.white, for: .disabled)
confirmButton.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
confirmButton.accessibilityIdentifier = "travelAlbum.aiRetouchConfirmButton"
}
private var buttonTitleTransformer: UIConfigurationTextAttributesTransformer {
UIConfigurationTextAttributesTransformer { attributes in
var attributes = attributes
attributes.font = .systemFont(ofSize: 16, weight: .semibold)
return attributes
}
}
private func configureDataSource() {
dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) {
[weak self] collectionView, indexPath, item in
@@ -267,6 +258,7 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
for: indexPath
) as! TravelAlbumAIRetouchModeCell
cell.apply(
requiredQuota: self.viewModel.requiredQuota,
remainingQuota: self.viewModel.remainingQuota,
insufficient: self.viewModel.remainingQuota.map {
self.viewModel.requiredQuota > $0
@@ -293,10 +285,10 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
title: category.title,
badge: category == .cover
? .gift
: (self.viewModel.isOptional(category) ? .optional : nil)
: (self.viewModel.isOptional(category) ? .optional : .required)
)
case .mode:
header.apply(title: "修图模式", badge: nil)
return nil
case .overview:
return nil
}
@@ -305,7 +297,7 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
}
private func makeLayout() -> UICollectionViewCompositionalLayout {
UICollectionViewCompositionalLayout { [weak self] sectionIndex, _ in
UICollectionViewCompositionalLayout { [weak self] sectionIndex, layoutEnvironment in
guard let self,
self.dataSource != nil,
self.dataSource.snapshot().sectionIdentifiers.indices.contains(sectionIndex) else {
@@ -315,25 +307,27 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
case .overview:
let size = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(self.viewModel.shouldShowInitialTips ? 160 : 74)
heightDimension: .absolute(self.viewModel.shouldShowInitialTips ? 150 : 82)
)
let item = NSCollectionLayoutItem(layoutSize: size)
let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item])
return NSCollectionLayoutSection(group: group)
case .templates:
let availableWidth = layoutEnvironment.container.effectiveContentSize.width
let cardWidth = min(118, floor((availableWidth - 56) / 3))
let itemSize = NSCollectionLayoutSize(
widthDimension: .absolute(118),
heightDimension: .absolute(154)
widthDimension: .absolute(cardWidth),
heightDimension: .absolute(156)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let group = NSCollectionLayoutGroup.horizontal(layoutSize: itemSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary
section.interGroupSpacing = 12
section.interGroupSpacing = 10
section.contentInsets = NSDirectionalEdgeInsets(
top: 4,
leading: 18,
bottom: 12,
bottom: 14,
trailing: 18
)
section.boundarySupplementaryItems = [
@@ -350,27 +344,17 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
case .mode:
let itemSize = NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(64)
heightDimension: .absolute(78)
)
let item = NSCollectionLayoutItem(layoutSize: itemSize)
let group = NSCollectionLayoutGroup.vertical(layoutSize: itemSize, subitems: [item])
let section = NSCollectionLayoutSection(group: group)
section.contentInsets = NSDirectionalEdgeInsets(
top: 4,
top: 8,
leading: 18,
bottom: 18,
trailing: 18
)
section.boundarySupplementaryItems = [
NSCollectionLayoutBoundarySupplementaryItem(
layoutSize: NSCollectionLayoutSize(
widthDimension: .fractionalWidth(1),
heightDimension: .absolute(42)
),
elementKind: UICollectionView.elementKindSectionHeader,
alignment: .top
),
]
return section
}
}
@@ -400,10 +384,9 @@ final class TravelAlbumAIRetouchTemplateViewController: BaseViewController {
cancelButton.isEnabled = !viewModel.isSubmitting
confirmButton.isEnabled = viewModel.canSubmit
confirmButton.alpha = viewModel.canSubmit ? 1 : 0.45
var confirmConfiguration = confirmButton.configuration
confirmConfiguration?.title = viewModel.isSubmitting ? "提交中" : "确定"
confirmConfiguration?.showsActivityIndicator = viewModel.isSubmitting
confirmButton.configuration = confirmConfiguration
confirmButton.setGradientEnabled(viewModel.canSubmit)
confirmButton.setTitle(viewModel.isSubmitting ? "提交中" : "确定", for: .normal)
confirmButton.setLoading(viewModel.isSubmitting)
isModalInPresentation = viewModel.isSubmitting
applySnapshot()
}
@@ -478,58 +461,84 @@ final class TravelAlbumAIRetouchOverviewCell: UICollectionViewCell {
private let titleLabel = UILabel()
private let selectionCountLabel = UILabel()
private let tipsContainer = UIView()
private let tipsIconContainer = UIView()
private let tipsIconView = UIImageView()
private let tipsLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
titleLabel.text = "AI修图"
titleLabel.textColor = AIRetouchTemplateStyle.textPrimary
titleLabel.font = .systemFont(ofSize: 20, weight: .semibold)
titleLabel.font = .systemFont(ofSize: 22, weight: .bold)
titleLabel.textAlignment = .center
titleLabel.accessibilityTraits = .header
selectionCountLabel.textColor = AIRetouchTemplateStyle.textSecondary
selectionCountLabel.font = UIFontMetrics(forTextStyle: .body).scaledFont(
for: .systemFont(ofSize: 17, weight: .regular)
selectionCountLabel.font = UIFontMetrics(forTextStyle: .subheadline).scaledFont(
for: .systemFont(ofSize: 15, weight: .regular)
)
selectionCountLabel.adjustsFontForContentSizeCategory = true
selectionCountLabel.textAlignment = .center
selectionCountLabel.accessibilityIdentifier = "travelAlbum.aiRetouchSelectionCountLabel"
tipsContainer.backgroundColor = AIRetouchTemplateStyle.tipsBackground
tipsContainer.layer.cornerRadius = 10
tipsContainer.layer.cornerRadius = 12
tipsContainer.layer.borderWidth = 1
tipsContainer.layer.borderColor = AIRetouchTemplateStyle.tipsBorder.cgColor
tipsContainer.accessibilityIdentifier = "travelAlbum.aiRetouchTipsContainer"
tipsIconContainer.backgroundColor = AIRetouchTemplateStyle.primary
tipsIconContainer.layer.cornerRadius = 8
tipsIconView.image = UIImage(systemName: "wand.and.stars")
tipsIconView.tintColor = .white
tipsIconView.contentMode = .scaleAspectFit
tipsLabel.textColor = AIRetouchTemplateStyle.tipsText
tipsLabel.font = UIFontMetrics(forTextStyle: .footnote).scaledFont(
for: .systemFont(ofSize: 13, weight: .regular)
for: .systemFont(ofSize: 12, weight: .regular)
)
tipsLabel.adjustsFontForContentSizeCategory = true
tipsLabel.numberOfLines = 0
tipsLabel.numberOfLines = 2
tipsLabel.accessibilityIdentifier = "travelAlbum.aiRetouchTipsLabel"
stackView.axis = .vertical
stackView.alignment = .fill
stackView.spacing = 4
stackView.spacing = 2
stackView.addArrangedSubview(titleLabel)
stackView.addArrangedSubview(selectionCountLabel)
stackView.addArrangedSubview(tipsContainer)
stackView.setCustomSpacing(12, after: selectionCountLabel)
stackView.setCustomSpacing(14, after: selectionCountLabel)
contentView.addSubview(stackView)
tipsContainer.addSubview(tipsIconContainer)
tipsIconContainer.addSubview(tipsIconView)
tipsContainer.addSubview(tipsLabel)
stackView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(10)
make.top.equalToSuperview().offset(8)
make.leading.trailing.equalToSuperview().inset(18)
make.bottom.lessThanOrEqualToSuperview().offset(-10)
make.bottom.lessThanOrEqualToSuperview().offset(-8)
}
titleLabel.snp.makeConstraints { make in
make.height.equalTo(30)
make.height.equalTo(32)
}
selectionCountLabel.snp.makeConstraints { make in
make.height.equalTo(24)
make.height.equalTo(22)
}
tipsContainer.snp.makeConstraints { make in
make.height.equalTo(52)
}
tipsIconContainer.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(12)
make.centerY.equalToSuperview()
make.size.equalTo(32)
}
tipsIconView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(7)
}
tipsLabel.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(UIEdgeInsets(top: 10, left: 12, bottom: 10, right: 12))
make.leading.equalTo(tipsIconContainer.snp.trailing).offset(10)
make.trailing.equalToSuperview().offset(-12)
make.centerY.equalToSuperview()
}
}
@@ -543,6 +552,7 @@ final class TravelAlbumAIRetouchOverviewCell: UICollectionViewCell {
selectionCountLabel.text = "已选择 \(selectedPhotoCount) 张照片"
tipsLabel.text = tips
tipsContainer.isHidden = tips == nil
tipsIconContainer.isHidden = tips == nil
tipsLabel.isHidden = tips == nil
}
}
@@ -560,22 +570,27 @@ final class TravelAlbumAIRetouchTemplateCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = .white
contentView.backgroundColor = AIRetouchTemplateStyle.cardBackground
contentView.layer.cornerRadius = 12
contentView.layer.borderWidth = 1
contentView.clipsToBounds = true
layer.shadowColor = UIColor(hex: 0x254A7A).cgColor
layer.shadowOpacity = 0.08
layer.shadowRadius = 7
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.masksToBounds = false
previewImageView.contentMode = .scaleAspectFill
previewImageView.clipsToBounds = true
previewImageView.backgroundColor = UIColor(hex: 0xEDF2F8)
nameLabel.font = .systemFont(ofSize: 13, weight: .medium)
nameLabel.font = .systemFont(ofSize: 13, weight: .semibold)
nameLabel.textColor = AIRetouchTemplateStyle.textPrimary
nameLabel.textAlignment = .center
nameLabel.lineBreakMode = .byTruncatingTail
checkView.image = UIImage(systemName: "checkmark.circle.fill")
checkView.tintColor = AIRetouchTemplateStyle.primary
checkView.backgroundColor = .white
checkView.layer.cornerRadius = 10
checkView.layer.cornerRadius = 11
var previewConfiguration = UIButton.Configuration.filled()
previewConfiguration.title = "预览"
previewConfiguration.baseForegroundColor = .white
@@ -598,7 +613,7 @@ final class TravelAlbumAIRetouchTemplateCell: UICollectionViewCell {
contentView.addSubview(previewButton)
previewImageView.snp.makeConstraints { make in
make.top.leading.trailing.equalToSuperview()
make.height.equalTo(116)
make.height.equalTo(118)
}
nameLabel.snp.makeConstraints { make in
make.top.equalTo(previewImageView.snp.bottom)
@@ -606,13 +621,13 @@ final class TravelAlbumAIRetouchTemplateCell: UICollectionViewCell {
make.bottom.equalToSuperview()
}
checkView.snp.makeConstraints { make in
make.top.trailing.equalToSuperview().inset(8)
make.size.equalTo(20)
make.top.trailing.equalToSuperview().inset(7)
make.size.equalTo(22)
}
previewButton.snp.makeConstraints { make in
make.trailing.bottom.equalTo(previewImageView).inset(8)
make.width.equalTo(58)
make.height.equalTo(32)
make.trailing.bottom.equalTo(previewImageView).inset(7)
make.width.equalTo(54)
make.height.equalTo(30)
}
}
@@ -649,6 +664,7 @@ final class TravelAlbumAIRetouchTemplateCell: UICollectionViewCell {
selected ? AIRetouchTemplateStyle.primary : AIRetouchTemplateStyle.border
).cgColor
contentView.layer.borderWidth = selected ? 2 : 1
layer.shadowOpacity = selected ? 0.16 : 0.08
checkView.isHidden = !selected
isSelected = selected
accessibilityLabel = template.name
@@ -668,43 +684,138 @@ private final class AIRetouchTemplatePreviewButton: UIButton {
}
}
/// 模板选择页主操作按钮,使用品牌蓝渐变并支持禁用态切换。
private final class AIRetouchGradientButton: UIButton {
private let gradientLayer = CAGradientLayer()
private let activityIndicator = UIActivityIndicatorView(style: .medium)
override init(frame: CGRect) {
super.init(frame: frame)
gradientLayer.colors = [
AIRetouchTemplateStyle.primary.cgColor,
AIRetouchTemplateStyle.primaryGradientEnd.cgColor,
]
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.cornerRadius = 12
layer.insertSublayer(gradientLayer, at: 0)
activityIndicator.color = .white
activityIndicator.hidesWhenStopped = true
activityIndicator.isUserInteractionEnabled = false
addSubview(activityIndicator)
clipsToBounds = true
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
activityIndicator.center = CGPoint(x: bounds.midX - 44, y: bounds.midY)
}
/// 根据提交条件切换品牌渐变和禁用灰色。
func setGradientEnabled(_ enabled: Bool) {
gradientLayer.colors = enabled
? [
AIRetouchTemplateStyle.primary.cgColor,
AIRetouchTemplateStyle.primaryGradientEnd.cgColor,
]
: [
AIRetouchTemplateStyle.disabledStart.cgColor,
AIRetouchTemplateStyle.disabledEnd.cgColor,
]
}
/// 切换提交中状态,并在标题左侧展示活动指示器。
func setLoading(_ loading: Bool) {
if loading {
activityIndicator.startAnimating()
} else {
activityIndicator.stopAnimating()
}
setNeedsLayout()
}
}
/// AI 修图计费模式卡,展示按张收费规则与剩余额度。
final class TravelAlbumAIRetouchModeCell: UICollectionViewCell {
static let reuseIdentifier = "TravelAlbumAIRetouchModeCell"
private let iconContainer = UIView()
private let iconView = UIImageView()
private let modeLabel = UILabel()
private let quotaContainer = UIView()
private let quotaStack = UIStackView()
private let requiredQuotaContainer = UIView()
private let requiredQuotaLabel = UILabel()
private let quotaLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 12
contentView.layer.borderWidth = 1
contentView.layer.borderColor = AIRetouchTemplateStyle.border.cgColor
contentView.layer.shadowColor = UIColor(hex: 0x254A7A).cgColor
contentView.layer.shadowOpacity = 0.06
contentView.layer.shadowRadius = 8
contentView.layer.shadowOffset = CGSize(width: 0, height: 3)
iconContainer.backgroundColor = AIRetouchTemplateStyle.primary
iconContainer.layer.cornerRadius = 10
iconView.image = UIImage(systemName: "wand.and.stars")
iconView.tintColor = .white
iconView.contentMode = .scaleAspectFit
modeLabel.text = "AI精修 · 按张收费"
modeLabel.textColor = AIRetouchTemplateStyle.textPrimary
modeLabel.font = .systemFont(ofSize: 16, weight: .medium)
quotaContainer.backgroundColor = AIRetouchTemplateStyle.primary.withAlphaComponent(0.1)
quotaContainer.layer.cornerRadius = 15
quotaLabel.font = .monospacedDigitSystemFont(ofSize: 14, weight: .medium)
quotaLabel.textColor = AIRetouchTemplateStyle.primary
quotaLabel.textAlignment = .center
modeLabel.font = .systemFont(ofSize: 15, weight: .semibold)
quotaStack.axis = .vertical
quotaStack.alignment = .trailing
quotaStack.spacing = 3
requiredQuotaContainer.backgroundColor = AIRetouchTemplateStyle.primary.withAlphaComponent(0.1)
requiredQuotaContainer.layer.cornerRadius = 12
requiredQuotaLabel.font = .monospacedDigitSystemFont(ofSize: 12, weight: .semibold)
requiredQuotaLabel.textColor = AIRetouchTemplateStyle.primary
requiredQuotaLabel.textAlignment = .center
requiredQuotaLabel.accessibilityIdentifier = "travelAlbum.aiRetouchRequiredQuotaLabel"
quotaLabel.font = .monospacedDigitSystemFont(ofSize: 12, weight: .regular)
quotaLabel.textColor = AIRetouchTemplateStyle.textSecondary
quotaLabel.textAlignment = .right
quotaLabel.accessibilityIdentifier = "travelAlbum.aiRetouchRemainingQuotaLabel"
contentView.addSubview(iconContainer)
iconContainer.addSubview(iconView)
contentView.addSubview(modeLabel)
contentView.addSubview(quotaContainer)
quotaContainer.addSubview(quotaLabel)
contentView.addSubview(quotaStack)
quotaStack.addArrangedSubview(requiredQuotaContainer)
requiredQuotaContainer.addSubview(requiredQuotaLabel)
quotaStack.addArrangedSubview(quotaLabel)
iconContainer.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(14)
make.centerY.equalToSuperview()
make.size.equalTo(38)
}
iconView.snp.makeConstraints { make in
make.edges.equalToSuperview().inset(9)
}
modeLabel.snp.makeConstraints { make in
make.leading.equalToSuperview().offset(16)
make.leading.equalTo(iconContainer.snp.trailing).offset(10)
make.centerY.equalToSuperview()
make.trailing.lessThanOrEqualTo(quotaContainer.snp.leading).offset(-12)
make.trailing.lessThanOrEqualTo(quotaStack.snp.leading).offset(-10)
}
quotaContainer.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-16)
quotaStack.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-14)
make.centerY.equalToSuperview()
make.width.equalTo(96)
make.height.equalTo(30)
}
quotaLabel.snp.makeConstraints { make in
requiredQuotaContainer.snp.makeConstraints { make in
make.height.equalTo(24)
make.width.greaterThanOrEqualTo(108)
}
requiredQuotaLabel.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
@@ -714,20 +825,24 @@ final class TravelAlbumAIRetouchModeCell: UICollectionViewCell {
fatalError("init(coder:) has not been implemented")
}
/// 更新剩余可精修张数,并在额度不足时切换为警示色。
func apply(remainingQuota: Int?, insufficient: Bool) {
/// 更新预计消耗和剩余精修张数,并在额度不足时切换为警示色。
func apply(requiredQuota: Int, remainingQuota: Int?, insufficient: Bool) {
requiredQuotaLabel.text = "预计消耗 \(requiredQuota) 张"
requiredQuotaLabel.accessibilityLabel = "预计消耗\(requiredQuota)张修图额度"
quotaLabel.text = remainingQuota.map { "剩余 \($0) 张" } ?? "剩余 -- 张"
quotaLabel.accessibilityLabel = remainingQuota.map { "剩余\($0)张修图额度" }
?? "剩余修图额度暂不可用"
quotaLabel.textColor = insufficient
requiredQuotaLabel.textColor = insufficient
? AIRetouchTemplateStyle.danger
: (remainingQuota == nil ? AIRetouchTemplateStyle.textSecondary : AIRetouchTemplateStyle.primary)
quotaContainer.backgroundColor = quotaLabel.textColor.withAlphaComponent(0.1)
: AIRetouchTemplateStyle.primary
requiredQuotaContainer.backgroundColor = requiredQuotaLabel.textColor.withAlphaComponent(0.1)
quotaLabel.textColor = insufficient ? AIRetouchTemplateStyle.danger : AIRetouchTemplateStyle.textSecondary
}
}
/// 模板分组标题右侧的业务标记。
fileprivate enum AIRetouchTemplateSectionBadge {
case required
case optional
case gift
}
@@ -773,12 +888,16 @@ final class TravelAlbumAIRetouchSectionHeader: UICollectionReusableView {
titleLabel.text = title
optionalLabel.isHidden = badge == nil
switch badge {
case .required:
optionalLabel.text = " 必选 "
optionalLabel.textColor = AIRetouchTemplateStyle.primary
optionalLabel.backgroundColor = AIRetouchTemplateStyle.primary.withAlphaComponent(0.1)
case .optional:
optionalLabel.text = " 选填 "
optionalLabel.textColor = AIRetouchTemplateStyle.primary
optionalLabel.backgroundColor = AIRetouchTemplateStyle.primary.withAlphaComponent(0.1)
case .gift:
optionalLabel.text = " 赠送 · 不占精修张数 "
optionalLabel.text = " 赠送 · 不占额度 "
optionalLabel.textColor = AIRetouchTemplateStyle.gift
optionalLabel.backgroundColor = AIRetouchTemplateStyle.giftBackground
case nil:
@@ -918,13 +1037,18 @@ extension TravelAlbumAIRetouchTemplatePreviewViewController: UIScrollViewDelegat
/// AI 修图模板页视觉常量。
private enum AIRetouchTemplateStyle {
static let primary = UIColor(hex: 0x1677FF)
static let pageBackground = UIColor(hex: 0xF7F7F7)
static let primaryGradientEnd = UIColor(hex: 0x4B9BFF)
static let pageBackground = UIColor(hex: 0xF7FAFF)
static let cardBackground = UIColor.white
static let textPrimary = UIColor(hex: 0x172033)
static let textSecondary = UIColor(hex: 0x6E6E73)
static let tipsBackground = UIColor(hex: 0xEAF3FF)
static let textSecondary = UIColor(hex: 0x7D8799)
static let tipsBackground = UIColor(hex: 0xF4F8FF)
static let tipsBorder = UIColor(hex: 0xD9E7FA)
static let tipsText = UIColor(hex: 0x385778)
static let gift = UIColor(hex: 0xFF9418)
static let giftBackground = UIColor(hex: 0xFFF1D9)
static let border = UIColor(hex: 0xDCE4EF)
static let danger = UIColor(hex: 0xE53935)
static let disabledStart = UIColor(hex: 0xA9C8EE)
static let disabledEnd = UIColor(hex: 0xC6DAF3)
}