일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Animation
- GCD
- Network
- arkit
- auth
- combine
- 최적화
- WWDC
- 달력
- swift
- gesture
- state
- Performance
- stateobject
- dataflow
- firebase
- 접근성제어
- authentication
- SwiftUI
- avsession
- 네트워크
- withAnimation
- ios
- Concurrency
- iphone
- RxSwift
- UIKit
- toolbarvisibility
- CS
- view
- Today
- Total
XLOG
[SwiftUI] 캘린더용 InfinityScroll 로직에 대한 아이디어 본문
작년에 SwiftUI를 공부하면서 캘린더를 만들어야 하는 상황이 있었다.
단순히 버튼을 눌러 값을 변화시키는 것이라면 조금은 간단할 수 있지만, 기본 제공 달력 처럼 Scroll Animation 효과가 있어야 한다.
기본적인 Infinity Scroll의 경우 한 방향으로 지속적으로 작동, 값을 추가만 하면 된다.
하지만 달력의 경우 좌, 우 즉 값이 추가되거나 감소되거나 해야한다.
무작정 사람이 스크롤 할 만한 범위의 값을 불러와서 만들기는 싫었다.
내가 생각한 아이디어는 3주치의 배열을 계속해서 바꿔주는 것이다. Paging을 사용하게 되면 쉽게 될 줄 알았지만 값의 변화에 따른 View의 재생성되는 과정이 매끄럽지 못했다. 그 당시 생각할 수 있었던 아이디어는 HStack 의 특성을 이용하여 offset 을 DragGesture로 변화를 주고 값을 바꿔 재생성하자 밖에 떠오르지 않았다.
아이디어를 자세히 정리하자면
1. HStack 으로 이전 주, 현재 주, 다음 주의 뷰를 나열 후 한 주의 크기를 기기 사이즈를 고려하여 할당해준다.
2. DragGesture 로 HStack의 offset의 변화를 주어, 스크롤이 되는 것처럼 보여준다.
3. 한주의 변화가 있게 스크롤을 할 경우 HStack 의 offset 화면 크기에 맞게 변화를 준다.
4. 이때 Offset의 변화는 withAnimation 으로 애니메이션 효과를 주어 자동으로 스크롤이 되는것 처럼 연출한다.
5. DispatchQueue.main.asyncAfter 를 이용하여 애니메이션 효과가 끝나게 되면 가지고 있는 주간 데이터(일자) 의 변화를 주고, HStack의 offset을 0으로 준다.
6. State의 변화로 처음 만들어진 View는 한주가 바뀌어 있는 것처럼 새로운 View가 대체하게 된다.
@State var dayList:[[Int]] = [[1,2,3,4,5,6,7], [8,9,10,11,12,13,14], [15,16,17,18,19,20,21]]
@GestureState var gestureOffset: CGFloat = .zero
@State var offset: CGFloat = .zero
----------
HStack(spacing: 0) {
ForEach(dayList.indices, id: \.self) { i in
NumberView(numbers: $dayList[i])
}
}
// 날짜의 경우 DragGesture가 동작할때 값과 end가 될때 offset 값을 더해서 페이지 전환 효과를 준다.
.offset(x: width*0.03 + gestureOffset + offset)
.gesture(
DragGesture()
.updating($gestureOffset) { dragValue, gestureState, _ in
// drag 하는 도중 view의 offset을 실시간으로 반영해주기 위함
gestureState = dragValue.translation.width
}.onEnded { value in
// 제스쳐가 종료가 되는 즉시 offset을 스크롤된 값으로 할당해주고, 스크롤 방향, 값을 기준으로 페이지 전환 시 offset을 증감, 아닐시 .zero를 할당해주되, withAnimation의 closer에 정의함으로서 애니메이션 효과를 추가한다.
offset = value.translation.width
if value.translation.width < -100 {
withAnimation(.linear(duration: 0.05)) {
offset = width * -1
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.05) {
nextWeek()
}
} else if value.translation.width > 100 {
withAnimation(.linear(duration: 0.05)) {
offset = width * 1
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.05) {
previousWeek()
}
} else {
withAnimation(.linear(duration: 0.5)) {
offset = .zero
}
}
}
)
Github: https://github.com/profit0124/WeeklyInfinityScrollTest
'Swift > SwiftUI' 카테고리의 다른 글
[SwiftUI] QR Code Scanner 만들기 (SwifUI 에 ViewController 사용하기) (1) | 2023.03.08 |
---|---|
[SwiftUI] 날짜 계산, 월간달력 만들기 (0) | 2023.03.04 |
[SwiftUI] onAppear 에서 async 함수 실행하기 (0) | 2023.02.18 |
[SwiftUI] Scroll Value 구하기 (0) | 2023.01.30 |
[SwiftUI] ImageResize 후 onTapGesture 적용시 터치영역 버그 (0) | 2023.01.30 |