Pinch Gesture를 선형적으로 다루는 방법

Pinch Gesture를 선형적으로 다루는 방법

이전글에서 TransitionLayout에 관한 글을 썼었습니다. TransitionLayout을 구현하면서 가장 먼저 부딪혔던 문제 중 하나가 핀치 제스처의 scale 값을 어떻게 자연스럽게 transitionProgress에 매핑할지였습니다. 단순히 scale 값을 그대로 사용하면 전환이 너무 급격하거나 민감하게 반응해서 UX가 부자연스러웠죠. 그래서 저는 log2 변환을 통해 이 문제를 해결해 보았습니다.

문제: 곱셈 기반의 scale과 선형적인 transitionProgress

UIPinchGestureRecognizerscale 값은 두 손가락 사이 거리의 비율로, 제스처 시작 시 1.0이고, 두 배로 벌리면 2.0, 절반으로 좁히면 0.5가 됩니다.
하지만 UICollectionViewTransitionLayouttransitionProgress는 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 네 배로 늘어난 거리

핵심 포인트

비교: Threshold 기반 전환 방식과의 차이

기존에는 특정 scale 값이 임계치를 넘으면 레이아웃을 바꾸는 방식도 고려할 수 있습니다. 예를 들어:

if gesture.scale > 1.3 {
    switchToZoomedOutLayout()
} else if gesture.scale < 0.7 {
    switchToZoomedInLayout()
}

하지만 이런 방식은 다음과 같은 한계를 가집니다.

반면 log2(scale) 방식은.