Skip to content

SwiftUI DragGesture로 구현된 버튼에서 스크롤 제스처 충돌 문제 #10

@camosss

Description

@camosss

Describe the bug

SwiftUI에서 버튼의 pressed 상태를 구현하기 위해 DragGesture를 사용할 때 발생하는 스크롤 제스처 충돌 문제

  • 스크롤 가능한 영역(ScrollView) 내에 배치된 버튼에서 사용자가 스크롤을 시도하면,
  • 버튼의 DragGesture가 스크롤 제스처를 차단하여 스크롤이 되지 않음
.gesture(
    DragGesture(minimumDistance: 0)
        .onChanged { _ in
            stateType = .pressed
        }
        .onEnded { _ in
            stateType = .normal
            action()
        } 
)

Cause

  • DragGesture(minimumDistance: 0)을 사용하여 터치 즉시 드래그 제스처가 인식됨

  • onChangedonEnded에서 이동 거리나 방향을 고려하지 않고 모든 드래그 동작을 버튼 액션으로 처리

  • 결과적으로 스크롤뷰의 제스처가 완전히 차단 -> 사용자가 의도한 스크롤 동작을 수행할 수 없음


Solution

  • .gesture() -> .simultaneousGesture()로 변경하여 스크롤과 버튼 제스처가 동시에 작동하도록 수정

  • 이동 거리 기반 로직을 추가하여 탭과 스크롤을 구분

    • onChanged: 이동 거리가 10px 미만이면 pressed 상태 유지, 10px 이상이면 pressed 해제

    • onEnded: 최종 이동 거리가 10px 미만일 때만 버튼 액션 실행

버튼을 누른 채로 드래그하면 pressed 상태가 해제되고 스크롤만 실행됨

.simultaneousGesture(
    DragGesture(minimumDistance: 0)
        .onChanged { value in
            let distance = calculateDistance(from: value.translation)

            // 임계값 10px
            if distance < 10 {
                stateType = .pressed
            } else {
                // 스크롤이 시작되면 pressed 해제
                stateType = .normal
            }
        }
        .onEnded { value in
            let distance = calculateDistance(from: value.translation)

            // pressed 상태였고 이동이 작으면 액션 실행
            if distance < 10 {
                action()
            }

            stateType = .normal
        }
)

/// CGSize에서 이동 거리를 계산
private func calculateDistance(from translation: CGSize) -> CGFloat {
    return sqrt(pow(translation.width, 2) + pow(translation.height, 2))
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions