Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 19 additions & 35 deletions ACON-iOS/ACON-iOS.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions ACON-iOS/ACON-iOS/Global/Extensions/UIViewController+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,30 @@ extension UIViewController {
self.present(vc, animated: true)
}


// MARK: - 커스텀 알럿

func presentACAlert(_ customAlertType: ACAlertType,
longAction: (() -> Void)? = nil,
leftAction: (() -> Void)? = nil,
rightAction: (() -> Void)? = nil,
from vc: UIViewController? = nil) {
let alertVC = ACAlertViewController(customAlertType)

alertVC.do {
$0.onLongButtonTapped = longAction
$0.onLeftButtonTapped = leftAction
$0.onRightButtonTapped = rightAction

$0.modalPresentationStyle = .overFullScreen
$0.modalTransitionStyle = .crossDissolve
}

if let vc = vc {
vc.present(alertVC, animated: true)
} else {
self.present(alertVC, animated: true)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// CustomAlertActions.swift
// ACON-iOS
//
// Created by 이수민 on 5/20/25.
//

import UIKit

class ACAlertActionType {

// NOTE: - 알럿에 자주 사용되는 액션 클로저

static let openSettings = {
guard let settingsURL = URL(string: UIApplication.openSettingsURLString),
UIApplication.shared.canOpenURL(settingsURL) else { return }
UIApplication.shared.open(settingsURL, options: [:], completionHandler: nil)
}

static let goToTabBar = {
if let sceneDelegate = UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate {
sceneDelegate.window?.rootViewController = ACTabBarController()
}
}

}
113 changes: 113 additions & 0 deletions ACON-iOS/ACON-iOS/Global/UIComponents/ACAlert/Type/ACAlertType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// CustomAlertType.swift
// ACON-iOS
//
// Created by 이수민 on 5/20/25.
//

import Foundation

enum ACAlertType: CaseIterable {

case essentialUpdate // NOTE: 강제업데이트
case plainUpdate // NOTE: 일반업데이트

case locationAccessDenied // NOTE: 위치 권한 X
case locationAccessFail // NOTE: 위치 인식 실패 - 외국, GPS 지원 공간 X

case reviewLocationFail // NOTE: 리뷰 위치 인증 실패

case libraryAccessDenied // NOTE: 사진 권한 X
case changeNotSaved // NOTE: 프로필 변경사항 저장 X

case changeVerifiedArea // NOTE: 지역인증 변경 (지역 1개)

case logout // NOTE: 로그아웃

var title: String {
switch self {
case .essentialUpdate:
return "필수적인 업데이트가 있습니다"
case .plainUpdate:
return "최신 버전 업데이트가 있습니다"
case .locationAccessDenied:
return "'Acon'에 대한 위치접근\n권한이 없습니다."
case .locationAccessFail, .reviewLocationFail:
return "위치 인식 실패"

case .libraryAccessDenied:
return "'Acon'에 대한 라이브러리\n읽기/쓰기 기능이 없습니다."
case .changeNotSaved:
return "변경사항이 저장되지 않았습니다."

case .changeVerifiedArea:
return "지역 삭제 불가"

case .logout:
return "로그아웃 하시겠어요?"
}
}

var description: String? {
switch self {
case .locationAccessDenied:
return "설정에서 위치접근 권한을 허용해주세요."
case .locationAccessFail:
return "문제가 발생했습니다.\n나중에 다시 시도해주세요."
case .reviewLocationFail:
return "현재 위치와 리뷰 등록 장소가\n오차범위 밖에 있습니다.\n좀 더 가까이 이동해보세요."
case .libraryAccessDenied:
return "설정에서 권한을 켤 수 있습니다."
case .changeVerifiedArea:
return "지역은 최소 1개 이상을 등록해야 해요.\n현재 설정된 지역을 변경할까요?"
default:
return nil
}
}

var longButtonTitle: String? {
switch self {
case .essentialUpdate:
return "업데이트"
case .locationAccessDenied:
return "설정으로 가기"
case .changeVerifiedArea, .locationAccessFail, .reviewLocationFail:
return "확인"
default:
return nil
}
}

var leftButtonTitle: String? {
switch self {
case .plainUpdate, .libraryAccessDenied, .changeVerifiedArea, .logout:
return "취소"
case .changeNotSaved:
return "계속 작성"
default:
return nil
}
}

var rightButtonTitle: String? {
switch self {
case .plainUpdate:
return "업데이트"
case .libraryAccessDenied:
return "설정으로 가기"
case .changeNotSaved:
return "나가기"
case .changeVerifiedArea:
return "변경하기"
case .logout:
return "로그아웃"
default:
return nil
}
}

var isDangerButton: Bool {
return false
}

}
185 changes: 185 additions & 0 deletions ACON-iOS/ACON-iOS/Global/UIComponents/ACAlert/View/ACAlertView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
//
// CustomAlertView.swift
// ACON-iOS
//
// Created by 이수민 on 5/20/25.
//

import UIKit

final class ACAlertView: BaseView {

// MARK: - UI Properties

private let alertContainerView = GlassmorphismView(.alertGlass)

private let titleLabel = UILabel()

private let descriptionLabel = UILabel()

private let horizontalLineView: UIView = UIView()

let longButton = UIButton()

private let verticalLineView: UIView = UIView()

let leftButton = UIButton()

let rightButton = UIButton()


// MARK: - Lifecycle

override func setHierarchy() {
super.setHierarchy()

self.addSubview(alertContainerView)
alertContainerView.addSubviews(titleLabel,
descriptionLabel,
horizontalLineView,
longButton,
leftButton,
verticalLineView,
rightButton)
}

override func setLayout() {
super.setLayout()

alertContainerView.snp.makeConstraints {
$0.center.equalToSuperview()
$0.width.equalTo(ScreenUtils.widthRatio * 270)
$0.height.greaterThanOrEqualTo(ScreenUtils.heightRatio * 114)
}

titleLabel.snp.makeConstraints {
$0.top.equalToSuperview().inset(ScreenUtils.heightRatio*24)
$0.horizontalEdges.equalToSuperview().inset(ScreenUtils.widthRatio * 16)
}

descriptionLabel.snp.makeConstraints {
$0.top.equalTo(titleLabel.snp.bottom).offset(ScreenUtils.heightRatio * 8)
$0.horizontalEdges.equalToSuperview().inset(ScreenUtils.widthRatio * 16)
}

horizontalLineView.snp.makeConstraints {
$0.top.equalTo(titleLabel.snp.bottom).offset(ScreenUtils.heightRatio * 20)
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(1)
}

longButton.snp.makeConstraints {
$0.top.equalTo(horizontalLineView.snp.bottom)
$0.bottom.equalToSuperview()
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(ScreenUtils.heightRatio * 43)
}

leftButton.snp.makeConstraints {
$0.top.equalTo(horizontalLineView.snp.bottom)
$0.leading.bottom.equalToSuperview()
$0.width.equalTo((ScreenUtils.widthRatio * 270 - 1)/2)
$0.height.equalTo(ScreenUtils.heightRatio * 43)
}

verticalLineView.snp.makeConstraints {
$0.top.equalTo(horizontalLineView.snp.bottom)
$0.bottom.equalToSuperview()
$0.centerX.equalToSuperview()
$0.height.equalTo(ScreenUtils.heightRatio * 43)
$0.width.equalTo(1)
}

rightButton.snp.makeConstraints {
$0.top.equalTo(horizontalLineView.snp.bottom)
$0.trailing.bottom.equalToSuperview()
$0.width.equalTo((ScreenUtils.widthRatio * 270 - 1)/2)
$0.height.equalTo(ScreenUtils.heightRatio * 43)
}
}

override func setStyle() {
super.setStyle()

self.backgroundColor = .dimDefault

alertContainerView.do {
$0.layer.cornerRadius = 14
$0.clipsToBounds = true
}

[horizontalLineView, verticalLineView].forEach {
$0.backgroundColor = .gray100.withAlphaComponent(0.2)
}

[descriptionLabel,
longButton,
leftButton,
rightButton,
verticalLineView].forEach {
$0.isHidden = true
}
}

}


// MARK: - Configuration

extension ACAlertView {

func configure(title: String,
description: String?,
longButtonTitle: String?,
leftButtonTitle: String?,
rightButtonTitle: String?,
isDangerButton: Bool = false) {

titleLabel.setLabel(text: title,
style: .t4SB,
alignment: .center,
numberOfLines: 0)

if let description = description {
descriptionLabel.isHidden = false

descriptionLabel.setLabel(text: description,
style: .b1R,
color: .gray200,
alignment: .center,
numberOfLines: 0)

horizontalLineView.snp.remakeConstraints {
$0.top.equalTo(descriptionLabel.snp.bottom).offset(ScreenUtils.heightRatio * 20)
$0.horizontalEdges.equalToSuperview()
$0.height.equalTo(1)
}
}

if let longButtonTitle = longButtonTitle {
longButton.isHidden = false

longButton.setAttributedTitle(text: longButtonTitle,
style: .t4SB,
color: .labelAction)
}

if let leftButtonTitle = leftButtonTitle,
let rightButtonTitle = rightButtonTitle {

[leftButton, rightButton, verticalLineView].forEach { $0.isHidden = false }

leftButton.setAttributedTitle(text: leftButtonTitle,
style: .t4R,
color: isDangerButton ? .labelAction : .acWhite)

rightButton.setAttributedTitle(text: rightButtonTitle,
style: .t4SB,
color: isDangerButton ? .labelDanger : .labelAction)
}

self.alertContainerView.setNeedsUpdateConstraints()
self.alertContainerView.updateConstraintsIfNeeded()
}

}
Loading