적당한 고통은 희열이다

- 댄 브라운 '다빈치 코드' 중에서

Swift iOS 앱 개발/Swift

[Swift iOS] 화면 스와이프 swipe to pop / swipe back (+ RxGesture)

hongssup_ 2023. 7. 17. 10:30
반응형

uinavigationController 를 사용하면 화면 왼쪽 끝부분에서 스와이프 할 때 popViewController 처럼 이전 화면으로 넘어갈 수 있지만, 

화면 중앙이나 다른 부분에서는 스와이프를 해도 뒤로 넘어갈 수 없다. 

화면 어디서나 swipe back 을 할 수 있도록 만드는 방법을 알아보았다. 

 

카카오나 인스타그램의 경우 커스텀으로 swipe back 기능을 구현해주었는지, 

화면 어디서나 스와이프를 하면 손 위치를 따라 뷰가 같이 움직이며 따라오지만

그것까지는 아니고, 그냥 어디서든 스와이프 하면 이전 화면으로 넘어갈 수 있도록만 구현하였다. 

이거는 UISwipeGestureRecognizer 를 이용해서 간단하게 구현이 가능하다. 

 

1. UISwipeGestureRecognizer

var swipeRecognizer: UISwipeGestureRecognizer!

override func viewDidLoad() {
    super.viewDidLoad()
    swipeRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(swipeAction(_:)))
    swipeRecognizer.direction = .right
    self.view.addGestureRecognizer(swipeRecognizer)
}

@objc func swipeAction(_ sender: UISwipeGestureRecognizer) {
    if sender.direction == .right {
        self.navigationController?.popViewController(animated: true)
    }
}

 

2. RxGesture - swipe

RxGesture를 사용하면 gesture 인스턴스를 따로 생성해주지 않아도, 간단하게 rx로 처리가 가능하다. 

self.view.rx.swipeGesture(.right)
    .when(.recognized)
    .bind { [weak self] _ in
        self?.navigationController?.popViewController(animated: true)
    }
    .disposed(by: disposeBag)

swipe 제스처를 사용하면 스와이프가 잘 되기는 하는데, 제스처 감지가 너무 예민해서 살짝만 스쳐도 스와이프가 되어버리고

스와이프 제스처는 따로 제스처 감지 속도나 범위를 감지하고 커스텀으로 작동시키기가 힘들어서 UISwipeGestureRecognizer는 용도에 맞지 않는것 같아 PanGesture 를 이용하는 것으로 수정을 해보았다. 

 

** 스와이프 제스처에도 state == .end 확인을 해줬으면 바로 넘어가진 않았으려나,, 추가로 테스트를 해봐야겠다.

-> 스와이프 제스처에는 state 로 상태관리 하는 기능은 지원되지 않는 것 같다.

자세한 것은 UISwipeGestureRecognize 를 이용한 스와이프 액션을 파헤쳐보자! 글을 참고해주세욥


2023-07-21

3. RxGesture - Pan

gpt 한테 물어봤더니 다음과 같이 pan gesture를 사용하는 방법을 알려주었다. 

이렇게 하면 x 의 이동 범위와 pan 속도를 감지하여 원하는 값으로 pop view 를 구현해줄 수 있다.

그런데 위아래 스크롤인지 좌우 스와이프인지, 상하/좌우 이동을 감지하는 코드가 없어 위아래 스크롤을 할 때 오른쪽으로 살짝만 움직여도 스와이프가 되어버리는 치명적인 문제가 있었다. 

gpt 아직 더 분발해 ~~ 

let translationX = gesture.translation(in: self.view).x
let velocityX = gesture.velocity(in: self.view).x

if gesture.state == .ended {
    if translationX > 100 || velocityX > 300 {
        self.navigationController?.popViewController(animated: true)
    }
}

 

translation으로 x의 이동범위를 따로 구해주지 않아도, 
gesture.velocity 의 magnitude 라는 절대값을 이용해 좌우 스크롤을 감지하고, 제스처 속도에 따라 액션을 설정해줄 수도 있다. 

아래와 같이 설정해주니, 아아아주 잘 된다 

self.view.rx.panGesture()
      .when(.recognized)
      .filter { [weak self] _ in self?.isInteractivePopGestureEnabled ?? true }
      .bind { [weak self] gesture in
          guard let self else { return }
          
          let velocity = gesture.velocity(in: self.view)
          
          if velocity.x.magnitude > velocity.y.magnitude { //좌우
              if velocity.x > 0 {
                  if velocity.x.magnitude > 300 { //제스처 빠를 때
                      self.navigationController?.popViewController(animated: true)
                  } else { //제스처 느릴 때
                  }
              }
          }
      }
      .disposed(by: disposeBag)

굿굿! 👍🏻 

 

 


+ 가로 스크롤 컬렉션 뷰 에서 오른쪽으로 스크롤하면 swipe back 되는 문제

 

 

 


Handling swipe gestures

https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/handling_uikit_gestures/handling_swipe_gestures?language=objc 

 

728x90
반응형