반응형

👋 들어가기 전
swiftUI를 하면서, 당황스러운 경험을 겪어서 한번 적어보려한다.
상황은 다음과 같다.
🧑💻: 나 너네 네비바 말고 내가 만들어 쓸래 가능?
🍎: ㅇㅇ 그럼 우리꺼 숨겨
🧑💻: ㅇㅋ!
🧑💻: 어 잠만, 숨기니깐 왜 swipe pop 기능이 안돼?
🍎: 우리꺼 안쓴다며
🧑💻: 아니 네비바만 안쓴다고, swipe pop 기능은 쓸꺼야
🍎: 안돼, 그것도 니가 만들어 쓰셈
🧑💻: ... 😭
🏁 학습할 내용
- 상황 설명
- 해결 과정
- UIKit 힘 빌리기
- 커스텀 뷰 만들기
기본 형태
toolbar를 숨기지 않을 때, swipe pop이 너무 잘 동작한다.

toolbar(.hidden)

다음 함수를 써서, Visibility를 hidden 처리하면, 아무리 swipe을 해도 동작하지 않는다.

🌊 해결과정
SwiftUI는 SwiftUI 자체로 해결을 못하면, 자연럽게 UIKit쪽까지 고려를 해봐야한다.
이 문제 역시 마찬가지다.
🧰 UIKit 힘 빌리기
준비물은 다음과 같다.
- UIViewRepresentable: UIView를 사용하기위해
- UIPanGestureRecognizer: interactivePopGestureRecognizer에 동작을 가로챌 gesture
BackgroundSwipePopView.swift
PanGesture를 인식하기위해 뒤에 몰래 숨겨 놓는 뷰다.
이게 바로 킥이다.
import SwiftUI
import UIKit
struct BackgroundSwipePopView: UIViewRepresentable {
@Binding var gesture: UIPanGestureRecognizer
func makeUIView(context: Context) -> UIView {
return UIView()
}
func updateUIView(_ uiView: UIView, context: Context) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
if let parentVC = uiView.parentViewController {
if let navigationController = parentVC.navigationController {
if let _ = navigationController.view.gestureRecognizers?.first(where: { $0.name == gesture.name }) {
// Already added
} else {
navigationController.addFullSwipeGesture(gesture)
}
}
}
}
}
}
fileprivate extension UINavigationController {
func addFullSwipeGesture(_ gesture: UIPanGestureRecognizer) {
let key = "targets"
guard let gestureSelector = interactivePopGestureRecognizer?.value(forKey: key) else { return }
gesture.setValue(gestureSelector, forKey: key)
view.addGestureRecognizer(gesture)
}
}
fileprivate extension UIView {
//responder chain을 따라 next를 순차적으로 타고 올라가면서 UIViewController를 탐색
var parentViewController: UIViewController? {
sequence(first: self) {
$0.next
}.first { $0 is UIViewController } as? UIViewController
}
}
func UpdateUIView
- SwiftUI에서 이 뷰가 업데이트될 때마다 호출
- DispatchQueue.main.asyncAfter(deadline: .now() + 1):
- 살짝 지연시켜서 NavigationController가 완전히 생성된 이후에 실행
- UIView의 parentViewController를 찾고 , parentViewController의 navigationController를 찾는다.
- 이후 navigationController에 외부에서 받은 gesture를 등록한다,
func addFullswipeGesture
- interactivePopGestureRecognizer의 내부 target-action("targets" key)을 가져와서 새로운 제스처에 그대로 복사
- 이로써 gesture도 같은 방식으로 작동하도록 만듬
📚 커스텀 네비게이션 스택 만들기
위에서 만든 뷰를 뷔에 숨기는 컨테이너 뷰가 필요하다.
import SwiftUI
import UIKit
public struct SwipePopNavigationStack<Content: View>: View {
private let content: () -> Content
@State private var gesture: UIPanGestureRecognizer = {
let gesture = UIPanGestureRecognizer()
gesture.name = UUID().uuidString
gesture.isEnabled = true
return gesture
}()
public init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
public var body: some View {
NavigationStack {
content()
.background {
BackgroundSwipePopView(gesture: $gesture)
}
}
}
}
✅ 결과

😂 쉬운 방법을 나중에 찾음
다음 코드를 사용하면, back버튼을 지워도 시스템에서 지원하는 swipe pop을 사용할 수 있다.
extension UINavigationController: @retroactive UIGestureRecognizerDelegate {
override open func viewDidLoad() {
super.viewDidLoad()
interactivePopGestureRecognizer?.delegate = self
}
public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return viewControllers.count > 1
}
}
출처
반응형
'iOS > SwiftUI' 카테고리의 다른 글
| 스유에서 lineHeight과 letterSpacing 적용하기 (2) | 2025.07.22 |
|---|---|
| .contentShape (0) | 2025.07.19 |
| 커스텀 뷰를 만들 때 고민점 (0) | 2025.07.05 |
| @Observable 매크로 (0) | 2025.07.03 |
| Custom Carousel 만들기 (0) | 2025.03.16 |