티스토리 뷰
🚨 문제상황
Xcode 15.3 + iOS 18.2에서 scrollview 내부에 gesture가 있으면 스크롤 뷰가 먹히지 않아 화면이 스크롤되지 않는 버그가 존재하는 걸 발견했다.
구글링 해서 찾아보니 xcode 버그 같긴 한데 testflight로 배포된 핸드폰에선 또 잘 작동하고, iOS 17.5 버전의 시뮬레이터는 잘 작동되었다,,
(왜 18.2만 안되는 거지)
문제가 되는 코드는 아래와 같다.
.gesture(
DragGesture()
.onChanged { value in
withAnimation {
if value.translation.width < 0, isMyChat { // 내 채팅방일 때만 스와이프 가능
offset = max(value.translation.width, -110)
isShowingDeleteButton = true
} else {
offset = 0
isShowingDeleteButton = false
}
}
}
.onEnded { value in
withAnimation {
if value.translation.width < -80, isMyChat {
// 스와이프가 80pt 이상이면 삭제 버튼 표시
offset = -90 * DynamicSizeFactor.factor()
isShowingDeleteButton = true
} else {
// 그렇지 않으면 원래 위치로 복구
offset = 0
isShowingDeleteButton = false
}
}
}
)
위에 작성된 코드는 스와이프 기능 자체적으로 custom 하여 만든 것이다.
.swipeActions를 사용해서 스와이프 기능을 구현할 수 있지만 iOS 15.0 이상 버전부터 가능하여 최소 지원 버전을 14.0 이상으로 타깃을 잡은 우리 앱에서는 사용할 수 없었다.
따라서 나와 같이 custom으로 스와이프 기능을 만들게 되었다면 아마 나와 같은 문제를 겪었을 것이다!
✏️ 시도해 본 방법
우선 내가 해결해야 하는 부분은 아래 사항과 같다.
- 스크롤 뷰가 작동해야 함
- 슬라이드 했을 때 나가기라는 버튼이 gesture로 표시돼야 함(슬라이드 기능이 돼야 함)
- 셀을 탭 했을 때 정상적으로 NavigationLink가 연결돼야 함
1. .gesture 대신 .simultaneousGesture를 사용
stackOverflow에도 비슷한 사례가 있었다.
제스처를 동시에 인식하게 하는 simultaneoutGesture을 사용하고, 제스처 간의 우선순위를 정하는 highPriorityGesture로 제스처의 우선순위를 정해주도록 코드를 작성하였다.
.simultaneousGesture(gesture)
.highPriorityGesture(
TapGesture()
.exclusively(before: gesture)
)
.gesture(gesture) 대신 위 코드처럼 사용하니 스크롤은 가능했지만 버그가 두 가지나 발생했다
- 스크롤과 스와이프는 가능하지만 NavigationLink가 작동하지 않음
- 스와이프를 하면 제스처를 동시에 인식해서 스와이프가 시작되는 동시에 NavigationLink가 연결돼서 다른 뷰를 보여줌
내가 봤던 StackOverflow에서는 스크롤 + 스와이프만 가능하면 됐기 때문에 위 방법을 사용해도 상관없었지만 나 같은 경우는 셀을 클릭했을 때 NavigationLink가 연결됐었어야 했기에 저 코드로는 문제를 해결하지 못했었다.
🛠️ 해결 방법
그럼 어떻게 해결했을까!!? 해결방법은 생각보다 간단했다.
.gesture을 사용하되 대신 DragGesture(minimumDistance: 20)으로 설정하니 해결되었다.
정확한 원인은 apple에서 따로 설명해주지 않아서 모르겠지만 아마 Gesture와 ScrollView사이에 충돌이 발생한 거 같다.
SwiftUI에서 DragGesture는 한 번 터치가 감지되면 바로 drag 이벤트를 활성화하게 된다. 그 말은 사용자가 화면을 터치한 순간부터 dragGesture가 활성화되어 scrollview가 스크롤 기회를 얻지 못하게 되기 때문에 충돌이 나게 되면서 수직 스크롤이 막혀버리는 것이다.
그래서 minimumDistance를 설정하여 사용자가 20포인트 이상 움직이기 전까지는 드래그 이벤트를 발생하지 않도록 하여 scrollview와 gesture 간의 어떤 제스처를 활성화할 건지 판단할 시간이 생기면서 스크롤 이벤트를 처리할 수 있게 되는 것이다!
예를 들어 사용자가 셀을 수직으로 드래그할 때는 dragGesutre가 감지되지 않고 scrollview가 먼저 이벤트를 받아서 정상적인 스크롤이 가능하게 하도록 하며, 사용자가 20포인트 이상의 크게 드래그를 할 경우 gesture가 작동되도록 하는 방식이다.
🧐 왜 minimumDistance를 최솟값을 20 이상으로 설정해야 할까?
DragGesture() 안에 매개변수로 사용할 수 있는 minimumDistance는 제스처가 시작되기 전에 제스쳐가 성공하기 위한 최소 드래그 거리를 의미한다.
주로 기본값은 10인데, 기본값을 주게 될 경우 사용자가 손가락을 약간만 움직여도 제스처로 인식되기 때문에 스와이프 동작이랑 탭 제스처랑 구분이 되지 않아 충돌하여 처리되지 않을 가능성이 높다.
그래서 최솟값을 20 정도로 적절한 값을 주어 스크롤과 제스처 간의 충돌이 발생하지 않도록 방지해 주기 때문이다!
성공한 버전
시뮬레이터로 다시 확인해 봤을 땐 스크롤도 잘 되고 드래그도 잘 되는 것을 확인할 수 있다~
🛠️ scrollview + gesture 버그 해결 by yanni13 · Pull Request #302 · CollaBu/pennyway-client-ios
작업 이유 scrollview + gesture 버그 해결 작업 사항 1️⃣ scrollview + gesture 버그 해결 xcode 15.3 + iOS 18.2 시뮬레이터에서 scrollview내부에 gesture를 사용하는게 일부 버그가 있는 듯하여 스크롤 뷰가 작동
github.com
위에 PR에 있는 코드를 참고해 보는 것도 좋을 거 같다!
참고
'iOS > SwiftUI' 카테고리의 다른 글
[iOS] VisionKit로 문서 스캔하기 (2) | 2025.02.27 |
---|---|
[iOS/SwiftUI] Debounce 사용해보기 (0) | 2024.08.25 |
[iOS] 앨범/카메라 접근 권한 및 datepicker 연동 (0) | 2024.08.11 |
[iOS/SwiftUI] .background()에 NavigationLink를 중첩시키기 (0) | 2024.08.03 |
[iOS/SwiftUI] Custom Week Calendar 구현하기 (0) | 2024.07.27 |
- Total
- Today
- Yesterday
- 프로그래머스
- Fastlane
- wwdc25
- mergeconflict
- SWIFT
- XCTest
- 코딩테스트
- asyne-let
- mlmodel
- Xcode
- unstructed task
- swiftUI
- detached task
- 16173
- 클로저
- UIKit
- 스위프트
- combine
- Task
- ObservableObject
- rxswift
- group tasks
- CoreData
- foundation models
- securefield
- 백준
- closure
- imagepicker
- 병합충돌
- ios
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |