286 lines
9.4 KiB
Swift
286 lines
9.4 KiB
Swift
|
|
//
|
||
|
|
// CommonUploadImageButton.swift
|
||
|
|
// Crush
|
||
|
|
//
|
||
|
|
// Created by Leon on 2025/7/21.
|
||
|
|
//
|
||
|
|
|
||
|
|
import UIKit
|
||
|
|
import SnapKit
|
||
|
|
|
||
|
|
enum CommentUploadState: Int {
|
||
|
|
case loading
|
||
|
|
case success
|
||
|
|
case failed
|
||
|
|
case checkdFailed
|
||
|
|
}
|
||
|
|
|
||
|
|
class CommonUploadImageButton: UIButton {
|
||
|
|
private var indicatorBGView: UIView!
|
||
|
|
private var indicatorView: UIActivityIndicatorView!
|
||
|
|
private var retryButton: UIButton!
|
||
|
|
private var errorButton: UIButton!
|
||
|
|
private var contentImageView: UIImageView!
|
||
|
|
private var closeButton: EPIconTertiaryDarkButton!
|
||
|
|
|
||
|
|
@Published private(set) var uploadState: CommentUploadState = .loading {
|
||
|
|
didSet {
|
||
|
|
reloadViews()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
private(set) var imageUrlNowIfHave: String?
|
||
|
|
var uploadModel: CommentImageModel?
|
||
|
|
|
||
|
|
var optionErrorMsg: String?
|
||
|
|
var closeAction: ((CommonUploadImageButton) -> Void)?
|
||
|
|
var tapRetryAction: ((CommonUploadImageButton) -> Void)?
|
||
|
|
|
||
|
|
var uploadSuceess: ((CommonUploadImageButton) -> Void)?
|
||
|
|
var uploadOrCheckFailed: ((CommonUploadImageButton) -> Void)?
|
||
|
|
|
||
|
|
override init(frame: CGRect) {
|
||
|
|
super.init(frame: frame)
|
||
|
|
initializeViews()
|
||
|
|
}
|
||
|
|
|
||
|
|
required init?(coder: NSCoder) {
|
||
|
|
super.init(coder: coder)
|
||
|
|
initializeViews()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func initializeViews() {
|
||
|
|
layer.cornerRadius = 8.0
|
||
|
|
layer.masksToBounds = true
|
||
|
|
|
||
|
|
contentImageView = {
|
||
|
|
let imageView = UIImageView()
|
||
|
|
addSubview(imageView)
|
||
|
|
imageView.contentMode = .scaleAspectFill
|
||
|
|
imageView.snp.makeConstraints { make in
|
||
|
|
make.edges.equalToSuperview()
|
||
|
|
}
|
||
|
|
return imageView
|
||
|
|
}()
|
||
|
|
|
||
|
|
indicatorBGView = {
|
||
|
|
let view = UIView()
|
||
|
|
addSubview(view)
|
||
|
|
view.isHidden = true
|
||
|
|
view.layer.borderColor = UIColor.c.civn.cgColor//EPSystemToken.color(.important_variant_normal).cgColor
|
||
|
|
view.layer.cornerRadius = layer.cornerRadius
|
||
|
|
view.backgroundColor = UIColor.c.csedn//EPSystemToken.color(.surface_element_dark_normal)
|
||
|
|
view.snp.makeConstraints { make in
|
||
|
|
make.edges.equalToSuperview()
|
||
|
|
}
|
||
|
|
return view
|
||
|
|
}()
|
||
|
|
|
||
|
|
indicatorView = {
|
||
|
|
let indicator = UIActivityIndicatorView()
|
||
|
|
addSubview(indicator)
|
||
|
|
indicator.color = .c.ctpsn//EPSystemToken.color(.txt_primary_specialmap_normal)
|
||
|
|
indicator.isHidden = true
|
||
|
|
indicator.snp.makeConstraints { make in
|
||
|
|
make.center.equalToSuperview()
|
||
|
|
make.size.equalTo(CGSize(width: 24, height: 24))
|
||
|
|
}
|
||
|
|
return indicator
|
||
|
|
}()
|
||
|
|
|
||
|
|
closeButton = {
|
||
|
|
let button = EPIconTertiaryDarkButton(radius: .round, iconSize: .xxs, iconCode: .delete)
|
||
|
|
addSubview(button)
|
||
|
|
button.isHidden = true
|
||
|
|
button.touchAreaInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
|
||
|
|
button.addTarget(self, action: #selector(closeButtonAction), for: .touchUpInside)
|
||
|
|
button.snp.makeConstraints { make in
|
||
|
|
make.trailing.equalToSuperview().offset(-8)
|
||
|
|
make.top.equalToSuperview().offset(8)
|
||
|
|
make.size.equalTo(CGSize(width: 16, height: 16))
|
||
|
|
}
|
||
|
|
return button
|
||
|
|
}()
|
||
|
|
|
||
|
|
retryButton = {
|
||
|
|
let button = UIButton(type: .custom)
|
||
|
|
addSubview(button)
|
||
|
|
button.isHidden = true
|
||
|
|
let image = MWIconFont.image(fromIcon: .iconReload, size: .init(width: 16, height: 16), color: .c.ctpsn)
|
||
|
|
button.setImage(image, for: .normal)
|
||
|
|
button.contentMode = .center
|
||
|
|
button.touchAreaInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
|
||
|
|
button.addTarget(self, action: #selector(retryButtonAction), for: .touchUpInside)
|
||
|
|
button.snp.makeConstraints { make in
|
||
|
|
make.center.equalToSuperview()
|
||
|
|
make.size.equalTo(CGSize(width: 32, height: 32))
|
||
|
|
}
|
||
|
|
return button
|
||
|
|
}()
|
||
|
|
|
||
|
|
errorButton = {
|
||
|
|
let button = UIButton(type: .custom)
|
||
|
|
addSubview(button)
|
||
|
|
button.isHidden = true
|
||
|
|
button.setImage(UIImage(named: "album_reupload"), for: .normal)
|
||
|
|
button.touchAreaInsets = UIEdgeInsets(top: 2, left: 2, bottom: 2, right: 2)
|
||
|
|
button.addTarget(self, action: #selector(errorButtonAction), for: .touchUpInside)
|
||
|
|
button.snp.makeConstraints { make in
|
||
|
|
make.center.equalToSuperview()
|
||
|
|
make.size.equalTo(CGSize(width: 32, height: 32))
|
||
|
|
}
|
||
|
|
return button
|
||
|
|
}()
|
||
|
|
|
||
|
|
setupImageDefaultState()
|
||
|
|
}
|
||
|
|
|
||
|
|
private func reloadViews() {
|
||
|
|
indicatorBGView.isHidden = true
|
||
|
|
indicatorBGView.layer.borderWidth = 0
|
||
|
|
if indicatorView.isAnimating {
|
||
|
|
indicatorView.stopAnimating()
|
||
|
|
}
|
||
|
|
retryButton.isHidden = true
|
||
|
|
closeButton.isHidden = true
|
||
|
|
errorButton.isHidden = true
|
||
|
|
|
||
|
|
switch uploadState {
|
||
|
|
case .loading:
|
||
|
|
indicatorBGView.isHidden = false
|
||
|
|
indicatorView.startAnimating()
|
||
|
|
case .failed:
|
||
|
|
indicatorBGView.isHidden = false
|
||
|
|
retryButton.isHidden = false
|
||
|
|
closeButton.isHidden = false
|
||
|
|
indicatorBGView.layer.borderWidth = CLSystemToken.border(token: .bs)//EPSystemToken.border(.s)
|
||
|
|
case .success:
|
||
|
|
closeButton.isHidden = false
|
||
|
|
case .checkdFailed:
|
||
|
|
indicatorBGView.isHidden = false
|
||
|
|
closeButton.isHidden = false
|
||
|
|
errorButton.isHidden = false
|
||
|
|
indicatorBGView.layer.borderWidth = CLSystemToken.border(token: .bs)//EPSystemToken.border(.s)
|
||
|
|
}
|
||
|
|
|
||
|
|
if imageView?.image != nil {
|
||
|
|
setImage(nil, for: .normal)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Public Methods
|
||
|
|
|
||
|
|
func setupImageDefaultState() {
|
||
|
|
|
||
|
|
let image = MWIconFont.image(fromIcon: .iconUploadimg, size: .init(width: 24, height: 24), color: .c.cttn)
|
||
|
|
setImage(image, for: .normal)
|
||
|
|
backgroundColor = .c.csen//EPSystemToken.color(.surface_element_normal)
|
||
|
|
|
||
|
|
contentImageView.image = nil
|
||
|
|
imageUrlNowIfHave = nil
|
||
|
|
closeButton.isHidden = true
|
||
|
|
indicatorBGView.isHidden = true
|
||
|
|
errorButton.isHidden = true
|
||
|
|
}
|
||
|
|
|
||
|
|
@discardableResult
|
||
|
|
func bindViewImage(_ image: UIImage, report: Bool = false, checkImage: Bool = false) -> UploadPhotoM {
|
||
|
|
contentImageView.image = image
|
||
|
|
uploadState = .loading
|
||
|
|
DispatchQueue.main.async {[weak self] in
|
||
|
|
// UploadPhotoM is returned finally
|
||
|
|
self?.uploadState = .loading
|
||
|
|
}
|
||
|
|
|
||
|
|
let model = CommentImageModel()
|
||
|
|
model.isReport = report
|
||
|
|
uploadModel = model
|
||
|
|
let photo = model.startUpload(with: image, checkImage: checkImage)
|
||
|
|
uploadModel?.imageUploadComplete = { [weak self] result, photo in
|
||
|
|
guard let self = self else { return }
|
||
|
|
if result {
|
||
|
|
self.uploadState = .success
|
||
|
|
self.uploadSuceess?(self)
|
||
|
|
} else if photo.imageChecked && photo.imageCheckIsViolation {
|
||
|
|
self.uploadState = .checkdFailed
|
||
|
|
self.uploadOrCheckFailed?(self)
|
||
|
|
} else {
|
||
|
|
self.uploadState = .failed
|
||
|
|
self.uploadOrCheckFailed?(self)
|
||
|
|
}
|
||
|
|
self.reloadViews()
|
||
|
|
}
|
||
|
|
reloadViews()
|
||
|
|
return photo
|
||
|
|
}
|
||
|
|
|
||
|
|
func bindViewImageNoUpload(_ image: UIImage) {
|
||
|
|
contentImageView.image = image
|
||
|
|
uploadState = .loading
|
||
|
|
reloadViews()
|
||
|
|
}
|
||
|
|
|
||
|
|
func bindViewImage(_ image: UIImage?) {
|
||
|
|
guard let img = image else{
|
||
|
|
return
|
||
|
|
}
|
||
|
|
contentImageView.image = img
|
||
|
|
}
|
||
|
|
|
||
|
|
func bindSuccessImage(_ image: UIImage?) {
|
||
|
|
guard let img = image else{
|
||
|
|
return
|
||
|
|
}
|
||
|
|
contentImageView.image = img
|
||
|
|
uploadState = .success
|
||
|
|
}
|
||
|
|
|
||
|
|
func bindViewImageUrl(_ imageUrl: String) {
|
||
|
|
imageUrlNowIfHave = imageUrl
|
||
|
|
//contentImageView.sd_setImage(with: URL(string: imageUrl), placeholderImage: UIImage.imgPlaceHolder)
|
||
|
|
contentImageView.loadImage(imageUrl)
|
||
|
|
let model = CommentImageModel()
|
||
|
|
model.uploadUrl = imageUrl
|
||
|
|
uploadModel = model
|
||
|
|
uploadState = .success
|
||
|
|
reloadViews()
|
||
|
|
}
|
||
|
|
|
||
|
|
func givtStateOutsize(_ state: CommentUploadState) {
|
||
|
|
uploadState = state
|
||
|
|
}
|
||
|
|
|
||
|
|
func setupCornerRadius(_ radius: CGFloat) {
|
||
|
|
layer.cornerRadius = radius
|
||
|
|
indicatorBGView.layer.cornerRadius = radius
|
||
|
|
}
|
||
|
|
|
||
|
|
// MARK: - Actions
|
||
|
|
|
||
|
|
@objc private func retryButtonAction() {
|
||
|
|
uploadState = .loading
|
||
|
|
reloadViews()
|
||
|
|
if let tapRetryAction = tapRetryAction {
|
||
|
|
tapRetryAction(self)
|
||
|
|
} else {
|
||
|
|
if let img = uploadModel?.image{
|
||
|
|
uploadModel?.startUpload(with: img)
|
||
|
|
}else{
|
||
|
|
fatalError()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@objc private func errorButtonAction() {
|
||
|
|
if let optionErrorMsg = optionErrorMsg, !optionErrorMsg.isEmpty {
|
||
|
|
//Hud.showTip(optionErrorMsg)
|
||
|
|
Hud.toast(str: optionErrorMsg)
|
||
|
|
} else {
|
||
|
|
Hud.toast(str: "NSFW materials have been detected, please upload other photos")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@objc private func closeButtonAction() {
|
||
|
|
closeAction?(self)
|
||
|
|
}
|
||
|
|
}
|