소개
SwiftUI에서 일반 탭 외에 길게 눌렀을때의 동작을 어떻게 처리하는지 알아보겠습닌다. 먼저 Long Press라는 동작에 대한 이해가 선행되어야 합니다.
- Long Press는 누르는 동안 동작이 지속되지 않습니다.
minimum duration
(최소 지속 시간)동안 press가 되었다면 계속 누르고 있다 하더라도 minimum duration 후에 최후의 동작 (onEnded)을 1회 실행하고 끝이 납니다. - 지금은 사용하지 않는 아이폰의 3D 포스 터치와 행동 양식이 같습니다. 누르는 강도를 시간(duration)으로 치환한 것 뿐입니다.
아래 그림을 보면 일반적인 minimum duration이 1초로 설정된 Long Press의 동작이 어떤 것인지 알 수 있습니다. 일반적인 Long Press는 아무리 길게 눌러도 1초가 지나면 동작이 끝납니다. 아래 Continous Press Button은 특수한 처리를 한 경우로 구현 방법은 후술합니다.
Long Press
some Gesture
타입의 longPress
계산된 변수를 SwiftUI View 구조체 내에 추가합니다.
@GestureState private var isDetectingLongPress = false var longPress: some Gesture { LongPressGesture(minimumDuration: 1) .updating($isDetectingLongPress) { value, gestureState, _ in gestureState = value }.onEnded { value in print("LongPress OnEnded") } }
- @GestureState
- 제스처 전용 State입니다. 나중에
if
문 등에서 사용할 수 있습니다.
- 제스처 전용 State입니다. 나중에
updating
에 누르고 있을 때의 동작을 추가합니다.updating
은minimumDuration
동안 지속됩니다.
onEnded
에 동작을 완료했을 때, 즉 누른 채로minimumDuration
이 되었을 때 마무리해야 할 동작을 설정합니다.
위의 longPress
는 제스처와 관련된 다양한 곳에서 사용할 수 있습니다.
VStack { Image(systemName: isDetectingLongPress ? "globe" : "pause.fill") Button {} label: { Text("Long Press Button") }.simultaneousGesture(longPress) }
아래 그림에서 Long Press Button
이 해당 예입니다.
특수한 경우: Continuous Press
위의 예는 아무리 버튼을 길게 눌러도 minimum duration이 지나면 동작이 완료됩니다. 이것 대신 만약 버튼을 누르고 있을 때 updating이 지속되고 버튼을 떼야만 onEnded가 실행되는 것으로 하고 싶다면 어떻게 해야할까요?
sequenced gesture 기능을 이용해 Long Press 전에 Drag Gesture를 추가하면 이 문제를 해결할 수 있습니다.
@GestureState private var isDetectingContinuousPress = false var continuousPress: some Gesture { LongPressGesture(minimumDuration: 0.1) .sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local)) .updating($isDetectingContinuousPress) { value, gestureState, _ in switch value { case .second(true, nil): gestureState = true print("updating: Second") default: break } }.onEnded { value in switch value { case .second(_, _): print("onended: Second") default: break } } }
- sequenced before
DragGesture
를 첫 번째(first) 제스처로 삽입합니다.
- DragGesture
minimumDistance
를0
으로 설정하면 길게 탭한 것도 드래그로 취급됩니다.
- updating에서 case .second(First.Value, Second.value?)
- 첫 번째 제스처가 실행되었다면(
true
) 두 번째 제스처(롱프레스)에서 실행할 작업을 case
내부에 지정합니다.
- 첫 번째 제스처가 실행되었다면(
- onEnded에서 case .second(First.Value, Second.value?)
- 프레스를 종료했을 때 해야 할 작업을 지정합니다.
위의 continuousPress
제스처 또한 다양한 곳에서 사용할 수 있습니다.
VStack { Image(systemName: isDetectingContinuousPress ? "globe" : "pause.fill") Button {} label: { Text("Continuous Press Button") }.simultaneousGesture(continuousPress) }
아래 그림에서 Continuous Press Button
이 해당 예입니다.
전체 코드
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContinuousLongPressView: View { | |
@GestureState private var isDetectingLongPress = false | |
var longPress: some Gesture { | |
LongPressGesture(minimumDuration: 1) | |
.updating($isDetectingLongPress) { value, gestureState, _ in | |
gestureState = value | |
}.onEnded { value in | |
print("LongPress OnEnded") | |
} | |
} | |
@GestureState private var isDetectingContinuousPress = false | |
var continuousPress: some Gesture { | |
LongPressGesture(minimumDuration: 0.1) | |
.sequenced(before: DragGesture(minimumDistance: 0, coordinateSpace: .local)) | |
.updating($isDetectingContinuousPress) { value, gestureState, _ in | |
switch value { | |
case .second(true, nil): | |
gestureState = true | |
print("updating: Second") | |
default: | |
break | |
} | |
}.onEnded { value in | |
switch value { | |
case .second(_, _): | |
print("onended: Second") | |
default: | |
break | |
} | |
} | |
} | |
var body: some View { | |
VStack { | |
Image(systemName: isDetectingLongPress ? "globe" : "pause.fill") | |
Button {} label: { | |
Text("Long Press Button") | |
}.simultaneousGesture(longPress) | |
Divider() | |
Image(systemName: isDetectingContinuousPress ? "globe" : "pause.fill") | |
Button {} label: { | |
Text("Continuous Press Button") | |
}.simultaneousGesture(continuousPress) | |
} | |
// Bool 값이 변경되었을 때의 처리는 onChange에서 | |
// .onChange(of: isDetectingLongPress) { newValue in | |
// print("onChange", newValue) | |
// } | |
} | |
} |
0개의 댓글