// // UploadImageView.swift // Crush // // Created by Leon on 2025/7/20. // import UIKit import SnapKit enum UploadImageState: UInt { case `default` case uploading case success case failed case oversize case yellow } class UploadImageView: UIView { // MARK: - Properties let imageView: CLImageView let errorLabel: UILabel let deleteButton: EPIconTertiaryDarkButton let indicatorView: UIActivityIndicatorView var state: UploadImageState = .default { didSet { dealUploadState(state) } } var notShowDeleteButton: Bool = false { didSet { deleteButton.alpha = notShowDeleteButton ? 0 : 1 } } var bgTapBlock: (() -> Void)? var deleteTapBlock: (() -> Void)? var retryTapBlock: (() -> Void)? private var bottomBottom: UIButton private var bgButton: EPHighlightBorderButton private var loadingView: UIView private var indicatorLabel: UILabel private var errorView: UIView private var retryButton: UIButton private var videoMode: Bool = false // MARK: - Initialization override init(frame: CGRect) { imageView = CLImageView(frame: .zero) errorLabel = UILabel() deleteButton = EPIconTertiaryDarkButton(radius: .round, iconSize: .small, iconCode: .delete) indicatorView = UIActivityIndicatorView() bottomBottom = UIButton(type: .custom) bgButton = EPHighlightBorderButton(type: .custom) loadingView = UIView() indicatorLabel = UILabel() errorView = UIView() retryButton = UIButton() super.init(frame: frame) setupUI() } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Setup private func setupUI() { backgroundColor = .c.csen//EPSystemToken.color(.surfaceElementNormal) layer.cornerRadius = 8 clipsToBounds = true // Bottom button bottomBottom.backgroundColor = .clear bottomBottom.addTarget(self, action: #selector(bgButtonTapAction), for: .touchUpInside) addSubview(bottomBottom) bottomBottom.snp.makeConstraints { make in make.edges.equalTo(self) } // Background button bgButton.layer.cornerRadius = layer.cornerRadius bgButton.clipsToBounds = true bgButton.addTarget(self, action: #selector(bgButtonTapAction), for: .touchUpInside) let bgButtonImageColor: UIColor = .c.cttn let image = MWIconFont.image(fromIcon: .iconUploadimg, size: CGSize(width: 48, height: 48), color: bgButtonImageColor) //MWIconFont.image(fromIconInt: .iconUploadimg, size: CGSize(width: 48, height: 48), color: bgButtonImageColor, edgeInsets: .zero) bgButton.setImage(image, for: .normal) bgButton.setTitle("Click to generate images", for: .normal) bgButton.setTitleColor(.c.cttn, for: .normal) bgButton.titleLabel?.font = .t.tbm //EPSystemToken.typography(.txtBodyM).font addSubview(bgButton) bgButton.snp.makeConstraints { make in make.edges.equalTo(self) } // Image view imageView.layer.cornerRadius = 8 imageView.clipsToBounds = true imageView.layer.borderColor = UIColor.c.civn.cgColor imageView.isHidden = true imageView.contentMode = .scaleAspectFill addSubview(imageView) imageView.snp.makeConstraints { make in make.edges.equalTo(self) } // Loading view loadingView.isHidden = true loadingView.backgroundColor = UIColor.black.withAlphaComponent(0.7) // Replaced hex_120E1B_bg addSubview(loadingView) loadingView.snp.makeConstraints { make in make.edges.equalTo(self) } // Indicator view indicatorView.color = .white // Replaced hex_FFFFFF loadingView.addSubview(indicatorView) indicatorView.snp.makeConstraints { make in make.centerX.equalTo(loadingView) make.centerY.equalTo(loadingView).offset(-10) } // Indicator label indicatorLabel.font = .t.tbm//EPSystemToken.typography(.txtBodyM).font indicatorLabel.textColor = .c.ctpsn//EPSystemToken.color(.txtPrimarySpecialmapNormal) indicatorLabel.text = NSLocalizedString("uploading", comment: "") loadingView.addSubview(indicatorLabel) indicatorLabel.snp.makeConstraints { make in make.centerX.equalTo(loadingView) make.top.equalTo(indicatorView.snp.bottom).offset(12) } // Error view errorView.isHidden = true errorView.backgroundColor = .red //EPSystemToken.color(.importantOnpicNormal) addSubview(errorView) errorView.snp.makeConstraints { make in make.leading.trailing.bottom.equalTo(self) } // Error label errorLabel.isHidden = false errorLabel.font = .t.tbs//EPSystemToken.typography(.txtBodyS).font errorLabel.textColor = .c.ctpsn//EPSystemToken.color(.txtPrimarySpecialmapNormal) errorLabel.numberOfLines = 5 errorLabel.textAlignment = .left errorView.addSubview(errorLabel) errorLabel.snp.makeConstraints { make in make.leading.equalTo(errorView).offset(16) make.trailing.equalTo(errorView).offset(-16) make.top.equalTo(errorView).offset(8) make.bottom.equalTo(errorView).offset(-8) } // Delete button let deleteButtonSize = deleteButton.bgImageSize() deleteButton.addTarget(self, action: #selector(deleteAction), for: .touchUpInside) addSubview(deleteButton) deleteButton.snp.makeConstraints { make in make.trailing.equalTo(self).offset(-16) make.top.equalTo(self).offset(16) make.size.equalTo(deleteButtonSize) } // Retry button retryButton.isHidden = true let retryImage = MWIconFont.image(fromIcon: .iconReload, size: CGSize(width: 48, height: 48), color: UIColor.c.ctpsn) retryButton.setImage(retryImage, for: .normal) retryButton.addTarget(self, action: #selector(retryAction), for: .touchUpInside) addSubview(retryButton) retryButton.snp.makeConstraints { make in make.center.equalTo(self) make.size.equalTo(CGSize(width: 48, height: 48)) } state = .default } // MARK: - Public Methods func setupVideoMode() { videoMode = true } // MARK: - Actions @objc private func bgButtonTapAction() { bgTapBlock?() } @objc private func deleteAction() { deleteTapBlock?() } @objc private func retryAction() { retryTapBlock?() } // MARK: - State Handling private func dealUploadState(_ state: UploadImageState) { indicatorView.stopAnimating() imageView.isHidden = false imageView.layer.borderWidth = 0 errorLabel.text = "" bgButton.isHidden = true switch state { case .default: bgButton.isHidden = false bgButton.isEnabled = true imageView.isHidden = true deleteButton.isHidden = true retryButton.isHidden = true loadingView.isHidden = true errorView.isHidden = true case .uploading: bottomBottom.isEnabled = false deleteButton.isHidden = false retryButton.isHidden = true loadingView.isHidden = false errorView.isHidden = true indicatorView.startAnimating() case .failed: bottomBottom.isEnabled = false deleteButton.isHidden = false retryButton.isHidden = false loadingView.isHidden = true errorView.isHidden = false imageView.layer.borderWidth = CLSystemToken.border(token: .bs)//EPSystemToken.border(.borderS) case .oversize: bottomBottom.isEnabled = false deleteButton.isHidden = false retryButton.isHidden = true loadingView.isHidden = true errorView.isHidden = false imageView.layer.borderWidth = CLSystemToken.border(token: .bs)//EPSystemToken.border(.borderS) case .yellow: bottomBottom.isEnabled = false deleteButton.isHidden = false retryButton.isHidden = true loadingView.isHidden = true errorView.isHidden = false imageView.layer.borderWidth = CLSystemToken.border(token: .bs) case .success: bottomBottom.isEnabled = true deleteButton.isHidden = false retryButton.isHidden = true loadingView.isHidden = true errorView.isHidden = true } } // MARK: - Overrides override func layoutSubviews() { super.layoutSubviews() bgButton.setUp(.top, padding: 12) } }