| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- //
- // LNImageUploadView.swift
- // Lanu
- //
- // Created by OneeChan on 2025/12/25.
- //
- import Foundation
- import UIKit
- import SnapKit
- protocol LNImageUploadViewDelegate: NSObject {
- func onImageUploadView(view: LNImageUploadView, didUploadImage url: String)
- func onImageUploadViewStartUpload(view: LNImageUploadView)
- func onImageUploadViewDidClickDelete(view: LNImageUploadView)
- }
- extension LNImageUploadViewDelegate {
- func onImageUploadView(view: LNImageUploadView, didUploadImage url: String) {}
- func onImageUploadViewStartUpload(view: LNImageUploadView) {}
- func onImageUploadViewDidClickDelete(view: LNImageUploadView) {}
- }
- class LNImageUploadView: UIImageView {
- private let defaultView = UIImageView()
- var showDefault: Bool = false {
- didSet {
- defaultView.isHidden = image != nil || !showDefault
- }
- }
-
- private let progressCover = UIView()
- let progressView = LNCircleProgressView()
- private let failedIc = UIImageView()
-
- private var curTask: String?
- private(set) var imageUrl: String?
-
- private let deleteButton = UIButton()
- var showClearButton: Bool = false {
- didSet {
- deleteButton.isHidden = !showClearButton || image == nil
- }
- }
-
- var uploadType: LNUploadFileType = .feedback
- weak var delegate: LNImageUploadViewDelegate?
-
- override var image: UIImage? {
- didSet {
- defaultView.isHidden = image != nil || !showDefault
- }
- }
-
- override init(image: UIImage? = nil) {
- super.init(image: image)
-
- setupViews()
- }
-
- func loadImage(url: String) {
- reset()
-
- imageUrl = url
-
- sd_setImage(with: URL(string: url))
- progressCover.isHidden = true
-
- if showClearButton {
- deleteButton.isHidden = false
- }
- }
-
- func uploadImage(image: UIImage) {
- reset()
-
- self.image = image
-
- progressCover.isHidden = false
- failedIc.isHidden = true
- progressView.isHidden = false
-
- if showClearButton {
- deleteButton.isHidden = false
- }
-
- let suffix = "\(Int(image.size.width))x\(Int(image.size.height)).jpeg"
- delegate?.onImageUploadViewStartUpload(view: self)
- curTask = LNFileUploader.shared.startUpload(
- type: uploadType, fileData: image.jpegData(compressionQuality: 1.0)!,
- suffix: suffix)
- { [weak self] progress in
- guard let self else { return }
- progressView.setProgress(CGFloat(progress), animated: true)
- } completionHandler: { [weak self] url, err in
- guard let self else { return }
-
- curTask = nil
-
- if let url, err == nil {
- imageUrl = url
-
- progressCover.isHidden = true
-
- let key = SDWebImageManager.shared.cacheKey(for: URL(string: url))
- SDWebImageManager.shared.imageCache.store?(image, imageData: nil, forKey: key, context: nil, cacheType: .all)
- delegate?.onImageUploadView(view: self, didUploadImage: url)
- } else if url == nil, let err {
- showToast(err)
-
- failedIc.isHidden = false
- progressView.isHidden = true
- }
- }
- }
-
- func cancelUpload() {
- if let curTask {
- LNFileUploader.shared.cancelUpload(taskID: curTask)
- }
- }
-
- func reset() {
- cancelUpload()
-
- image = nil
- imageUrl = nil
- curTask = nil
- deleteButton.isHidden = true
- }
-
- required init?(coder: NSCoder) {
- fatalError("init(coder:) has not been implemented")
- }
- }
- extension LNImageUploadView {
- private func setupViews() {
- isUserInteractionEnabled = true
- contentMode = .scaleAspectFill
-
- let noneView = buildDefault()
- addSubview(noneView)
- noneView.snp.makeConstraints { make in
- make.center.equalToSuperview()
- }
-
- let progress = buildProgress()
- addSubview(progress)
- progress.snp.makeConstraints { make in
- make.edges.equalToSuperview()
- }
-
- let clear = buildClearButton()
- addSubview(clear)
- clear.snp.makeConstraints { make in
- make.top.equalToSuperview()
- make.trailing.equalToSuperview()
- }
- }
-
- private func buildDefault() -> UIView {
- let config = UIImage.SymbolConfiguration(pointSize: 17)
- defaultView.image = .init(systemName: "plus", withConfiguration: config)
- defaultView.tintColor = .text_3
- defaultView.isHidden = true
-
- return defaultView
- }
-
- private func buildProgress() -> UIView {
- progressCover.isHidden = true
- progressCover.backgroundColor = .black.withAlphaComponent(0.3)
-
- progressCover.addSubview(progressView)
- progressView.snp.makeConstraints { make in
- make.center.equalToSuperview()
- make.width.height.equalTo(30)
- }
-
- failedIc.isHidden = true
- failedIc.image = .init(named: "ic_im_chat_send_failed")
- failedIc.onTap { [weak self] in
- guard let self else { return }
- guard let image = self.image else { return }
- uploadImage(image: image)
- }
- progressCover.addSubview(failedIc)
- failedIc.snp.makeConstraints { make in
- make.center.equalToSuperview()
- make.width.height.equalTo(24)
- }
-
- return progressCover
- }
-
- private func buildClearButton() -> UIView {
- deleteButton.isHidden = true
- deleteButton.addAction(UIAction(handler: { [weak self] _ in
- guard let self else { return }
- reset()
-
- delegate?.onImageUploadViewDidClickDelete(view: self)
- }), for: .touchUpInside)
- deleteButton.snp.makeConstraints { make in
- make.width.height.equalTo(28)
- }
-
- let config = UIImage.SymbolConfiguration(pointSize: 7)
- let icon = UIImageView()
- icon.image = .init(systemName: "xmark", withConfiguration: config)
- icon.contentMode = .center
- icon.tintColor = .white
- icon.backgroundColor = .black.withAlphaComponent(0.5)
- icon.layer.cornerRadius = 8
- deleteButton.addSubview(icon)
- icon.snp.makeConstraints { make in
- make.center.equalToSuperview()
- make.width.height.equalTo(16)
- }
-
- return deleteButton
- }
- }
|