iOS/SwiftUI

[SwiftUI] Carousel Card View

HarryJeonn 2022. 8. 1. 06:21

Why ?


스와이프하면 다음 카드로 이동하는 UI를 그리고 싶어서 Horizontal ScrollView를 사용했다가 스와이프하면 자동으로 중심을 맞춰주는 뷰를 구현하고 싶었다.

How ?


GeometryReader를 활용했다.

GeometryReader { proxy in
    
		// 스크롤하면 움직일 x축의 값
    let xOffsetToShift = cardWidth + spacing
    
    HStack(spacing: 10) {
        ForEach(Array(viewModel.places.enumerated()), id: \\.0) { idx, place in
            viewModel.createPlaceCard(place: place, index: idx)
        }
    }
    .padding(.leading, 22)
		// xOffsetToShift에 index를 곱해서 offset의 값을 바꿔준다.
    .offset(x: offset - (CGFloat(viewModel.cardCurrentIndex) * xOffsetToShift))
    .gesture(
        DragGesture()
            .updating($offset, body: { value, out, _ in
                out = value.translation.width
            })
						// 어느정도 스크롤을 해야 다음 카드로 넘어갈지 계산하는 부분
            .onEnded({ value in
                let offsetX = value.translation.width
                let progress = -offsetX / (proxy.size.width / 2)
                let roundIndex = progress.rounded()
                
                viewModel.cardCurrentIndex = max(min(viewModel.cardCurrentIndex + Int(roundIndex), viewModel.places.count - 1), 0)
                
                viewModel.slideCard(index)
            })
            .onChanged({ value in
                let offsetX = value.translation.width
                let progress = -offsetX / (proxy.size.width / 2)
                let roundIndex = progress.rounded()
                
                index = max(min(viewModel.cardCurrentIndex + Int(roundIndex), viewModel.places.count - 1), 0)
            })
    )
}
.frame(height: UIScreen.main.bounds.height * 0.13)
.animation(.easeInOut, value: offset == 0)

전체 코드 중 내가 생각하는 핵심 코드부분이다.

카드의 width와 spacing을 더하여 한번 스와이프하면 움직일 x 값을 구했다.

그리고 index와 곱하여 다음 카드에 맞게 계속 움직일 수 있도록 구현했다.

🤔


처음에는 남의 코드를 가져다 쓰려고 하다보니 offset값 계산하는데 애를먹었다.

남의 코드를 보고 그냥 쓰지말고 어떻게 동작하는지 이해하고 직접 계산해보니 조금 수월했다.

역시 무지성으로 가져다가 쓰면 결국 고생하는건 나다.

이해하고 사용하자!