SwiftUI 제스처가 동작하지 않는 문제 해결 방법

SwiftUI 제스처

SwiftUI를 사용해서 앱을 만들 때, .onTapGesture(), .gesture() 등 기본적인 제스처들을 통해 다양한 사용자 상호작용을 구현할 수 있다. 버튼 요소도 많이 쓰이지만, 제스처 속성도 상당히 많이 쓰이는 코드 중에 하나다.

보통 제스처를 통해 한 번 탭 했을 때의 동작을 정의한다던가. 아래와 같이 길게 화면의 요소를 탭 했을 때의 동작을 정의한다.

우선 간단하게 SwiftUI에서 .gesture()의 코드를 사용하는 예시를 보자.

SwiftU의 기본 제스처 사용법

1. TapGesture: 화면을 탭했을 때 동작을 정의
Swift
.onTapGesture(count: 2) {
    print("더블 탭 감지됨")
}
2. LongPressGesture: 길게 누르는 동작을 정의
Swift
.gesture(
    LongPressGesture(minimumDuration: 1.0)
        .onEnded { _ in
            isPressed.toggle()
        }
)
3. DragGesture: 드래그 동작을 정의
Swift
.gesture(
    DragGesture()
        .onChanged { value in
            offset = value.translation
        }
        .onEnded { value in
            withAnimation(.spring()) {
                offset = .zero
            }
        }
)

그런데, List안에서와 ScrollView안에서는 일반 SwiftUI 제스처 동작이 동작하지 않는다.

아무래도 ListScrollView의 기본 동작에 제스처가 들어있기 때문에 다른 동작을 무시하는 것 같다. 왜냐하면 SwiftUI에서는 제스처 간에 우선순위가 있기 때문이다. 이로 인해 충돌이 발생할 수 있다.

이럴 때 SwiftUI에 이미 사용할 수 있는 제스처가 정의되어 있다.

바로 simultaneousGesture 이다.

SwiftUI simultaneousGesture 사용 방법

simultaneousGesture 공식 문서 보기

Swift
.simultaneousGesture(
    TapGesture()
        .onEnded {
            tapCount += 1
            print("Tapped \(tapCount) times!")
        }
)

위 코드 예시와 같이 SwiftUI에서 .gesture가 아닌 .simultaneousGesture로 사용하면 제스처를 동작하게 할 수 있다. 기본 제스처와 사용자 정의 제스처가 동시에 동작한다.

단순히 그냥 제스처를 넣는것 뿐만 아니라 제스처와 애니메이션을 결합하여 더 풍부한 사용자 경험을 제공할 수 있다.

또는 List 내에서 항목을 터치했을 때 상세 정보로 이동하면서, 길게 눌렀을 때는 컨텍스트 메뉴를 표시하는 패턴을 구현할 수도 있다.

아니면, 리스트 안에서 navigationLink를 넣을 때 동시에 애니메이션 효과나 햅틱 피드백 효과를 주고 싶을 수 있다. 그럴 때 일반 제스처를 이용하면 navigationLink의 우선순위 때문에 동작하지 않지만 simultaneousGesture를 사용하여 애니메이션이나 햅틱 피드백 효과를 줄 수 있다.

simultaneous의 뜻을 살펴보자.

simultaneous 뜻

일반 제스처가 아닌 동시에 일어나는, 동시의, 동시에 존재하는 프레임워크에 미리 정의되어있는 SwiftUI 제스처다.

그래서 다른 기본적인 제스처나 내장된 제스처와 함께 사용할 때에도 동작이 무시되지 않고 잘 동작한다.

SwiftUI 제스처를 마스터하면 사용자에게 더 직관적이고 매력적인 앱 경험을 제공할 수 있다.

다만 simultaneousGesture 남발하면 여러 동작이 너무 동시에 일어나는 바람에 사용자가 가로로 스크롤 하고 싶었지만 세로로 스크롤도 동시에 되어버리는 일이 발생하여 사용자의 경험을 해치니 꼭 필요한 곳에만 사용하는 것이 좋다.