UIScrollView의 스크롤 방향 구하기

RiverWindowHyeok·2024년 3월 18일
post-thumbnail

개요

안녕하세요. WindowHyeok입니다. 오늘은 제가 프로젝트를 진행하면서 스크롤뷰의 방향을 제 나름대로의 계산으로 구해보고, 이를 실제 프로젝트에서 어떻게 활용했는지에 대해서 이야기 해보려고 합니다.

UIScrollView는 ios 개발에서 아주 중요한 녀석입니다.
화면의 실제 크기보다 표시해줘야 하는 컨텐츠의 크기가 클경우 사용하게 되며,
UI를 구성하면서 많이 쓰이는 UITableView와 UICollectionView도 UIScrollView를 기반으로 서브클래싱하여 만들어진 객체입니다.

코드

func scrollViewDidScroll(_ scrollView: UIScrollView) {

        // UIScrollView 안에있는 panGesture의 velocity 메서드를 활용하여
        //스크롤 될때마다 velocity가 계산됨
        let direction = scrollView.panGestureRecognizer.velocity(in: scrollView)
        
        if direction.y > 0 {
        	//터치시작점과 끝나는 지점의 방향이 위로 올라갈때
            print("밑에서 아래로!!! 이전 아이템 방향")
        } else if direction.y < 0 {
            //터치시작점과 끝나는 지점의 방향이 아래로 내려갈때
            print("아래에서 위로!!! 다음 아이템 방향")
        } else {
        	//스크롤 뷰에서 터치가 끝나서 velocity는 0이지만 아직 스크롤은 계속 진행중인 상태
        }
    }

그래서 저는 보통 이런식으로 UIScrolView의 방향을 구해서 사용했습니다.
UIScrollView 안에있는 pangesture가 제공하는 velocity(in:) 메서드는 어떤 역할을 하는 걸까요??

velocity(in:) 메서드

공식 문서의 내용에 따르면,
해당 메서드는 매개변수로 받은 View의 좌표계에서 팬 제스쳐의 속도를 계산해주는 메서드라고 합니다.
여기서 리턴값으로 넘어오는 타입 형식은 CGPoint인데, x,y 축에 대한 스크롤 속도를 계산해서 리턴해줍니다.
즉 여기서 리턴되는 x, y값을 바탕으로 스크롤 방향을 알수 있는 것이죠.

y값에 따른 스크롤 방향 처리를 해주면 되는데, 말로 설명하는 것 보단 직접 눈으로 보시는게 이해가 더 빠를것 같습니다.

y > 0 일때(Previous)


터치시작점 -> 터치 끝나는 점의 방향이 위에서 아래로 진행됩니다.
이때 y값은 양수값으로 계산됩니다.
보통 이전 컨텐츠를 보려고 스크롤 할때 이 경우에 해당합니다.

y < 0 일때(Next)


터치시작점 -> 터치 끝나는 점의 방향이 아래에서 위로 진행됩니다.
이때 y값은 음수값으로 계산됩니다.
보통 다음 컨텐츠를 보려고 스크롤 할때 이 경우에 해당합니다.

y == 0 일때(Filck)

이 두가지 케이스만 처리하면 될것 같지만, 1가지 상황이 더 있습니다.
위의 두가지 상황은 스크롤중에 화면에서 저의 손가락이 떨어지지 않은채로 계속 스크롤이 진행되었기 때문에 velocity값이 정확하게 나왔지만,
일반적으로 다들 스크롤을 할땐 처음 시작할때만 화면에 터치를 하고,
스크롤하면서 화면의 터치가 자연스럽게 띄어지게 되잖아요??
이 경우에는 화면상에서 보여질때 스크롤은 계속 진행되지만 해당 velocity 값은 (0,0) 이 나오게 되는 상황이 발생합니다. 이 상황에 대해서도 처리를 해줄 필요가 있어요.

프로젝트에서 겪었던 문제

저같은 경우에는 스크롤 방향에 따라 화면 상단의 뷰의 isHidden을 설정해줘야 되는 상황이었는데,
이 케이스를 고려하지 않고 처리하게 되어 문제가 발생했었습니다.
Previous, Next 케이스에 대해 각 처리를 하고,
filnk일땐 단순히 return 시키게 처리하니까 제가 의도한 동작을 제대로 할 수 있었습니다.

코드 응용

스크롤의 방향을 구해서 문제를 해결했으나,
이후 여러가지 상황에서 스크롤의 방향을 구해야 하는 상황이 많이 발생했습니다.
좀더 읽기 좋고 편하게 사용하고자 UIScrollView에 Extension으로
scrollDirection 속성을 구현해서 좀더 쉽게 사용할수 있도록 구현했습니다.

extension UIScrollView {
    
    /// 스크롤 방향
    enum ScrollDirection {
        case previous
        case next
        case flink // 스크린에서 터치가 끝난 이후에 스크롤이 진행될때, 
        		   //(0,0) 상태를 처리하기 위한 case
    }
    
    /// 스크롤 뷰 축 방향
    enum ScrollAxis {
        case horizontal
        case vertical
    }
    
    /// 스크롤뷰의 스크롤 되는 방향.
    /// - velocity를 사용하여 스크롤뷰의 스크롤 방향을 계산
    var scrollDirection: ScrollDirection {
        
        let velocity = self.panGestureRecognizer.velocity(in: self)
        // 스크롤 뷰의 컨텐츠 높이와 폭의 길이를 비교하여 스크롤뷰의 축 방향을 정한다.
        let scrollAxis = self.contentSize.height > self.contentSize.width ? ScrollAxis.vertical : ScrollAxis.horizontal
        switch scrollAxis {
        case .horizontal:
            guard velocity.x >= 0 else {
                return .next
            }
            guard velocity.x == 0 else {
                return .previous
            }
            return .flink
        case .vertical:
            guard velocity.y >= 0 else {
                return .next
            }
            guard velocity.y == 0 else {
                return .previous
            }
            return .flink
        }
    }
}

ScrollDirection, ScrollAxis을 enum으로 구현해서 코드의 가독성을 높여 이해하기 쉽도록 작성했습니다.
기본적인 계산 방법은 유사한데, contentSize의 값을 활용해서

Height이 Width보다 큰 경우

축방향을 수직으로 설정하고 y값을 활용해서 스크롤 방향을 계산

Width가 Height 보다 큰 경우

축방향을 수평으로 설정하고 x값을 활용해서 스크롤 방향을 계산

앞으로 스크롤 방향을 구하고자 할때 유용하게 사용할수 있을거 같습니다.
글에 잘못된 부분이나 궁금한게 있다면 댓글 남겨주세요. 읽어주셔서 감사합니다.

profile
기본에 충실하기

0개의 댓글