본문 바로가기
Dev/고민과 삽질의 기록들🤔

[삽질] BottomSheet 구현 중 시행착오 + 해결과정 (PanGesture, CGAffineTransform)

by Mintta 2023. 4. 16.

바텀시트를 구현하면서 겪언던 시행착오를 기록하고 어떻게 해결했는지 그 방법을 정리해보자.

초기방법

처음에 바텀시트뷰을 아래에 붙인다음에 높이를 Snapkit의 updateConstraint를 통해서 화면의 반까지 올리는 방법으로 해보니 아래 저장하기 버튼이 이상하게 남는 현상이 있었습니다..저장하기 버튼 뷰의 아래에 붙어있어서 화면에 처음 뜰 때부터 마지막에 사라지기 직전까지 남아있는 문제였습니다.

-> 그럼 아예 화면 띄워놓고 아래에 숨겨놓고 시작하자 !

문제의 상황

초기화면 구성

화면 구성

- 왼쪽그림부터 보면 구상한 대로 바텀시트를 화면 아래에 숨깁니다. (top을 bottom으로)

그렇게 되면 바텀시트의 초기 origin값은 (0, 812: Iphone13 mini기준)가 될 것입니다. 해당 origin값은 이 후 CGAffineTransform의 transform identity에 해당하기 때문에 앞으로도 계속 저 값을 이용하게 될 것이니 기억해둡시다 !

https://developer.apple.com/documentation/corefoundation/cgaffinetransform

 

- 화면을 아래 숨겨놓았다면 화면에 띄우는 것은 어떻게 할까?

간단히 해당 뷰의 frame의 높이만큼 위로 올려주면 됩니다 ! 좌표계에서 위로 올린다는 것은 -를 의미하기 때문에 해당 프레임의 높이만큼 빼주면 됩니다. 화면을 dismiss시키는 것은 반대로 해당 값을 더해주면 되겠죠?

작동 모습

잘 작동하는 것을 확인할 수 있습니다.

이제 PanGesture를 적용해보자

UIPanGesture에 관한 것은 해당 글에서는 다루지 않고 삽질글에서 개념에 대한 이야기까지 다루기에는 너무 길어지기에 어떻게 생각하고 해결했는지를 우선으로 적어보겠습니다.

왼: 다 뜬 상태, 오: While Panning

앞서 화면을 구성할 때에 초기 origin값이 곧 CGAffineTransform의 transform identity에 해당한다고 말했었습니다.

Panning을 하는 동안 바텀시트의 높이를 어떻게 계산할 수 있을지 오른쪽 그림에 그려보았습니다.

 

어떻게 계산하는지 보기에 앞서 UIPanGesture의 translation(in:)메서드에 대해서만 간단하게 알아보자면..

우리가 이동시키고 싶은 View를 파라미터로 넣으면 해당 View의 SuperView의 좌표계에서 우리가 이동시키고 싶은 View의 새로운 위치를 식별하는 점, CGPoint를 반환해주는 메서드입니다.

 

UIPanGestureRecognizer는 continuous reporter이기 때문에 계속해서 호출됩니다.

이 때 UIPanGestrueRecognizer의 translation을 할 때의 기준은 처음 Panning을 시작했을 때의 위치 !! 가 됩니다.

 

우리는 바텀시트가 화면에 다 올라온 이후에 드래그를 하기 때문에 처음 panning을 시작한 위치는 파란색으로 표시한 위치가 됩니다.

즉, 해당 위치가 origin (0, 0)이 되는 것입니다.

 

기준이란 단어를 계속 언급해서 헷갈릴 것 같아서 한번 정리하고 가겠습니다 ! (헷갈린다 헷갈려)

- transform(UIView의 프로퍼티)의 기준: 뷰가 처음 나타날 때 레이아웃이 모두 잡힌 직후의 origin값. (0, 812) -> View의 Bound configure
- translation(UIPanGestureRecognizer의 메서드)의 기준: 우리가 손가락을 대고 Panning(드래그) 동작을 시작한 순간의 뷰의 origin. (0, view의 높이) -> Panning시의 위치 tracking

transform: https://developer.apple.com/documentation/uikit/uiview/1622459-transform

translation: https://developer.apple.com/documentation/uikit/uipangesturerecognizer

공식 문서도 첨부해두겠습니다.

 

 

다시 돌아와서 우리가 바텀시트를 아래로 드래그를 한다면 해당 origin을 기준으로 y값은 계속 +로 나타나고, 위로 드래고한다면 -값이 나오게 됩니다.

그렇다면 viewTranslation의 y값은 우리가 드래그한 정도를 뜻하게 되겠죠 !

아래 사진을 보면 쉽게 이해가 가실겁니다

화면을 내릴 때

 

Roll back or Dismiss ?

바텀시트를 조금만 내려도 화면이 닫히면 억울할 겁니다. 이 기준을 바텀시트 프레임의 높이의 반으로 잡았습니다.

위에서 viewTranslation의 y값은 즉, 우리가 드래그한 정도를 뜻한다고 했었죠.

그렇다면 드래그한 정도가 view의 frame 높이의 반을 넘었다면 dismiss, 넘지 않았다면 다시 원래대로 roll back시켜주겠습니다.

다시 원래대로 되돌리는 것과 dismiss시키는 것은 매우 간단합니다.

원래대로 되돌리는 것은 처음에 우리가 화면을 띄우고 내렸던 방법 그대로 다시 해주면 됩니다.

transform의 기준은 처음에 말했던 (0, 812)이기 때문에 해당 위치에서 view의 높이를 뺀 것만큼 다시 되돌리면 되고,

내리는 것은 해당 높이만큼 더해주면 화면 아래로 사라지게 되고 이후에 dismiss처리해주면 됩니다.

최종 결과


이거 정리하고 글 쓰는데 이렇게 오래걸릴줄 몰랐는데 그래도 기록해놓으면 나중에 비슷한 상황이 오면 다시 찾아와서 읽으면 기억나겠죠??
아무튼 PanGesture를 활용한 바텀시트 구현에 대해서 정리해보았습니다 끝!

 

Reference

https://zeddios.tistory.com/356

https://lidium.tistory.com/21

 

댓글