왜 Swipe에 맞춰서 움직이세요?
너무 구현해보고 싶어서 다양한 도전끝에 길을 찾았습니다.
공유해볼게요!
...Λ_Λ
(ㆍωㆍ)つ━☆。
⊂ ノ .뾰
し-J °。로 ´¨)
.. .· ´¸.·롱´¨) ¸.·¨)
(¸.·´ (????¸.'*
저는 저 안에
[backButton]
[bottomLineView]
[CollectionView]
이렇게 전체적인 뷰를 구성해줬어요.
이렇게 구성되어진 뷰를 기반으로 만들었기 때문에, 저랑 다르게 레이아웃을 잡았을 경우에는 다른 방법을 찾는 걸 추천드립니다!
저는 일단 CollectionView의 기반인 ScrollView가 Scroll되는걸 감지해서
저 bottomLineView가 움직이게 해줬어요.
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { }
이 함수를 사용해서 ScrollView의 Dragging이 끝날때쯤에 현재 CollectionView의 index를 감지해서 해당 인덱스에 맞게 LineView가 움직이도록 했어요.
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let cellWidthIncludingSpacing = UIScreen.main.bounds.size.width
// targetContentOff을 이용하여 x좌표가 얼마나 이동했는지 확인
// 이동한 x좌표 값과 item의 크기를 비교하여 몇 페이징이 될 것인지 값 설정
var offset = targetContentOffset.pointee
let index = (offset.x + scrollView.contentInset.left) / cellWidthIncludingSpacing
var roundedIndex = round(index)
// scrollView, targetContentOffset의 좌표 값으로 스크롤 방향을 알 수 있다.
// index를 반올림하여 사용하면 item의 절반 사이즈만큼 스크롤을 해야 페이징이 된다.
// 스크로로 방향을 체크하여 올림,내림을 사용하면 좀 더 자연스러운 페이징 효과를 낼 수 있다.
if scrollView.contentOffset.x > targetContentOffset.pointee.x {
roundedIndex = floor(index)
} else if scrollView.contentOffset.x < targetContentOffset.pointee.x {
roundedIndex = ceil(index)
} else {
roundedIndex = round(index)
}
// 기존에는 스크롤한 값에 대해 페이징되는 인덱스 값을 구하여, 인덱스 크기만큼 페이징 처리를 했습니다.
// 업데이트 된 코드에서는 페이징되는 인덱스 크기를 1로 수정하여 하나씩만 페이징 되도록 처리했습니다.
if currentIndex > roundedIndex {
currentIndex -= 1
roundedIndex = currentIndex
moveNegativeDirection()
} else if currentIndex < roundedIndex {
currentIndex += 1
roundedIndex = currentIndex
movePositiveDirection()
}
// 위 코드를 통해 페이징 될 좌표값을 targetContentOffset에 대입하면 된다.
offset = CGPoint(x: roundedIndex * cellWidthIncludingSpacing - scrollView.contentInset.left, y: -scrollView.contentInset.top)
targetContentOffset.pointee = offset
}
// + 방향으로 움직임
// 전체 width사이즈에서 현재 가지고 있는 text의 갯수만큼 나눈걸 index만큼 곱해주면
// index가 클 수록 멀게 이동한다
func movePositiveDirection() {
UIView.animate(withDuration: 0.3, animations: {
self.lineView.transform = CGAffineTransform(translationX: UIScreen.main.bounds.size.width/self.textCount * self.currentIndex, y: 0)
})
}
// - 방향으로 움직임
// 전체 width사이즈에서 현재 인덱스를 빼준다
// index가 작아질수록 원래 자리로 돌아온다.
func moveNegativeDirection() {
UIView.animate(withDuration: 0.3, animations: {
self.lineView.transform = CGAffineTransform(translationX: UIScreen.main.bounds.size.width - UIScreen.main.bounds.size.width/self.textCount * (self.textCount - self.currentIndex), y: 0)
})
}
복잡하죠..?
cellWidthIncludingSpacing
cellWidthIncludingSpacing를 스크린의 width로 지정했습니다.
나중에 cell 하나 하나의 크기를 알아야지 전체 스크롤해서 현재 index가 몇인지 알 수 있기 때문이지요.
offset
현재 target이 되는 content의 좌표를 나타내고 있어요.
index
현재 offset.x 좌표와 scrollView의 왼쪽 inset를 더한 다음에 셀 하나의 크기로 나눠서 현재 index로 넣어줬어요.
roundedIndex
roundedIndex를 통해서 계산한 index를 반올림했습니다.
뒤에 소수점이 생기는데 만약 소수점이 0.5 이상이면 위로 올리고 나머지는 그 밑으로 계산했어요.