본문 바로가기
PROGRAMMING CODE/SWIFT

[SwiftUI] Drag&Drop (커스텀 뷰 기초, offset과 DragGesture)

by daye_ 2024. 2. 18.

 

 

 

Drag & Drop은 사용자의 Gesture에 따라 움직이는 뷰를 만들때 사용한다.

Custom으로 뷰를 만들 때 응용하면 아주 좋음! 

 

 

 

public struct DragGesture : Gesture { ... }

 

DragGesture에는 위 두가지 함수가 있음!

그리고 그 함수 내부에서 꺼내 사용할 수 있는 요소들은 아래와 같음!

time: 드래그 제스처의 현재 이벤트와 관련된 시간 정보.
location: 드래그 제스처의 현재 이벤트 위치.
startLocation: 드래그 제스처의 첫 번째 이벤트 위치.
translation: 드래그 제스처의 시작부터 현재 이벤트까지의 전체 이동량. (location.{x,y} - startLocation.{x,y})
velocity: 현재 드래그 속도.
predictedEndLocation: 현재 드래그 속도를 기반으로 드래그가 지금 멈췄을 때 최종 위치의 예측.
predictedEndTranslation: 현재 드래그 속도를 기반으로 드래그가 지금 멈췄을 때 최종 이동량의 예측.
== 함수: 두 DragGesture.Value 값이 동일한지 여부를 판단하는 함수.

나는 translation을 이용해서 만들어 볼 것이다~!

 

 

 

 

 

 

 

일단 귀엽게 하트 추가 ^0^~

struct DragAndDrop: View {    
    var body: some View {
    	VStack {
        	HeartView
        }
    }
    
    private var HeartView: some View {
        Image(systemName: "suit.heart.fill")
             .resizable()
             .frame(width: 70, height: 60)
             .foregroundStyle(Color.pink)
}

해당 구조체 뷰에만 쓰일거라 구조체 내부에 선언해줬음!

 

 

 

struct DragAndDrop: View { 
    @State private var dragOffSet = CGSize.zero
    @State private var currentOffSet = CGSize.zero
    
    var body: some View {
    	VStack {
            HeartView
            	.offset(CGSize(width: dragOffSet.width, height: dragOffSet.height))
        }
    }
    
    private var HeartView: some View {
        Image(systemName: "suit.heart.fill")
             .resizable()
             .frame(width: 70, height: 60)
             .foregroundStyle(Color.pink)
     }

}

현재 움직인 Offset(dragOffset)과, 움직이기 전의 Offset(currentOffset)을 저장하기 위해 변수를 선언해줌!

그리고 중간으로 zero로 넣어준다.

 

화면 중심은 0.0이고, 뷰의 offset기준은 뷰의 중심이다.

x좌표는 왼쪽은 음수 오른쪽은 양수 / y좌표는 위쪽은 음수 아래쪽은 양수

 

 

 

struct DragAndDrop: View { 
    @State private var dragOffSet = CGSize.zero
    @State private var currentOffSet = CGSize.zero
    
    var body: some View {
    	VStack {
            HeartView
            	.offset(CGSize(width: dragOffSet.width, height: dragOffSet.height))
                .gesture(
                    DragGesture()
                        .onChanged({ gesture in
                            dragOffSet = CGSize(width: gesture.translation.width + currentOffSet.width, height: gesture.translation.height + currentOffSet.height)
                        })
                        .onEnded({ gesture in
                            dragOffSet = CGSize(width: gesture.translation.width + currentOffSet.width, height: gesture.translation.height + currentOffSet.height)
                            currentOffSet = dragOffSet
                        })
                )
        }
    }
    
    private var HeartView: some View {
        Image(systemName: "suit.heart.fill")
             .resizable()
             .frame(width: 70, height: 60)
             .foregroundStyle(Color.pink)
     }

}

DragGesture를 이용해서 gesture.traslation(CGSize)를 구해준다.

그래서 원래 움직인 Offset과 현재 위치했던 Offset를 더한 후 dragOffset에 넣어주면

HeartView의 위치가 drag에 따라 변한다!

 

그리고 마지막으로 드래그가 멈췄을 때

currentOffset에 dragOffset을 저장해주면 끝!

 

 

 

멋.지.다.

 

 

 

 

import SwiftUI

struct DragAndDrop: View {
    @State private var dragOffSet = CGSize.zero
    @State private var currentOffSet = CGSize.zero
    
    var body: some View {
        VStack {
            HeartView
                .offset(CGSize(width: dragOffSet.width, height: dragOffSet.height))
                .gesture(
                    DragGesture()
                        .onChanged({ gesture in
                            calcOffset(gesture.translation)
                            print(gesture.translation)
                        })
                        .onEnded({ gesture in
                            calcOffset(gesture.translation)
                            currentOffSet = dragOffSet
                        })
                )
        }
    }
    
    private var HeartView: some View {
        Image(systemName: "suit.heart.fill")
             .resizable()
             .frame(width: 70, height: 60)
             .foregroundStyle(Color.pink)
    }
    
    private func calcOffset(_ gestureOffset: CGSize) {
        dragOffSet = CGSize(width: gestureOffset.width + currentOffSet.width, height: gestureOffset.height + currentOffSet.height)
    }
}

#Preview {
    ContentView()
}

 

마지막으로 요렇게 함수로 빼줌!