on
Pinch Gesture를 선형적으로 다루는 방법
Pinch Gesture를 선형적으로 다루는 방법
이전글에서 TransitionLayout에 관한 글을 썼었습니다. TransitionLayout을 구현하면서 가장 먼저 부딪혔던 문제 중 하나가 핀치 제스처의 scale
값을 어떻게 자연스럽게 transitionProgress
에 매핑할지였습니다. 단순히 scale
값을 그대로 사용하면 전환이 너무 급격하거나 민감하게 반응해서 UX가 부자연스러웠죠. 그래서 저는 log2
변환을 통해 이 문제를 해결해 보았습니다.
문제: 곱셈 기반의 scale과 선형적인 transitionProgress
UIPinchGestureRecognizer
의 scale
값은 두 손가락 사이 거리의 비율로, 제스처 시작 시 1.0이고, 두 배로 벌리면 2.0, 절반으로 좁히면 0.5가 됩니다.
하지만 UICollectionViewTransitionLayout
의 transitionProgress
는 0.0에서 1.0까지 선형적으로 변화하는 값입니다.
gesture.scale | 의미 |
---|---|
0.5 | 두 손가락 거리가 절반 |
1.0 | 제스처 시작 상태 |
2.0 | 두 손가락 거리가 두 배 |
이처럼 scale
은 곱셈 기반이라, 값을 그대로 사용하면 전환 진행도가 직관적이지 않고, 사용자 감각과 맞지 않는 비선형 반응이 발생합니다.
해결: log2 변환으로 선형화
이 문제를 해결하기 위해 scale
값에 log2
를 적용했습니다.
log2
함수는 곱셈 관계를 덧셈 관계로 바꿔주기 때문에, scale
의 지수적 변화를 선형적인 값으로 변환해 줍니다.
@objc private func handlePinch(_ gesture: UIPinchGestureRecognizer) {
let logScale = log2(gesture.scale)
let targetIndex = currentIndex + round(logScale)
}
logScale
값을 기준으로 currentIndex + 1
, currentIndex - 1
, currentIndex + 2
등의 타겟 레이아웃을 유동적으로 계산할 수 있으며,
해당 구간 내에서 transitionProgress
도 자연스럽게 구할 수 있습니다. 이를 통해 제스처 감각과 시각적 전환 흐름이 훨씬 일치하게 됩니다.
scale | log2(scale) | 설명 |
---|---|---|
0.5 | -1 | 절반으로 줄어든 거리 |
1.0 | 0 | 제스처 시작 상태 |
2.0 | +1 | 두 배로 늘어난 거리 |
4.0 | +2 | 네 배로 늘어난 거리 |
핵심 포인트
scale
은 곱셈 기반이므로 선형 매핑에 적합하지 않음log2
를 사용하면 곱셈 → 덧셈 변환으로 선형화 가능transitionProgress
를 별도의 계산 없이 구할 수 있음 (ex: log2(1.5) -> 0.58)
비교: Threshold 기반 전환 방식과의 차이
기존에는 특정 scale
값이 임계치를 넘으면 레이아웃을 바꾸는 방식도 고려할 수 있습니다. 예를 들어:
if gesture.scale > 1.3 {
switchToZoomedOutLayout()
} else if gesture.scale < 0.7 {
switchToZoomedInLayout()
}
하지만 이런 방식은 다음과 같은 한계를 가집니다.
- 단계 사이 간격이 균일하지 않음: 1.3과 0.7 같은 하드코딩된 수치는 레이아웃이 여러 단계일 때 적용하기 어렵습니다.
- 제스처 흐름과의 단절: 특정 값을 넘기 전까지는 아무 일도 일어나지 않기 때문에 사용자는 전환이 먹히지 않는다고 느낄 수 있습니다.
- 전환 진행도 제어 불가: transitionProgress와 같은 중간 상태 값을 활용할 수 없어 부드러운 애니메이션이 어려워집니다.
반면 log2(scale)
방식은.
- 레이아웃 단계 사이를 정의 가능
- 제스처 진행도에 따라 자연스럽게 targetIndex와 transitionProgress를 계산
- 중간 상태의 연속적인 전환을 표현할 수 있어 UX가 부드럽고 직관적입니다.