// // LNCommonAlertView.swift // Lanu // // Created by OneeChan on 2025/12/12. // import Foundation import UIKit import SnapKit extension LNCommonAlertView { static func showCommonDeleteAlert(handler: (() -> Void)?) { let alert = LNCommonAlertView() alert.titleLabel.text = .init(key: "A00308") alert.showConfirm { handler?() } alert.showCancel() alert.popup() } } class LNCommonAlertView: UIView { private let background = UIView() private let container = UIView() private let messageViews = UIStackView() private let buttonViews = UIStackView() private let miniScale = 0.01 private let animateDuration = 0.15 let titleLabel = UILabel() let messageLabel = UILabel() var touchOutsideCancel = true override init(frame: CGRect) { super.init(frame: frame) setupViews() } func showConfirm(_ title: String = .init(key: "A00002"), handler: @escaping () -> Void) { let button = buildConfirmButton() button.setTitle(title, for: .normal) button.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } dismiss() handler() }), for: .touchUpInside) buttonViews.insertArrangedSubview(button, at: 0) } func showCancel(_ title: String = .init(key: "A00003"), handler: (() -> Void)? = nil) { let button = buildCancelButton() button.setTitle(title, for: .normal) button.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } dismiss() handler?() }), for: .touchUpInside) buttonViews.addArrangedSubview(button) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNCommonAlertView { func popup(_ holder: UIView? = nil) { guard let holder = holder ?? UIView.appKeyWindow else { return } holder.addSubview(self) frame = holder.bounds titleLabel.isHidden = titleLabel.text?.isEmpty != false messageLabel.isHidden = messageLabel.text?.isEmpty != false layoutIfNeeded() container.transform = .init(scaleX: miniScale, y: miniScale) UIView.animate(withDuration: animateDuration, delay: 0, options: .curveEaseInOut) { [weak self] in guard let self else { return } container.transform = .identity } } func dismiss() { UIView.animate(withDuration: animateDuration, delay: 0, options: .curveEaseInOut) { [weak self] in guard let self else { return } container.transform = .init(scaleX: miniScale, y: miniScale) } completion: { [weak self] _ in guard let self else { return } removeFromSuperview() } } } extension LNCommonAlertView { private func setupViews() { background.backgroundColor = .black.withAlphaComponent(0.4) background.onTap { [weak self] in guard let self else { return } guard touchOutsideCancel else { return } dismiss() } addSubview(background) background.snp.makeConstraints { make in make.edges.equalToSuperview() } container.backgroundColor = .fill container.layer.cornerRadius = 20 addSubview(container) container.snp.makeConstraints { make in make.center.equalToSuperview() make.width.equalToSuperview().multipliedBy(0.8) } let closeButton = UIButton() closeButton.setImage(.init(systemName: "xmark"), for: .normal) closeButton.tintColor = .text_2 closeButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } dismiss() }), for: .touchUpInside) container.addSubview(closeButton) closeButton.snp.makeConstraints { make in make.top.equalToSuperview().offset(10) make.trailing.equalToSuperview().offset(-10) make.width.height.equalTo(24) } messageViews.axis = .vertical messageViews.spacing = 10 container.addSubview(messageViews) messageViews.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(24) make.top.equalToSuperview().offset(30) } titleLabel.font = .heading_h3 titleLabel.textColor = .text_4 titleLabel.textAlignment = .center titleLabel.numberOfLines = 0 messageViews.addArrangedSubview(titleLabel) messageLabel.font = .body_m messageLabel.textColor = .text_4 messageLabel.textAlignment = .center messageLabel.numberOfLines = 0 messageViews.addArrangedSubview(messageLabel) buttonViews.axis = .vertical buttonViews.spacing = 16 container.addSubview(buttonViews) buttonViews.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(50) make.top.equalTo(messageViews.snp.bottom).offset(16) make.bottom.equalToSuperview().offset(-30) } } private func buildConfirmButton() -> UIButton { let button = UIButton() button.setBackgroundImage(.primary_8, for: .normal) button.setTitleColor(.text_1, for: .normal) button.titleLabel?.font = .heading_h4 button.layer.cornerRadius = 18 button.clipsToBounds = true button.snp.makeConstraints { make in make.height.equalTo(36) } return button } private func buildCancelButton() -> UIButton { let button = UIButton() button.setTitleColor(.text_4, for: .normal) button.titleLabel?.font = .body_m button.layer.cornerRadius = 18 button.layer.borderWidth = 1 button.layer.borderColor = UIColor.text_3.cgColor button.clipsToBounds = true button.snp.makeConstraints { make in make.height.equalTo(36) } return button } } #if DEBUG import SwiftUI struct LNCommonAlertViewPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let button = UIButton() button.backgroundColor = .red button.addAction(UIAction(handler: { [weak container] _ in guard let container else { return } let view = LNCommonAlertView() view.popup(container) }), for: .touchUpInside) container.addSubview(button) button.snp.makeConstraints { make in make.center.equalToSuperview() make.width.height.equalTo(50) } return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNCommonAlertViewPreview() }) #endif