
나는 일기 작성 화면(DiaryEditorViewController) 에서 날짜 셀을 누르면
CustomCalendarViewController 를 popover 형태로 띄우고 있었지:
cell.onTapDate = { [weak self] in
guard let self else { return }
let calendarVC = CustomCalendarViewController(initializedDate: diaryEditorVM.diary.createdAt) { selectedDate in
cell.configure(date: selectedDate)
self.diaryEditorVM.diary.createdAt = selectedDate
}
calendarVC.modalPresentationStyle = .popover
calendarVC.preferredContentSize = CGSize(width: 320, height: 280)
if let popover = calendarVC.popoverPresentationController {
popover.sourceView = self.view
popover.sourceRect = CGRect(
x: self.view.bounds.midX,
y: self.view.bounds.midY,
width: 0,
height: 0
)
popover.delegate = self
popover.permittedArrowDirections = []
}
self.present(calendarVC, animated: true)
}
여기까지는 잘 동작하는데,
캘린더가 뜰 때 뒤 배경을 살짝 어둡게(dimming) 만들고 싶은 욕심이 생김.
문제는…
modalPresentationStyle = .popover 인 경우, UIKit이 기본으로 “딤 배경”을 안 깔아준다.
그냥 뒤쪽이 투명하게 보인다.
그래서 배경을 어둡게 하고 싶으면 직접 구현해야 한다.
첫 번째 시도: CustomCalendarViewController 안에 dimView 넣기
처음 제안은 아주 단순했어.
class CustomCalendarViewController: UIViewController {
private let dimView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.3)
view.alpha = 0
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(dimView)
dimView.frame = view.bounds
UIView.animate(withDuration: 0.2) {
self.dimView.alpha = 1
}
}
}
❗️CustomCalendarViewController는 “공용”으로 여러 화면에서 재사용할 예정인데,
거기 안에 dimView를 넣어버리면 캘린더가 자기 뒤 배경까지 책임지는 이상한 구조가 되어버린다.
그래서 dim 처리는 캘린더 내부가 아니라, “캘린더를 띄우는 쪽(부모 ViewController)”에서 관리하는 게 더 맞는 구조라는 결론이 나왔다.
최종 설계: “딤 배경”은 Presenting ViewController가 관리한다
우리가 최종적으로 선택한 방식은:
📌 공통으로 쓸 수 있는 UIViewController Extension을 만들고,
필요한 화면에서 showDim(), hideDim()만 호출해서 배경을 어둡게/원래대로 만들기
UIViewController Extension으로 dimView 공통화
import UIKit
extension UIViewController {
// 딤 뷰를 찾기 위한 고유 태그
private var dimViewTag: Int { 987654 }
/// 배경을 어둡게(딤) 깔기
func showDim(animated: Bool = true, alpha: CGFloat = 0.35) {
// 이미 dimView가 있으면 다시 만들지 않음
if let _ = view.viewWithTag(dimViewTag) { return }
let dim = UIView(frame: view.bounds)
dim.backgroundColor = UIColor.black.withAlphaComponent(alpha)
dim.alpha = 0
dim.tag = dimViewTag
view.addSubview(dim)
if animated {
UIView.animate(withDuration: 0.25) {
dim.alpha = 1
}
} else {
dim.alpha = 1
}
}
/// 깔려 있는 딤 배경 제거
func hideDim(animated: Bool = true) {
guard let dim = view.viewWithTag(dimViewTag) else { return }
if animated {
UIView.animate(withDuration: 0.25, animations: {
dim.alpha = 0
}) { _ in
dim.removeFromSuperview()
}
} else {
dim.removeFromSuperview()
}
}
}
private var dimViewTag: Int { 987654 }
UIView에는 tag: Int 라는 속성이 있어서,
-> view.viewWithTag(숫자) 로 나중에 다시 찾을 수 있음
우리는 딤 배경 뷰를 나중에 다시 찾아서 제거해야 하니까
이 뷰에 고유한 tag 값을 하나 박아둔 것이야.
숫자 자체에는 특별한 의미는 없고,
-> 다른 뷰들과 겹치지 않을 것 같은 “적당히 큰 값”을 넣어준 것뿐이야.
-> 예: 999, 123456, 987654 등 아무거나 상관 없음
중요한 건 계속 일관되게 같은 숫자를 써야 한다는 점.
showDim() → 새로운 dimView를 만들고 tag = 987654로 설정
hideDim() → view.viewWithTag(987654)로 찾아서 제거
실제 적용 예시: Date 셀 눌렀을 때 dim + popover 띄우기
이제 이걸 DiaryEditorViewController 에 적용해 보자.
case .date:
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: DiaryDateCell.reuseIdentifier,
for: indexPath
) as? DiaryDateCell else {
return UICollectionViewCell()
}
cell.configure(date: Date())
cell.onTapDate = { [weak self] in
guard let self else { return }
// 🔥 1) 딤 배경 깔기
self.showDim()
// 🔥 2) 캘린더 VC 생성
let calendarVC = CustomCalendarViewController(initializedDate: diaryEditorVM.diary.createdAt) { selectedDate in
print("선택된 날짜:", selectedDate)
cell.configure(date: selectedDate)
self.diaryEditorVM.diary.createdAt = selectedDate
}
calendarVC.modalPresentationStyle = .popover
calendarVC.preferredContentSize = CGSize(width: 320, height: 280)
if let popover = calendarVC.popoverPresentationController {
popover.sourceView = self.view
popover.sourceRect = CGRect(
x: self.view.bounds.midX,
y: self.view.bounds.midY,
width: 0,
height: 0
)
popover.delegate = self
popover.permittedArrowDirections = []
}
// 🔥 3) 캘린더 표시
self.present(calendarVC, animated: true)
}
return cell
마무리
요약하면, 이 글의 핵심은:
1. popover는 기본적으로 배경 딤 처리가 없다.
2. 딤 처리를 캘린더 VC 내부에 넣어버리면 “공용 컴포넌트”로 쓰기 불편해진다.
3. 그래서 UIViewController Extension으로 dimView를 관리하는 공통 함수를 만들고,
-> showDim()으로 깔고
-> hideDim()으로 지우는 흐름으로 구현했다.
4. tag는 딤 뷰를 다시 찾고 제거하기 위한 식별자일 뿐이고,
-> 다른 뷰와 겹치지 않을 만큼 유니크하게만 정해주면 된다.
'감정일기(가칭)' 카테고리의 다른 글
| 👉 주어진 날짜(date)가 속한 “주(week)”의 시작 요일(일요일)을 구하는 함수 & 일요일 ~ 토요일까지의 범위(DateInterval)를 생성하는 함수 (0) | 2025.11.21 |
|---|---|
| ✅ 주간 (일 ~ 토) 날짜를 요일 enum으로 매핑해주는 함수 (0) | 2025.11.21 |
| 📌 화면 진입 시 감정 선택창 자동 오픈하기, 저장 완료 후 화면 닫기 처리 (0) | 2025.11.18 |
| 📑 DiaryEditor 유효성 검사 설계부터 UI 처리까지 (0) | 2025.11.18 |
| 📘 DiaryContentCell 리팩토링 기록 — 단일 String 통합 방식에서 → 구조체 기반 다중 필드 관리로 개선하기 (0) | 2025.11.17 |