| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
- swift associated type
- reactorkit
- OAtuh 2.0
- custom ui
- swift dashed line
- swift custom ui
- task cancel
- swift existential type
- swift concurrency
- uikit toast
- RxSwift
- BFS
- DP
- task cancellation
- UIKit
- 버튼 피드백
- swift bottomsheet
- swift navigationcontroller
- swift 점선
- Tuist
- button configuration
- custom navigation bar
- custombottomsheet
- swift 백준
- swift opaque type
- SWIFT
- scene delegate
- coordinator
- identifiable
- 드롭다운 버튼
- Today
- Total
김경록의 앱 개발 여정
[Swift UIKit] Popover 드롭다운 버튼 화면 전환 버그 수정 과정 본문
개요
https://roks-apps.tistory.com/88
이전 포스팅에서 UIPopover + UITableView로 드롭다운 버튼을 만들었다가, 드롭다운이 펼쳐진 상태에서 좌측 엣지 스와이프(인터랙티브 뒤로가기) 를 하면 화면이 얼어붙는 이슈가 생겼다

- 드롭다운을 연 상태에서 뒤로가기 제스쳐를 할 경우 네비게이션 타이틀만 바뀌고 화면이 멈추는 버그
증상
- 드롭다운 버튼 탭 → 팝오버로 드롭다운 표시
- 드롭다운이 열린 상태에서 좌측 엣지 스와이프로 뒤로가기 제스처 시작
- 네비게이션 바 타이틀은 바뀌지만 뷰는 그대로, 터치도 먹지 않는 상태 발생
원인
원인 추적은 금방 가능했다 아마 화면 전환에서 문제가 생긴듯했다.
presentedViewController(팝오버)가 떠 있는 동안 UINavigationController의 인터랙티브 pop 전환이 시작되면, 두 전환이 동시에 유지되면서 내부 전환 코디네이터가 꼬인다. 결과적으로 화면이 멈춘 것처럼 보이는 상태가 된다는 결론에 도달했다.
결국
뒤로가기 제스처가 시작되기 전에 떠 있는 팝오버를 먼저 닫아야 한다.
해결
1) 제스처 시작 직전에 팝오버 닫고, 이번 제스처는 취소
interactivePopGestureRecognizer의 delegate에서 팝오버가 떠 있으면 즉시 dismiss 하고, 이번 제스처는 false로 취소한다. 사용자는 자연스럽게 한 번 더 스와이프하면 정상적으로 뒤로가기 된다.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
hookInteractivePopGesture()
}
// 일반 전환(pop/push) 중에도 안전하게 정리
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isMovingFromParent || navigationController?.transitionCoordinator != nil {
presentedViewController?.dismiss(animated: false)
}
}
private func hookInteractivePopGesture() {
guard let nc = navigationController,
let popGR = nc.interactivePopGestureRecognizer else { return }
popGR.delegate = self
popGR.isEnabled = (nc.viewControllers.count > 1)
}
}
extension BaseViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer === navigationController?.interactivePopGestureRecognizer,
presentedViewController != nil {
// 팝오버 먼저 닫고, 이번 제스처는 취소
presentedViewController?.dismiss(animated: false)
return false
}
return true
}
}
2) 전환 직전 추가 방어
viewWillDisappear(_:)에서 presentedViewController?.dismiss(animated: false)를 한 번 더 호출해 두면, 일반적인 pop/push에서도 잔여 프레젠테이션이 남지 않아 안전하다. (위 코드 포함)
체크리스트
- 제스처 시작 전에 presentedViewController 있으면 dismiss 후 제스처 취소
- viewWillDisappear에서도 한 번 더 dismiss로 잔여 프레젠테이션 제거
- 팝오버의 passthroughViews = []로 바깥 터치 전달 최소화
- 가능하면 UIButton.menu 등 시스템 풀다운 고려
마무리

네비게이션 타이틀이 변화하는걸 확인해서 다행히 원인을 빨리 찾고 해결할 수 있었다.
뒤로가기 제스처가 시작되기 전에 항상 떠 있는 팝오버를 닫는 흐름으로 바꾸니, 화면 멈춤 증상은 재현되지 않았다.
드롭다운을 팝오버로 구현해야 하는 상황이라면 위 가드 코드들을 반드시 넣어두는 걸 추천한다.
'Trouble Shooting' 카테고리의 다른 글
| [Swift UIKit] 유저 사용성을 고려한 UI의 재설계 과정 (드롭 다운 버튼) (0) | 2025.09.19 |
|---|---|
| [Swift UIKit] Button 터치 피드백이 안보이는 경우 (0) | 2025.05.02 |
| [Swift MVVM] UI와 독립적인 ViewModel, 어디까지 고려해야 할까? (feat. ReactorKit) (0) | 2025.04.17 |
| [Swift UIKit] 앱 전반에서 사용 될 Toast Message 구현기 (0) | 2025.04.16 |
| [Swift UIKit] 점선 스타일 보더 적용하기(Dashed Border Style) (0) | 2025.04.16 |